automatically decode base64 encoded SAML values
[moonshot.git] / mech_eap / accept_sec_context.c
1 /*
2  * Copyright (c) 2011, JANET(UK)
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  *
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  *
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * 3. Neither the name of JANET(UK) nor the names of its contributors
17  *    may be used to endorse or promote products derived from this software
18  *    without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
24  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30  * SUCH DAMAGE.
31  */
32
33 /*
34  * Establish a security context on the acceptor (server). These functions
35  * wrap around libradsec and (thus) talk to a RADIUS server or proxy.
36  */
37
38 #include "gssapiP_eap.h"
39
40 #ifdef GSSEAP_ENABLE_REAUTH
41 static OM_uint32
42 eapGssSmAcceptGssReauth(OM_uint32 *minor,
43                         gss_cred_id_t cred,
44                         gss_ctx_id_t ctx,
45                         gss_name_t target,
46                         gss_OID mech,
47                         OM_uint32 reqFlags,
48                         OM_uint32 timeReq,
49                         gss_channel_bindings_t chanBindings,
50                         gss_buffer_t inputToken,
51                         gss_buffer_t outputToken,
52                         OM_uint32 *smFlags);
53 #endif
54
55 /*
56  * Mark an acceptor context as ready for cryptographic operations
57  */
58 static OM_uint32
59 acceptReadyEap(OM_uint32 *minor, gss_ctx_id_t ctx, gss_cred_id_t cred)
60 {
61     OM_uint32 major, tmpMinor;
62     VALUE_PAIR *vp;
63     gss_buffer_desc nameBuf = GSS_C_EMPTY_BUFFER;
64
65     /* Cache encryption type derived from selected mechanism OID */
66     major = gssEapOidToEnctype(minor, ctx->mechanismUsed,
67                                &ctx->encryptionType);
68     if (GSS_ERROR(major))
69         return major;
70
71     gssEapReleaseName(&tmpMinor, &ctx->initiatorName);
72
73     major = gssEapRadiusGetRawAvp(minor, ctx->acceptorCtx.vps,
74                                   PW_USER_NAME, 0, &vp);
75     if (major == GSS_S_COMPLETE && vp->length) {
76         nameBuf.length = vp->length;
77         nameBuf.value = vp->vp_strvalue;
78     } else {
79         ctx->gssFlags |= GSS_C_ANON_FLAG;
80     }
81
82     major = gssEapImportName(minor, &nameBuf,
83                              (ctx->gssFlags & GSS_C_ANON_FLAG) ?
84                                 GSS_C_NT_ANONYMOUS : GSS_C_NT_USER_NAME,
85                              ctx->mechanismUsed,
86                              &ctx->initiatorName);
87     if (GSS_ERROR(major))
88         return major;
89
90     major = gssEapRadiusGetRawAvp(minor, ctx->acceptorCtx.vps,
91                                   PW_MS_MPPE_SEND_KEY, VENDORPEC_MS, &vp);
92     if (GSS_ERROR(major)) {
93         *minor = GSSEAP_KEY_UNAVAILABLE;
94         return GSS_S_UNAVAILABLE;
95     }
96
97     major = gssEapDeriveRfc3961Key(minor,
98                                    vp->vp_octets,
99                                    vp->length,
100                                    ctx->encryptionType,
101                                    &ctx->rfc3961Key);
102     if (GSS_ERROR(major))
103         return major;
104
105     major = rfc3961ChecksumTypeForKey(minor, &ctx->rfc3961Key,
106                                        &ctx->checksumType);
107     if (GSS_ERROR(major))
108         return major;
109
110     major = sequenceInit(minor,
111                          &ctx->seqState, ctx->recvSeq,
112                          ((ctx->gssFlags & GSS_C_REPLAY_FLAG) != 0),
113                          ((ctx->gssFlags & GSS_C_SEQUENCE_FLAG) != 0),
114                          TRUE);
115     if (GSS_ERROR(major))
116         return major;
117
118     major = gssEapCreateAttrContext(minor, cred, ctx,
119                                     &ctx->initiatorName->attrCtx,
120                                     &ctx->expiryTime);
121     if (GSS_ERROR(major))
122         return major;
123
124     *minor = 0;
125     return GSS_S_COMPLETE;
126 }
127
128 static OM_uint32
129 gssEapSupportedInitiatorExts[] = {
130 };
131
132 static struct gss_eap_itok_map
133 gssEapAcceptorExtsFlagMap[] = {
134     { ITOK_TYPE_REAUTH_CREDS, CTX_FLAG_KRB_REAUTH_SUPPORTED },
135 };
136
137 static OM_uint32
138 eapGssSmAcceptExts(OM_uint32 *minor,
139                    gss_cred_id_t cred GSSEAP_UNUSED,
140                    gss_ctx_id_t ctx GSSEAP_UNUSED,
141                    gss_name_t target GSSEAP_UNUSED,
142                    gss_OID mech GSSEAP_UNUSED,
143                    OM_uint32 reqFlags GSSEAP_UNUSED,
144                    OM_uint32 timeReq GSSEAP_UNUSED,
145                    gss_channel_bindings_t chanBindings GSSEAP_UNUSED,
146                    gss_buffer_t inputToken,
147                    gss_buffer_t outputToken,
148                    OM_uint32 *smFlags GSSEAP_UNUSED)
149 {
150     OM_uint32 major;
151
152     major = gssEapProcessSupportedExts(minor, inputToken,
153                                        gssEapAcceptorExtsFlagMap,
154                                         sizeof(gssEapAcceptorExtsFlagMap) /
155                                        sizeof(gssEapAcceptorExtsFlagMap[0]),
156                                        &ctx->flags);
157     if (GSS_ERROR(major))
158         return major;
159
160     major = gssEapEncodeSupportedExts(minor,
161                                       gssEapSupportedInitiatorExts,
162                                       sizeof(gssEapSupportedInitiatorExts) /
163                                         sizeof(gssEapSupportedInitiatorExts[0]),
164                                       outputToken);
165     if (GSS_ERROR(major))
166         return major;
167
168     return GSS_S_CONTINUE_NEEDED;
169 }
170
171 static OM_uint32
172 eapGssSmAcceptAcceptorName(OM_uint32 *minor,
173                            gss_cred_id_t cred GSSEAP_UNUSED,
174                            gss_ctx_id_t ctx,
175                            gss_name_t target GSSEAP_UNUSED,
176                            gss_OID mech GSSEAP_UNUSED,
177                            OM_uint32 reqFlags GSSEAP_UNUSED,
178                            OM_uint32 timeReq GSSEAP_UNUSED,
179                            gss_channel_bindings_t chanBindings GSSEAP_UNUSED,
180                            gss_buffer_t inputToken GSSEAP_UNUSED,
181                            gss_buffer_t outputToken,
182                            OM_uint32 *smFlags GSSEAP_UNUSED)
183 {
184     OM_uint32 major;
185
186     /* XXX TODO import and validate name from inputToken */
187
188     if (ctx->acceptorName != GSS_C_NO_NAME) {
189         /* Send desired target name to acceptor */
190         major = gssEapDisplayName(minor, ctx->acceptorName,
191                                   outputToken, NULL);
192         if (GSS_ERROR(major))
193             return major;
194     }
195
196     return GSS_S_CONTINUE_NEEDED;
197 }
198
199 #ifdef GSSEAP_DEBUG
200 static OM_uint32
201 eapGssSmAcceptVendorInfo(OM_uint32 *minor,
202                          gss_cred_id_t cred GSSEAP_UNUSED,
203                          gss_ctx_id_t ctx GSSEAP_UNUSED,
204                          gss_name_t target GSSEAP_UNUSED,
205                          gss_OID mech GSSEAP_UNUSED,
206                          OM_uint32 reqFlags GSSEAP_UNUSED,
207                          OM_uint32 timeReq GSSEAP_UNUSED,
208                          gss_channel_bindings_t chanBindings GSSEAP_UNUSED,
209                          gss_buffer_t inputToken,
210                          gss_buffer_t outputToken GSSEAP_UNUSED,
211                          OM_uint32 *smFlags GSSEAP_UNUSED)
212 {
213     fprintf(stderr, "GSS-EAP: vendor: %.*s\n",
214             (int)inputToken->length, (char *)inputToken->value);
215
216     *minor = 0;
217     return GSS_S_CONTINUE_NEEDED;
218 }
219 #endif
220
221
222 /*
223  * Emit a identity EAP request to force the initiator (peer) to identify
224  * itself.
225  */
226 static OM_uint32
227 eapGssSmAcceptIdentity(OM_uint32 *minor,
228                        gss_cred_id_t cred,
229                        gss_ctx_id_t ctx,
230                        gss_name_t target GSSEAP_UNUSED,
231                        gss_OID mech GSSEAP_UNUSED,
232                        OM_uint32 reqFlags GSSEAP_UNUSED,
233                        OM_uint32 timeReq GSSEAP_UNUSED,
234                        gss_channel_bindings_t chanBindings GSSEAP_UNUSED,
235                        gss_buffer_t inputToken,
236                        gss_buffer_t outputToken,
237                        OM_uint32 *smFlags)
238 {
239     OM_uint32 major;
240     struct wpabuf *reqData;
241     gss_buffer_desc pktBuffer;
242
243     if (!gssEapCredAvailable(cred, ctx->mechanismUsed)) {
244         *minor = GSSEAP_CRED_MECH_MISMATCH;
245         return GSS_S_BAD_MECH;
246     }
247
248     if (inputToken != GSS_C_NO_BUFFER && inputToken->length != 0) {
249         *minor = GSSEAP_WRONG_SIZE;
250         return GSS_S_DEFECTIVE_TOKEN;
251     }
252
253     reqData = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_IDENTITY, 0,
254                             EAP_CODE_REQUEST, 0);
255     if (reqData == NULL) {
256         *minor = ENOMEM;
257         return GSS_S_FAILURE;
258     }
259
260     pktBuffer.length = wpabuf_len(reqData);
261     pktBuffer.value = (void *)wpabuf_head(reqData);
262
263     major = duplicateBuffer(minor, &pktBuffer, outputToken);
264     if (GSS_ERROR(major))
265         return major;
266
267     wpabuf_free(reqData);
268
269     GSSEAP_SM_TRANSITION_NEXT(ctx);
270
271     *minor = 0;
272     *smFlags |= SM_FLAG_SEND_TOKEN | SM_FLAG_OUTPUT_TOKEN_CRITICAL;
273
274     return GSS_S_CONTINUE_NEEDED;
275 }
276
277 /*
278  * Returns TRUE if the input token contains an EAP identity response.
279  */
280 static int
281 isIdentityResponseP(gss_buffer_t inputToken)
282 {
283     struct wpabuf respData;
284
285     wpabuf_set(&respData, inputToken->value, inputToken->length);
286
287     return (eap_get_type(&respData) == EAP_TYPE_IDENTITY);
288 }
289
290 /*
291  * Save the asserted initiator identity from the EAP identity response.
292  */
293 static OM_uint32
294 importInitiatorIdentity(OM_uint32 *minor,
295                         gss_ctx_id_t ctx,
296                         gss_buffer_t inputToken)
297 {
298     OM_uint32 tmpMinor;
299     struct wpabuf respData;
300     const unsigned char *pos;
301     size_t len;
302     gss_buffer_desc nameBuf;
303
304     wpabuf_set(&respData, inputToken->value, inputToken->length);
305
306     pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_IDENTITY,
307                            &respData, &len);
308     if (pos == NULL) {
309         *minor = GSSEAP_PEER_BAD_MESSAGE;
310         return GSS_S_DEFECTIVE_TOKEN;
311     }
312
313     nameBuf.value = (void *)pos;
314     nameBuf.length = len;
315
316     gssEapReleaseName(&tmpMinor, &ctx->initiatorName);
317
318     return gssEapImportName(minor, &nameBuf, GSS_C_NT_USER_NAME,
319                             ctx->mechanismUsed, &ctx->initiatorName);
320 }
321
322 /*
323  * Pass the asserted initiator identity to the authentication server.
324  */
325 static OM_uint32
326 setInitiatorIdentity(OM_uint32 *minor,
327                      gss_ctx_id_t ctx,
328                      VALUE_PAIR **vps)
329 {
330     OM_uint32 major, tmpMinor;
331     gss_buffer_desc nameBuf;
332
333     /*
334      * We should have got an EAP identity response, but if we didn't, then
335      * we will just avoid sending User-Name. Note that radsecproxy requires
336      * User-Name to be sent on every request (presumably so it can remain
337      * stateless).
338      */
339     if (ctx->initiatorName != GSS_C_NO_NAME) {
340         major = gssEapDisplayName(minor, ctx->initiatorName, &nameBuf, NULL);
341         if (GSS_ERROR(major))
342             return major;
343
344         major = gssEapRadiusAddAvp(minor, vps, PW_USER_NAME, 0, &nameBuf);
345         if (GSS_ERROR(major))
346             return major;
347
348         gss_release_buffer(&tmpMinor, &nameBuf);
349     }
350
351     *minor = 0;
352     return GSS_S_COMPLETE;
353 }
354
355 /*
356  * Pass the asserted acceptor identity to the authentication server.
357  */
358 static OM_uint32
359 setAcceptorIdentity(OM_uint32 *minor,
360                     gss_ctx_id_t ctx,
361                     VALUE_PAIR **vps)
362 {
363     OM_uint32 major;
364     gss_buffer_desc nameBuf;
365     krb5_context krbContext = NULL;
366     krb5_principal krbPrinc;
367     struct rs_context *rc = ctx->acceptorCtx.radContext;
368
369     assert(rc != NULL);
370
371     if (ctx->acceptorName == GSS_C_NO_NAME) {
372         *minor = 0;
373         return GSS_S_COMPLETE;
374     }
375
376     if ((ctx->acceptorName->flags & NAME_FLAG_SERVICE) == 0) {
377         *minor = GSSEAP_BAD_SERVICE_NAME;
378         return GSS_S_BAD_NAME;
379     }
380
381     GSSEAP_KRB_INIT(&krbContext);
382
383     krbPrinc = ctx->acceptorName->krbPrincipal;
384     assert(krbPrinc != NULL);
385     assert(KRB_PRINC_LENGTH(krbPrinc) >= 2);
386
387     /* Acceptor-Service-Name */
388     krbPrincComponentToGssBuffer(krbPrinc, 0, &nameBuf);
389
390     major = gssEapRadiusAddAvp(minor, vps,
391                                PW_GSS_ACCEPTOR_SERVICE_NAME,
392                                VENDORPEC_UKERNA,
393                                &nameBuf);
394     if (GSS_ERROR(major))
395         return major;
396
397     /* Acceptor-Host-Name */
398     krbPrincComponentToGssBuffer(krbPrinc, 1, &nameBuf);
399
400     major = gssEapRadiusAddAvp(minor, vps,
401                                PW_GSS_ACCEPTOR_HOST_NAME,
402                                VENDORPEC_UKERNA,
403                                &nameBuf);
404     if (GSS_ERROR(major))
405         return major;
406
407     if (KRB_PRINC_LENGTH(krbPrinc) > 2) {
408         /* Acceptor-Service-Specific */
409         krb5_principal_data ssiPrinc = *krbPrinc;
410         char *ssi;
411
412         KRB_PRINC_LENGTH(&ssiPrinc) -= 2;
413         KRB_PRINC_NAME(&ssiPrinc) += 2;
414
415         *minor = krb5_unparse_name_flags(krbContext, &ssiPrinc,
416                                          KRB5_PRINCIPAL_UNPARSE_NO_REALM, &ssi);
417         if (*minor != 0)
418             return GSS_S_FAILURE;
419
420         nameBuf.value = ssi;
421         nameBuf.length = strlen(ssi);
422
423         major = gssEapRadiusAddAvp(minor, vps,
424                                    PW_GSS_ACCEPTOR_SERVICE_SPECIFIC,
425                                    VENDORPEC_UKERNA,
426                                    &nameBuf);
427
428         if (GSS_ERROR(major)) {
429             krb5_free_unparsed_name(krbContext, ssi);
430             return major;
431         }
432         krb5_free_unparsed_name(krbContext, ssi);
433     }
434
435     krbPrincRealmToGssBuffer(krbPrinc, &nameBuf);
436     if (nameBuf.length != 0) {
437         /* Acceptor-Realm-Name */
438         major = gssEapRadiusAddAvp(minor, vps,
439                                    PW_GSS_ACCEPTOR_REALM_NAME,
440                                    VENDORPEC_UKERNA,
441                                    &nameBuf);
442         if (GSS_ERROR(major))
443             return major;
444     }
445
446     *minor = 0;
447     return GSS_S_COMPLETE;
448 }
449
450 /*
451  * Allocate a RadSec handle
452  */
453 static OM_uint32
454 createRadiusHandle(OM_uint32 *minor,
455                    gss_cred_id_t cred,
456                    gss_ctx_id_t ctx)
457 {
458     struct gss_eap_acceptor_ctx *actx = &ctx->acceptorCtx;
459     const char *configFile = RS_CONFIG_FILE;
460     const char *configStanza = "gss-eap";
461     struct rs_alloc_scheme ralloc;
462     struct rs_error *err;
463
464     assert(actx->radContext == NULL);
465     assert(actx->radConn == NULL);
466
467     if (rs_context_create(&actx->radContext) != 0) {
468         *minor = GSSEAP_RADSEC_CONTEXT_FAILURE;
469         return GSS_S_FAILURE;
470     }
471
472     if (cred->radiusConfigFile != NULL)
473         configFile = cred->radiusConfigFile;
474     if (cred->radiusConfigStanza != NULL)
475         configStanza = cred->radiusConfigStanza;
476
477     ralloc.calloc  = GSSEAP_CALLOC;
478     ralloc.malloc  = GSSEAP_MALLOC;
479     ralloc.free    = GSSEAP_FREE;
480     ralloc.realloc = GSSEAP_REALLOC;
481
482     rs_context_set_alloc_scheme(actx->radContext, &ralloc);
483
484     if (rs_context_read_config(actx->radContext, configFile) != 0) {
485         err = rs_err_ctx_pop(actx->radContext);
486         goto fail;
487     }
488
489     if (rs_context_init_freeradius_dict(actx->radContext, NULL) != 0) {
490         err = rs_err_ctx_pop(actx->radContext);
491         goto fail;
492     }
493
494     if (rs_conn_create(actx->radContext, &actx->radConn, configStanza) != 0) {
495         err = rs_err_conn_pop(actx->radConn);
496         goto fail;
497     }
498
499     if (actx->radServer != NULL) {
500         if (rs_conn_select_peer(actx->radConn, actx->radServer) != 0) {
501             err = rs_err_conn_pop(actx->radConn);
502             goto fail;
503         }
504     }
505
506     *minor = 0;
507     return GSS_S_COMPLETE;
508
509 fail:
510     return gssEapRadiusMapError(minor, err);
511 }
512
513 /*
514  * Process a EAP response from the initiator.
515  */
516 static OM_uint32
517 eapGssSmAcceptAuthenticate(OM_uint32 *minor,
518                            gss_cred_id_t cred,
519                            gss_ctx_id_t ctx,
520                            gss_name_t target GSSEAP_UNUSED,
521                            gss_OID mech GSSEAP_UNUSED,
522                            OM_uint32 reqFlags GSSEAP_UNUSED,
523                            OM_uint32 timeReq GSSEAP_UNUSED,
524                            gss_channel_bindings_t chanBindings GSSEAP_UNUSED,
525                            gss_buffer_t inputToken,
526                            gss_buffer_t outputToken,
527                            OM_uint32 *smFlags)
528 {
529     OM_uint32 major, tmpMinor;
530     struct rs_connection *rconn;
531     struct rs_request *request = NULL;
532     struct rs_packet *req = NULL, *resp = NULL;
533     struct radius_packet *frreq, *frresp;
534
535     if (ctx->acceptorCtx.radContext == NULL) {
536         /* May be NULL from an imported partial context */
537         major = createRadiusHandle(minor, cred, ctx);
538         if (GSS_ERROR(major))
539             goto cleanup;
540     }
541
542     if (isIdentityResponseP(inputToken)) {
543         major = importInitiatorIdentity(minor, ctx, inputToken);
544         if (GSS_ERROR(major))
545             return major;
546     }
547
548     rconn = ctx->acceptorCtx.radConn;
549
550     if (rs_packet_create_authn_request(rconn, &req, NULL, NULL) != 0) {
551         major = gssEapRadiusMapError(minor, rs_err_conn_pop(rconn));
552         goto cleanup;
553     }
554     frreq = rs_packet_frpkt(req);
555
556     major = setInitiatorIdentity(minor, ctx, &frreq->vps);
557     if (GSS_ERROR(major))
558         goto cleanup;
559
560     major = setAcceptorIdentity(minor, ctx, &frreq->vps);
561     if (GSS_ERROR(major))
562         goto cleanup;
563
564     major = gssEapRadiusAddAvp(minor, &frreq->vps,
565                                PW_EAP_MESSAGE, 0, inputToken);
566     if (GSS_ERROR(major))
567         goto cleanup;
568
569     if (ctx->acceptorCtx.state.length != 0) {
570         major = gssEapRadiusAddAvp(minor, &frreq->vps, PW_STATE, 0,
571                                    &ctx->acceptorCtx.state);
572         if (GSS_ERROR(major))
573             goto cleanup;
574
575         gss_release_buffer(&tmpMinor, &ctx->acceptorCtx.state);
576     }
577
578     if (rs_request_create(rconn, &request) != 0) {
579         major = gssEapRadiusMapError(minor, rs_err_conn_pop(rconn));
580         goto cleanup;
581     }
582
583     rs_request_add_reqpkt(request, req);
584     req = NULL;
585
586     if (rs_request_send(request, &resp) != 0) {
587         major = gssEapRadiusMapError(minor, rs_err_conn_pop(rconn));
588         goto cleanup;
589     }
590
591     assert(resp != NULL);
592
593     frresp = rs_packet_frpkt(resp);
594     switch (frresp->code) {
595     case PW_ACCESS_CHALLENGE:
596     case PW_AUTHENTICATION_ACK:
597         break;
598     case PW_AUTHENTICATION_REJECT:
599         *minor = GSSEAP_RADIUS_AUTH_FAILURE;
600         major = GSS_S_DEFECTIVE_CREDENTIAL;
601         goto cleanup;
602         break;
603     default:
604         *minor = GSSEAP_UNKNOWN_RADIUS_CODE;
605         major = GSS_S_FAILURE;
606         goto cleanup;
607         break;
608     }
609
610     major = gssEapRadiusGetAvp(minor, frresp->vps, PW_EAP_MESSAGE, 0,
611                                outputToken, TRUE);
612     if (major == GSS_S_UNAVAILABLE && frresp->code == PW_ACCESS_CHALLENGE) {
613         *minor = GSSEAP_MISSING_EAP_REQUEST;
614         major = GSS_S_DEFECTIVE_TOKEN;
615         goto cleanup;
616     } else if (GSS_ERROR(major))
617         goto cleanup;
618
619     if (frresp->code == PW_ACCESS_CHALLENGE) {
620         major = gssEapRadiusGetAvp(minor, frresp->vps, PW_STATE, 0,
621                                    &ctx->acceptorCtx.state, TRUE);
622         if (GSS_ERROR(major) && *minor != GSSEAP_NO_SUCH_ATTR)
623             goto cleanup;
624     } else {
625         ctx->acceptorCtx.vps = frresp->vps;
626         frresp->vps = NULL;
627
628         major = acceptReadyEap(minor, ctx, cred);
629         if (GSS_ERROR(major))
630             goto cleanup;
631
632         GSSEAP_SM_TRANSITION_NEXT(ctx);
633     }
634
635     major = GSS_S_CONTINUE_NEEDED;
636     *minor = 0;
637     *smFlags |= SM_FLAG_SEND_TOKEN | SM_FLAG_OUTPUT_TOKEN_CRITICAL;
638
639 cleanup:
640     if (request != NULL)
641         rs_request_destroy(request);
642     if (req != NULL)
643         rs_packet_destroy(req);
644     if (resp != NULL)
645         rs_packet_destroy(resp);
646     if (GSSEAP_SM_STATE(ctx) == GSSEAP_STATE_INITIATOR_EXTS) {
647         assert(major == GSS_S_CONTINUE_NEEDED);
648
649         rs_conn_destroy(ctx->acceptorCtx.radConn);
650         ctx->acceptorCtx.radConn = NULL;
651     }
652
653     return major;
654 }
655
656 static OM_uint32
657 eapGssSmAcceptGssFlags(OM_uint32 *minor,
658                        gss_cred_id_t cred GSSEAP_UNUSED,
659                        gss_ctx_id_t ctx,
660                        gss_name_t target GSSEAP_UNUSED,
661                        gss_OID mech GSSEAP_UNUSED,
662                        OM_uint32 reqFlags GSSEAP_UNUSED,
663                        OM_uint32 timeReq GSSEAP_UNUSED,
664                        gss_channel_bindings_t chanBindings GSSEAP_UNUSED,
665                        gss_buffer_t inputToken,
666                        gss_buffer_t outputToken GSSEAP_UNUSED,
667                        OM_uint32 *smFlags GSSEAP_UNUSED)
668 {
669     unsigned char *p;
670     OM_uint32 initiatorGssFlags;
671
672     assert((ctx->flags & CTX_FLAG_KRB_REAUTH) == 0);
673
674     if (inputToken->length < 4) {
675         *minor = GSSEAP_TOK_TRUNC;
676         return GSS_S_DEFECTIVE_TOKEN;
677     }
678
679     /* allow flags to grow for future expansion */
680     p = (unsigned char *)inputToken->value + inputToken->length - 4;
681
682     initiatorGssFlags = load_uint32_be(p);
683     initiatorGssFlags &= GSSEAP_WIRE_FLAGS_MASK;
684
685     ctx->gssFlags |= initiatorGssFlags;
686
687     return GSS_S_CONTINUE_NEEDED;
688 }
689
690 static OM_uint32
691 eapGssSmAcceptGssChannelBindings(OM_uint32 *minor,
692                                  gss_cred_id_t cred GSSEAP_UNUSED,
693                                  gss_ctx_id_t ctx,
694                                  gss_name_t target GSSEAP_UNUSED,
695                                  gss_OID mech GSSEAP_UNUSED,
696                                  OM_uint32 reqFlags GSSEAP_UNUSED,
697                                  OM_uint32 timeReq GSSEAP_UNUSED,
698                                  gss_channel_bindings_t chanBindings,
699                                  gss_buffer_t inputToken,
700                                  gss_buffer_t outputToken GSSEAP_UNUSED,
701                                  OM_uint32 *smFlags GSSEAP_UNUSED)
702 {
703     OM_uint32 major, tmpMinor;
704     gss_iov_buffer_desc iov[2];
705
706     assert((ctx->flags & CTX_FLAG_KRB_REAUTH) == 0);
707
708     iov[0].type = GSS_IOV_BUFFER_TYPE_DATA | GSS_IOV_BUFFER_FLAG_ALLOCATE;
709     iov[0].buffer.length = 0;
710     iov[0].buffer.value = NULL;
711
712     iov[1].type = GSS_IOV_BUFFER_TYPE_STREAM;
713     iov[1].buffer = *inputToken;
714
715     major = gssEapUnwrapOrVerifyMIC(minor, ctx, NULL, NULL,
716                                     iov, 2, TOK_TYPE_WRAP);
717     if (GSS_ERROR(major))
718         return major;
719
720     if (chanBindings != GSS_C_NO_CHANNEL_BINDINGS &&
721         !bufferEqual(&iov[0].buffer, &chanBindings->application_data)) {
722         major = GSS_S_BAD_BINDINGS;
723         *minor = GSSEAP_BINDINGS_MISMATCH;
724     } else {
725         major = GSS_S_CONTINUE_NEEDED;
726         *minor = 0;
727     }
728
729     gss_release_buffer(&tmpMinor, &iov[0].buffer);
730
731     return major;
732 }
733
734 #ifdef GSSEAP_ENABLE_REAUTH
735 static OM_uint32
736 eapGssSmAcceptReauthCreds(OM_uint32 *minor,
737                           gss_cred_id_t cred,
738                           gss_ctx_id_t ctx,
739                           gss_name_t target GSSEAP_UNUSED,
740                           gss_OID mech GSSEAP_UNUSED,
741                           OM_uint32 reqFlags GSSEAP_UNUSED,
742                           OM_uint32 timeReq GSSEAP_UNUSED,
743                           gss_channel_bindings_t chanBindings GSSEAP_UNUSED,
744                           gss_buffer_t inputToken GSSEAP_UNUSED,
745                           gss_buffer_t outputToken,
746                           OM_uint32 *smFlags GSSEAP_UNUSED)
747 {
748     OM_uint32 major;
749
750     /*
751      * If we're built with fast reauthentication enabled, then
752      * fabricate a ticket from the initiator to ourselves.
753      */
754     if (ctx->flags & CTX_FLAG_KRB_REAUTH_SUPPORTED)
755         major = gssEapMakeReauthCreds(minor, ctx, cred, outputToken);
756     else
757         major = GSS_S_UNAVAILABLE;
758
759     if (major == GSS_S_UNAVAILABLE)
760         major = GSS_S_COMPLETE;
761
762     return GSS_ERROR(major) ? major : GSS_S_CONTINUE_NEEDED;
763 }
764 #endif
765
766 static OM_uint32
767 eapGssSmAcceptInitiatorMIC(OM_uint32 *minor,
768                            gss_cred_id_t cred GSSEAP_UNUSED,
769                            gss_ctx_id_t ctx,
770                            gss_name_t target GSSEAP_UNUSED,
771                            gss_OID mech GSSEAP_UNUSED,
772                            OM_uint32 reqFlags GSSEAP_UNUSED,
773                            OM_uint32 timeReq GSSEAP_UNUSED,
774                            gss_channel_bindings_t chanBindings GSSEAP_UNUSED,
775                            gss_buffer_t inputToken,
776                            gss_buffer_t outputToken GSSEAP_UNUSED,
777                            OM_uint32 *smFlags GSSEAP_UNUSED)
778 {
779     OM_uint32 major;
780
781     assert((ctx->flags & CTX_FLAG_KRB_REAUTH) == 0);
782
783     major = gssEapVerifyConversationMIC(minor, ctx, inputToken);
784     if (GSS_ERROR(major))
785         return major;
786
787     GSSEAP_SM_TRANSITION_NEXT(ctx);
788
789     *minor = 0;
790
791     return GSS_S_CONTINUE_NEEDED;
792 }
793
794 static OM_uint32
795 eapGssSmAcceptAcceptorMIC(OM_uint32 *minor,
796                           gss_cred_id_t cred GSSEAP_UNUSED,
797                           gss_ctx_id_t ctx,
798                           gss_name_t target GSSEAP_UNUSED,
799                           gss_OID mech GSSEAP_UNUSED,
800                           OM_uint32 reqFlags GSSEAP_UNUSED,
801                           OM_uint32 timeReq GSSEAP_UNUSED,
802                           gss_channel_bindings_t chanBindings GSSEAP_UNUSED,
803                           gss_buffer_t inputToken GSSEAP_UNUSED,
804                           gss_buffer_t outputToken,
805                           OM_uint32 *smFlags)
806 {
807     OM_uint32 major;
808
809     major = gssEapGetConversationMIC(minor, ctx, outputToken);
810     if (GSS_ERROR(major))
811         return major;
812
813     GSSEAP_SM_TRANSITION(ctx, GSSEAP_STATE_ESTABLISHED);
814
815     *minor = 0;
816     *smFlags |= SM_FLAG_SEND_TOKEN | SM_FLAG_OUTPUT_TOKEN_CRITICAL;
817
818     return GSS_S_COMPLETE;
819 }
820
821 /*
822  * Acceptor state machine.
823  */
824 static struct gss_eap_sm eapGssAcceptorSm[] = {
825 #ifdef GSSEAP_ENABLE_REAUTH
826     {
827         ITOK_TYPE_REAUTH_REQ,
828         ITOK_TYPE_REAUTH_RESP,
829         GSSEAP_STATE_INITIAL | GSSEAP_STATE_REAUTHENTICATE,
830         0,
831         eapGssSmAcceptGssReauth,
832     },
833 #endif
834     {
835         ITOK_TYPE_ACCEPTOR_NAME_REQ,
836         ITOK_TYPE_ACCEPTOR_NAME_RESP,
837         GSSEAP_STATE_INITIAL,
838         0,
839         eapGssSmAcceptAcceptorName
840     },
841     {
842         ITOK_TYPE_SUPPORTED_ACCEPTOR_EXTS,
843         ITOK_TYPE_SUPPORTED_INITIATOR_EXTS,
844         GSSEAP_STATE_INITIAL,
845         0,
846         eapGssSmAcceptExts,
847     },
848 #ifdef GSSEAP_DEBUG
849     {
850         ITOK_TYPE_VENDOR_INFO,
851         ITOK_TYPE_NONE,
852         GSSEAP_STATE_INITIAL,
853         0,
854         eapGssSmAcceptVendorInfo,
855     },
856 #endif
857     {
858         ITOK_TYPE_NONE,
859         ITOK_TYPE_EAP_REQ,
860         GSSEAP_STATE_INITIAL,
861         SM_ITOK_FLAG_REQUIRED,
862         eapGssSmAcceptIdentity,
863     },
864     {
865         ITOK_TYPE_EAP_RESP,
866         ITOK_TYPE_EAP_REQ,
867         GSSEAP_STATE_AUTHENTICATE,
868         SM_ITOK_FLAG_REQUIRED,
869         eapGssSmAcceptAuthenticate
870     },
871     {
872         ITOK_TYPE_GSS_FLAGS,
873         ITOK_TYPE_NONE,
874         GSSEAP_STATE_INITIATOR_EXTS,
875         0,
876         eapGssSmAcceptGssFlags
877     },
878     {
879         ITOK_TYPE_GSS_CHANNEL_BINDINGS,
880         ITOK_TYPE_NONE,
881         GSSEAP_STATE_INITIATOR_EXTS,
882         0,
883         eapGssSmAcceptGssChannelBindings,
884     },
885     {
886         ITOK_TYPE_INITIATOR_MIC,
887         ITOK_TYPE_NONE,
888         GSSEAP_STATE_INITIATOR_EXTS,
889         SM_ITOK_FLAG_REQUIRED,
890         eapGssSmAcceptInitiatorMIC,
891     },
892 #ifdef GSSEAP_ENABLE_REAUTH
893     {
894         ITOK_TYPE_NONE,
895         ITOK_TYPE_REAUTH_CREDS,
896         GSSEAP_STATE_ACCEPTOR_EXTS,
897         0,
898         eapGssSmAcceptReauthCreds,
899     },
900 #endif
901     {
902         ITOK_TYPE_NONE,
903         ITOK_TYPE_ACCEPTOR_MIC,
904         GSSEAP_STATE_ACCEPTOR_EXTS,
905         0,
906         eapGssSmAcceptAcceptorMIC
907     },
908 };
909
910 OM_uint32
911 gss_accept_sec_context(OM_uint32 *minor,
912                        gss_ctx_id_t *context_handle,
913                        gss_cred_id_t cred,
914                        gss_buffer_t input_token,
915                        gss_channel_bindings_t input_chan_bindings,
916                        gss_name_t *src_name,
917                        gss_OID *mech_type,
918                        gss_buffer_t output_token,
919                        OM_uint32 *ret_flags,
920                        OM_uint32 *time_rec,
921                        gss_cred_id_t *delegated_cred_handle)
922 {
923     OM_uint32 major, tmpMinor;
924     gss_ctx_id_t ctx = *context_handle;
925
926     *minor = 0;
927
928     output_token->length = 0;
929     output_token->value = NULL;
930
931     if (src_name != NULL)
932         *src_name = GSS_C_NO_NAME;
933
934     if (input_token == GSS_C_NO_BUFFER || input_token->length == 0) {
935         *minor = GSSEAP_TOK_TRUNC;
936         return GSS_S_DEFECTIVE_TOKEN;
937     }
938
939     if (ctx == GSS_C_NO_CONTEXT) {
940         major = gssEapAllocContext(minor, &ctx);
941         if (GSS_ERROR(major))
942             return major;
943
944         *context_handle = ctx;
945     }
946
947     GSSEAP_MUTEX_LOCK(&ctx->mutex);
948
949     if (cred == GSS_C_NO_CREDENTIAL) {
950         if (ctx->defaultCred == GSS_C_NO_CREDENTIAL) {
951             major = gssEapAcquireCred(minor,
952                                       GSS_C_NO_NAME,
953                                       GSS_C_NO_BUFFER,
954                                       GSS_C_INDEFINITE,
955                                       GSS_C_NO_OID_SET,
956                                       GSS_C_ACCEPT,
957                                       &ctx->defaultCred,
958                                       NULL,
959                                       NULL);
960             if (GSS_ERROR(major))
961                 goto cleanup;
962         }
963
964         cred = ctx->defaultCred;
965     }
966
967     GSSEAP_MUTEX_LOCK(&cred->mutex);
968
969     if (cred->name != GSS_C_NO_NAME) {
970         major = gssEapDuplicateName(minor, cred->name, &ctx->acceptorName);
971         if (GSS_ERROR(major))
972             goto cleanup;
973     }
974
975     major = gssEapSmStep(minor,
976                          cred,
977                          ctx,
978                          GSS_C_NO_NAME,
979                          GSS_C_NO_OID,
980                          0,
981                          GSS_C_INDEFINITE,
982                          input_chan_bindings,
983                          input_token,
984                          output_token,
985                          eapGssAcceptorSm,
986                          sizeof(eapGssAcceptorSm) / sizeof(eapGssAcceptorSm[0]));
987     if (GSS_ERROR(major))
988         goto cleanup;
989
990     if (mech_type != NULL) {
991         OM_uint32 tmpMajor;
992
993         tmpMajor = gssEapCanonicalizeOid(&tmpMinor, ctx->mechanismUsed, 0, mech_type);
994         if (GSS_ERROR(tmpMajor)) {
995             major = tmpMajor;
996             *minor = tmpMinor;
997             goto cleanup;
998         }
999     }
1000     if (ret_flags != NULL)
1001         *ret_flags = ctx->gssFlags;
1002     if (delegated_cred_handle != NULL)
1003         *delegated_cred_handle = GSS_C_NO_CREDENTIAL;
1004
1005     if (major == GSS_S_COMPLETE) {
1006         if (src_name != NULL && ctx->initiatorName != GSS_C_NO_NAME) {
1007             major = gssEapDuplicateName(&tmpMinor, ctx->initiatorName, src_name);
1008             if (GSS_ERROR(major))
1009                 goto cleanup;
1010         }
1011         if (time_rec != NULL) {
1012             major = gssEapContextTime(&tmpMinor, ctx, time_rec);
1013             if (GSS_ERROR(major))
1014                 goto cleanup;
1015         }
1016     }
1017
1018     assert(CTX_IS_ESTABLISHED(ctx) || major == GSS_S_CONTINUE_NEEDED);
1019
1020 cleanup:
1021     if (cred != GSS_C_NO_CREDENTIAL)
1022         GSSEAP_MUTEX_UNLOCK(&cred->mutex);
1023     GSSEAP_MUTEX_UNLOCK(&ctx->mutex);
1024
1025     if (GSS_ERROR(major))
1026         gssEapReleaseContext(&tmpMinor, context_handle);
1027
1028     return major;
1029 }
1030
1031 #ifdef GSSEAP_ENABLE_REAUTH
1032 static OM_uint32
1033 acceptReadyKrb(OM_uint32 *minor,
1034                gss_ctx_id_t ctx,
1035                gss_cred_id_t cred,
1036                const gss_name_t initiator,
1037                const gss_OID mech,
1038                OM_uint32 timeRec)
1039 {
1040     OM_uint32 major;
1041
1042     major = gssEapGlueToMechName(minor, ctx, initiator, &ctx->initiatorName);
1043     if (GSS_ERROR(major))
1044         return major;
1045
1046     major = gssEapReauthComplete(minor, ctx, cred, mech, timeRec);
1047     if (GSS_ERROR(major))
1048         return major;
1049
1050     *minor = 0;
1051     return GSS_S_COMPLETE;
1052 }
1053
1054 static OM_uint32
1055 eapGssSmAcceptGssReauth(OM_uint32 *minor,
1056                         gss_cred_id_t cred,
1057                         gss_ctx_id_t ctx,
1058                         gss_name_t target GSSEAP_UNUSED,
1059                         gss_OID mech,
1060                         OM_uint32 reqFlags GSSEAP_UNUSED,
1061                         OM_uint32 timeReq GSSEAP_UNUSED,
1062                         gss_channel_bindings_t userChanBindings,
1063                         gss_buffer_t inputToken,
1064                         gss_buffer_t outputToken,
1065                         OM_uint32 *smFlags)
1066 {
1067     OM_uint32 major, tmpMinor;
1068     gss_name_t krbInitiator = GSS_C_NO_NAME;
1069     OM_uint32 gssFlags, timeRec = GSS_C_INDEFINITE;
1070     struct gss_channel_bindings_struct wireChanBindings = { 0 };
1071
1072     /*
1073      * If we're built with fast reauthentication support, it's valid
1074      * for an initiator to send a GSS reauthentication token as its
1075      * initial context token, causing us to short-circuit the state
1076      * machine and process Kerberos GSS messages instead.
1077      */
1078
1079     ctx->flags |= CTX_FLAG_KRB_REAUTH;
1080
1081     /*
1082      * To avoid an additional round trip, we use GSS channel bindings
1083      * to integrity protect the rest of the initiator exchange. This
1084      * does have the disadvantage of making it impossible for the
1085      * acceptor to ignore application channel bindings, behaviour
1086      * which differs from normal Kerberos and GSS-EAP itself.
1087      */
1088     major = gssEapMakeTokenChannelBindings(minor, ctx,
1089                                            userChanBindings,
1090                                            inputToken,
1091                                            &wireChanBindings);
1092     if (GSS_ERROR(major))
1093         return major;
1094
1095     major = gssAcceptSecContext(minor,
1096                                 &ctx->kerberosCtx,
1097                                 cred->krbCred,
1098                                 inputToken,
1099                                 &wireChanBindings,
1100                                 &krbInitiator,
1101                                 &mech,
1102                                 outputToken,
1103                                 &gssFlags,
1104                                 &timeRec,
1105                                 NULL);
1106     if (major == GSS_S_COMPLETE) {
1107         major = acceptReadyKrb(minor, ctx, cred,
1108                                krbInitiator, mech, timeRec);
1109         if (major == GSS_S_COMPLETE) {
1110             /* Generate acceptor MIC */
1111             GSSEAP_SM_TRANSITION(ctx, GSSEAP_STATE_ACCEPTOR_EXTS);
1112         }
1113         ctx->gssFlags = gssFlags;
1114     } else if (GSS_ERROR(major) &&
1115         (*smFlags & SM_FLAG_INPUT_TOKEN_CRITICAL) == 0) {
1116         /* Fall back to EAP */
1117         gssDeleteSecContext(&tmpMinor, &ctx->kerberosCtx, GSS_C_NO_BUFFER);
1118         ctx->flags &= ~(CTX_FLAG_KRB_REAUTH);
1119         GSSEAP_SM_TRANSITION(ctx, GSSEAP_STATE_INITIAL);
1120     } else {
1121         GSSEAP_SM_TRANSITION(ctx, GSSEAP_STATE_REAUTHENTICATE);
1122     }
1123
1124     major = GSS_S_CONTINUE_NEEDED;
1125
1126     gssReleaseName(&tmpMinor, &krbInitiator);
1127     gss_release_buffer(&tmpMinor, &wireChanBindings.application_data);
1128
1129     return major;
1130 }
1131 #endif /* GSSEAP_ENABLE_REAUTH */