Sent @REALM in EAP identity response
[mech_eap.orig] / 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_ctx_id_t ctx,
44                         gss_cred_id_t cred,
45                         gss_buffer_t inputToken,
46                         gss_channel_bindings_t chanBindings,
47                         gss_buffer_t outputToken);
48 #endif
49
50 /*
51  * Mark an acceptor context as ready for cryptographic operations
52  */
53 static OM_uint32
54 acceptReadyEap(OM_uint32 *minor, gss_ctx_id_t ctx, gss_cred_id_t cred)
55 {
56     OM_uint32 major, tmpMinor;
57     VALUE_PAIR *vp;
58     gss_buffer_desc nameBuf = GSS_C_EMPTY_BUFFER;
59
60     /* Cache encryption type derived from selected mechanism OID */
61     major = gssEapOidToEnctype(minor, ctx->mechanismUsed,
62                                &ctx->encryptionType);
63     if (GSS_ERROR(major))
64         return major;
65
66     gssEapReleaseName(&tmpMinor, &ctx->initiatorName);
67
68     major = gssEapRadiusGetRawAvp(minor, ctx->acceptorCtx.vps,
69                                   PW_USER_NAME, 0, &vp);
70     if (major == GSS_S_COMPLETE) {
71         nameBuf.length = vp->length;
72         nameBuf.value = vp->vp_strvalue;
73     } else {
74         ctx->gssFlags |= GSS_C_ANON_FLAG;
75     }
76
77     major = gssEapImportName(minor, &nameBuf,
78                              (ctx->gssFlags & GSS_C_ANON_FLAG) ?
79                                 GSS_C_NT_ANONYMOUS : GSS_C_NT_USER_NAME,
80                              &ctx->initiatorName);
81     if (GSS_ERROR(major))
82         return major;
83
84     major = gssEapRadiusGetRawAvp(minor, ctx->acceptorCtx.vps,
85                                   PW_MS_MPPE_SEND_KEY, VENDORPEC_MS, &vp);
86     if (GSS_ERROR(major)) {
87         *minor = GSSEAP_KEY_UNAVAILABLE;
88         return GSS_S_UNAVAILABLE;
89     }
90
91     major = gssEapDeriveRfc3961Key(minor,
92                                    vp->vp_octets,
93                                    vp->length,
94                                    ctx->encryptionType,
95                                    &ctx->rfc3961Key);
96     if (GSS_ERROR(major))
97         return major;
98
99     major = rfc3961ChecksumTypeForKey(minor, &ctx->rfc3961Key,
100                                        &ctx->checksumType);
101     if (GSS_ERROR(major))
102         return major;
103
104     major = sequenceInit(minor,
105                          &ctx->seqState, ctx->recvSeq,
106                          ((ctx->gssFlags & GSS_C_REPLAY_FLAG) != 0),
107                          ((ctx->gssFlags & GSS_C_SEQUENCE_FLAG) != 0),
108                          TRUE);
109     if (GSS_ERROR(major))
110         return major;
111
112     major = gssEapCreateAttrContext(minor, cred, ctx,
113                                     &ctx->initiatorName->attrCtx,
114                                     &ctx->expiryTime);
115     if (GSS_ERROR(major))
116         return major;
117
118     *minor = 0;
119     return GSS_S_COMPLETE;
120 }
121
122 /*
123  * Emit a identity EAP request to force the initiator (peer) to identify
124  * itself.
125  */
126 static OM_uint32
127 eapGssSmAcceptIdentity(OM_uint32 *minor,
128                        gss_ctx_id_t ctx,
129                        gss_cred_id_t cred,
130                        gss_buffer_t inputToken,
131                        gss_channel_bindings_t chanBindings,
132                        gss_buffer_t outputToken)
133 {
134     OM_uint32 major;
135     struct wpabuf *reqData;
136     gss_buffer_desc pktBuffer;
137
138     if (inputToken != GSS_C_NO_BUFFER && inputToken->length != 0) {
139         *minor = GSSEAP_WRONG_SIZE;
140         return GSS_S_DEFECTIVE_TOKEN;
141     }
142
143     assert(ctx->acceptorName == GSS_C_NO_NAME);
144
145     if (cred->name != GSS_C_NO_NAME) {
146         major = gssEapDuplicateName(minor, cred->name, &ctx->acceptorName);
147         if (GSS_ERROR(major))
148             return major;
149     }
150
151     reqData = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_IDENTITY, 0,
152                             EAP_CODE_REQUEST, 0);
153     if (reqData == NULL) {
154         *minor = ENOMEM;
155         return GSS_S_FAILURE;
156     }
157
158     pktBuffer.length = wpabuf_len(reqData);
159     pktBuffer.value = (void *)wpabuf_head(reqData);
160
161     major = duplicateBuffer(minor, &pktBuffer, outputToken);
162     if (GSS_ERROR(major))
163         return major;
164
165     ctx->state = GSSEAP_STATE_AUTHENTICATE;
166
167     wpabuf_free(reqData);
168
169     *minor = 0;
170     return GSS_S_CONTINUE_NEEDED;
171 }
172
173 /*
174  * Returns TRUE if the input token contains an EAP identity response.
175  */
176 static int
177 isIdentityResponseP(gss_buffer_t inputToken)
178 {
179     struct wpabuf respData;
180
181     wpabuf_set(&respData, inputToken->value, inputToken->length);
182
183     return (eap_get_type(&respData) == EAP_TYPE_IDENTITY);
184 }
185
186 /*
187  * Pass the asserted initiator identity to the authentication server.
188  */
189 static OM_uint32
190 setInitiatorIdentity(OM_uint32 *minor,
191                      gss_buffer_t inputToken,
192                      VALUE_PAIR **vps)
193 {
194     struct wpabuf respData;
195     const unsigned char *pos;
196     size_t len;
197     gss_buffer_desc nameBuf;
198
199     wpabuf_set(&respData, inputToken->value, inputToken->length);
200
201     pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_IDENTITY,
202                            &respData, &len);
203     if (pos == NULL) {
204         *minor = GSSEAP_PEER_BAD_MESSAGE;
205         return GSS_S_DEFECTIVE_TOKEN;
206     }
207
208     nameBuf.value = (void *)pos;
209     nameBuf.length = len;
210
211     return gssEapRadiusAddAvp(minor, vps, PW_USER_NAME, 0, &nameBuf);
212 }
213
214 /*
215  * Pass the asserted acceptor identity to the authentication server.
216  */
217 static OM_uint32
218 setAcceptorIdentity(OM_uint32 *minor,
219                     gss_ctx_id_t ctx,
220                     VALUE_PAIR **vps)
221 {
222     OM_uint32 major;
223     gss_buffer_desc nameBuf;
224     krb5_context krbContext = NULL;
225     krb5_principal krbPrinc;
226     struct rs_context *rc = ctx->acceptorCtx.radContext;
227
228     assert(rc != NULL);
229
230     if (ctx->acceptorName == GSS_C_NO_NAME) {
231         *minor = 0;
232         return GSS_S_COMPLETE;
233     }
234
235     if ((ctx->acceptorName->flags & NAME_FLAG_SERVICE) == 0) {
236         *minor = GSSEAP_BAD_SERVICE_NAME;
237         return GSS_S_BAD_NAME;
238     }
239
240     GSSEAP_KRB_INIT(&krbContext);
241
242     krbPrinc = ctx->acceptorName->krbPrincipal;
243     assert(krbPrinc != NULL);
244     assert(KRB_PRINC_LENGTH(krbPrinc) >= 2);
245
246     /* Acceptor-Service-Name */
247     krbPrincComponentToGssBuffer(krbPrinc, 0, &nameBuf);
248
249     major = gssEapRadiusAddAvp(minor, vps,
250                                PW_GSS_ACCEPTOR_SERVICE_NAME,
251                                VENDORPEC_UKERNA,
252                                &nameBuf);
253     if (GSS_ERROR(major))
254         return major;
255
256     /* Acceptor-Host-Name */
257     krbPrincComponentToGssBuffer(krbPrinc, 1, &nameBuf);
258
259     major = gssEapRadiusAddAvp(minor, vps,
260                                PW_GSS_ACCEPTOR_HOST_NAME,
261                                VENDORPEC_UKERNA,
262                                &nameBuf);
263     if (GSS_ERROR(major))
264         return major;
265
266     if (KRB_PRINC_LENGTH(krbPrinc) > 2) {
267         /* Acceptor-Service-Specific */
268         krb5_principal_data ssiPrinc = *krbPrinc;
269         char *ssi;
270
271         KRB_PRINC_LENGTH(&ssiPrinc) -= 2;
272         KRB_PRINC_NAME(&ssiPrinc) += 2;
273
274         *minor = krb5_unparse_name_flags(krbContext, &ssiPrinc,
275                                          KRB5_PRINCIPAL_UNPARSE_NO_REALM, &ssi);
276         if (*minor != 0)
277             return GSS_S_FAILURE;
278
279         nameBuf.value = ssi;
280         nameBuf.length = strlen(ssi);
281
282         major = gssEapRadiusAddAvp(minor, vps,
283                                    PW_GSS_ACCEPTOR_SERVICE_SPECIFIC,
284                                    VENDORPEC_UKERNA,
285                                    &nameBuf);
286
287         if (GSS_ERROR(major)) {
288             krb5_free_unparsed_name(krbContext, ssi);
289             return major;
290         }
291         krb5_free_unparsed_name(krbContext, ssi);
292     }
293
294     krbPrincRealmToGssBuffer(krbPrinc, &nameBuf);
295     if (nameBuf.length != 0) {
296         /* Acceptor-Realm-Name */
297         major = gssEapRadiusAddAvp(minor, vps,
298                                    PW_GSS_ACCEPTOR_REALM_NAME,
299                                    VENDORPEC_UKERNA,
300                                    &nameBuf);
301         if (GSS_ERROR(major))
302             return major;
303     }
304
305     *minor = 0;
306     return GSS_S_COMPLETE;
307 }
308
309 /*
310  * Allocate a RadSec handle
311  */
312 static OM_uint32
313 createRadiusHandle(OM_uint32 *minor,
314                    gss_cred_id_t cred,
315                    gss_ctx_id_t ctx)
316 {
317     struct gss_eap_acceptor_ctx *actx = &ctx->acceptorCtx;
318     const char *configFile = RS_CONFIG_FILE;
319     const char *configStanza = "gss-eap";
320     struct rs_alloc_scheme ralloc;
321     struct rs_error *err;
322
323     assert(actx->radContext == NULL);
324     assert(actx->radConn == NULL);
325
326     if (rs_context_create(&actx->radContext, RS_DICT_FILE) != 0) {
327         *minor = GSSEAP_RADSEC_CONTEXT_FAILURE;
328         return GSS_S_FAILURE;
329     }
330
331     if (cred->radiusConfigFile != NULL)
332         configFile = cred->radiusConfigFile;
333     if (cred->radiusConfigStanza != NULL)
334         configStanza = cred->radiusConfigStanza;
335
336     ralloc.calloc  = GSSEAP_CALLOC;
337     ralloc.malloc  = GSSEAP_MALLOC;
338     ralloc.free    = GSSEAP_FREE;
339     ralloc.realloc = GSSEAP_REALLOC;
340
341     rs_context_set_alloc_scheme(actx->radContext, &ralloc);
342
343     if (rs_context_read_config(actx->radContext, configFile) != 0) {
344         err = rs_err_ctx_pop(actx->radContext);
345         goto fail;
346     }
347
348     if (rs_conn_create(actx->radContext, &actx->radConn, configStanza) != 0) {
349         err = rs_err_conn_pop(actx->radConn);
350         goto fail;
351     }
352
353     if (actx->radServer != NULL) {
354         if (rs_conn_select_peer(actx->radConn, actx->radServer) != 0) {
355             err = rs_err_conn_pop(actx->radConn);
356             goto fail;
357         }
358     }
359
360     *minor = 0;
361     return GSS_S_COMPLETE;
362
363 fail:
364     return gssEapRadiusMapError(minor, err);
365 }
366
367 /*
368  * Process a EAP response from the initiator.
369  */
370 static OM_uint32
371 eapGssSmAcceptAuthenticate(OM_uint32 *minor,
372                            gss_ctx_id_t ctx,
373                            gss_cred_id_t cred,
374                            gss_buffer_t inputToken,
375                            gss_channel_bindings_t chanBindings,
376                            gss_buffer_t outputToken)
377 {
378     OM_uint32 major, tmpMinor;
379     struct rs_connection *rconn;
380     struct rs_request *request = NULL;
381     struct rs_packet *req = NULL, *resp = NULL;
382     struct radius_packet *frreq, *frresp;
383     int isIdentityResponse = isIdentityResponseP(inputToken);
384
385     if (ctx->acceptorCtx.radContext == NULL) {
386         /* May be NULL from an imported partial context */
387         major = createRadiusHandle(minor, cred, ctx);
388         if (GSS_ERROR(major))
389             goto cleanup;
390     }
391
392     rconn = ctx->acceptorCtx.radConn;
393
394     if (rs_packet_create_authn_request(rconn, &req, NULL, NULL) != 0) {
395         major = gssEapRadiusMapError(minor, rs_err_conn_pop(rconn));
396         goto cleanup;
397     }
398     frreq = rs_packet_frpkt(req);
399
400     if (isIdentityResponse) {
401         major = setInitiatorIdentity(minor, inputToken, &frreq->vps);
402         if (GSS_ERROR(major))
403             goto cleanup;
404
405         major = setAcceptorIdentity(minor, ctx, &frreq->vps);
406         if (GSS_ERROR(major))
407             goto cleanup;
408     }
409
410     major = gssEapRadiusAddAvp(minor, &frreq->vps,
411                                PW_EAP_MESSAGE, 0, inputToken);
412     if (GSS_ERROR(major))
413         goto cleanup;
414
415     if (ctx->acceptorCtx.state.length != 0) {
416         major = gssEapRadiusAddAvp(minor, &frreq->vps, PW_STATE, 0,
417                                    &ctx->acceptorCtx.state);
418         if (GSS_ERROR(major))
419             goto cleanup;
420
421         gss_release_buffer(&tmpMinor, &ctx->acceptorCtx.state);
422     }
423
424     if (rs_request_create(rconn, &request) != 0) {
425         major = gssEapRadiusMapError(minor, rs_err_conn_pop(rconn));
426         goto cleanup;
427     }
428
429     rs_request_add_reqpkt(request, req);
430     req = NULL;
431
432     if (rs_request_send(request, &resp) != 0) {
433         major = gssEapRadiusMapError(minor, rs_err_conn_pop(rconn));
434         goto cleanup;
435     }
436
437     assert(resp != NULL);
438
439     frresp = rs_packet_frpkt(resp);
440     switch (frresp->code) {
441     case PW_AUTHENTICATION_ACK:
442     case PW_ACCESS_CHALLENGE:
443         major = GSS_S_CONTINUE_NEEDED;
444         break;
445     case PW_AUTHENTICATION_REJECT:
446         *minor = GSSEAP_RADIUS_AUTH_FAILURE;
447         major = GSS_S_DEFECTIVE_CREDENTIAL;
448         goto cleanup;
449         break;
450     default:
451         *minor = GSSEAP_UNKNOWN_RADIUS_CODE;
452         major = GSS_S_FAILURE;
453         goto cleanup;
454         break;
455     }
456
457     major = gssEapRadiusGetAvp(minor, frresp->vps, PW_EAP_MESSAGE, 0,
458                                outputToken, TRUE);
459     if (major == GSS_S_UNAVAILABLE && frresp->code == PW_ACCESS_CHALLENGE) {
460         *minor = GSSEAP_MISSING_EAP_REQUEST;
461         major = GSS_S_DEFECTIVE_TOKEN;
462         goto cleanup;
463     } else if (GSS_ERROR(major))
464         goto cleanup;
465
466     if (frresp->code == PW_ACCESS_CHALLENGE) {
467         major = gssEapRadiusGetAvp(minor, frresp->vps, PW_STATE, 0,
468                                    &ctx->acceptorCtx.state, TRUE);
469         if (GSS_ERROR(major) && *minor != GSSEAP_NO_SUCH_ATTR)
470             goto cleanup;
471     } else {
472         ctx->acceptorCtx.vps = frresp->vps;
473         frresp->vps = NULL;
474
475         rs_conn_destroy(ctx->acceptorCtx.radConn);
476         ctx->acceptorCtx.radConn = NULL;
477
478         major = acceptReadyEap(minor, ctx, cred);
479         if (GSS_ERROR(major))
480             goto cleanup;
481
482         ctx->state = GSSEAP_STATE_EXTENSIONS_REQ;
483     }
484
485     *minor = 0;
486     major = GSS_S_CONTINUE_NEEDED;
487
488 cleanup:
489     if (request != NULL)
490         rs_request_destroy(request);
491     if (req != NULL)
492         rs_packet_destroy(req);
493
494     return major;
495 }
496
497 static OM_uint32
498 eapGssSmAcceptExtensionsReq(OM_uint32 *minor,
499                             gss_ctx_id_t ctx,
500                             gss_cred_id_t cred,
501                             gss_buffer_t inputToken,
502                             gss_channel_bindings_t chanBindings,
503                             gss_buffer_t outputToken)
504 {
505     OM_uint32 major;
506
507     major = gssEapVerifyExtensions(minor, cred, ctx, chanBindings, inputToken);
508     if (GSS_ERROR(major))
509         return major;
510
511     outputToken->length = 0;
512     outputToken->value = NULL;
513
514     ctx->state = GSSEAP_STATE_EXTENSIONS_RESP;
515
516     *minor = 0;
517     return GSS_S_CONTINUE_NEEDED;
518 }
519
520 static OM_uint32
521 eapGssSmAcceptExtensionsResp(OM_uint32 *minor,
522                              gss_ctx_id_t ctx,
523                              gss_cred_id_t cred,
524                              gss_buffer_t inputToken,
525                              gss_channel_bindings_t chanBindings,
526                              gss_buffer_t outputToken)
527 {
528     OM_uint32 major;
529
530     major = gssEapMakeExtensions(minor, cred, ctx, chanBindings, outputToken);
531     if (GSS_ERROR(major))
532         return major;
533
534     ctx->state = GSSEAP_STATE_ESTABLISHED;
535
536     *minor = 0;
537     return GSS_S_COMPLETE;
538 }
539
540 static OM_uint32
541 eapGssSmAcceptEstablished(OM_uint32 *minor,
542                           gss_ctx_id_t ctx,
543                           gss_cred_id_t cred,
544                           gss_buffer_t inputToken,
545                           gss_channel_bindings_t chanBindings,
546                           gss_buffer_t outputToken)
547 {
548     /* Called with already established context */
549     *minor = GSSEAP_CONTEXT_ESTABLISHED;
550     return GSS_S_BAD_STATUS;
551 }
552
553 static OM_uint32
554 makeErrorToken(OM_uint32 *minor,
555                OM_uint32 majorStatus,
556                OM_uint32 minorStatus,
557                gss_buffer_t outputToken)
558 {
559     unsigned char errorData[8];
560     gss_buffer_desc errorBuffer;
561
562     assert(GSS_ERROR(majorStatus));
563
564     /*
565      * Only return error codes that the initiator could have caused,
566      * to avoid information leakage.
567      */
568     if (IS_RADIUS_ERROR(minorStatus)) {
569         /* Squash RADIUS error codes */
570         minorStatus = GSSEAP_RADIUS_PROT_FAILURE;
571     } else if (!IS_WIRE_ERROR(minorStatus)) {
572         /* Don't return non-wire error codes */
573         return GSS_S_COMPLETE;
574     }
575
576     minorStatus -= ERROR_TABLE_BASE_eapg;
577
578     store_uint32_be(majorStatus, &errorData[0]);
579     store_uint32_be(minorStatus, &errorData[4]);
580
581     errorBuffer.length = sizeof(errorData);
582     errorBuffer.value = errorData;
583
584     return duplicateBuffer(minor, &errorBuffer, outputToken);
585 }
586
587 static struct gss_eap_acceptor_sm {
588     enum gss_eap_token_type inputTokenType;
589     enum gss_eap_token_type outputTokenType;
590     OM_uint32 (*processToken)(OM_uint32 *,
591                               gss_ctx_id_t,
592                               gss_cred_id_t,
593                               gss_buffer_t,
594                               gss_channel_bindings_t,
595                               gss_buffer_t);
596 } eapGssAcceptorSm[] = {
597     { TOK_TYPE_EAP_RESP,    TOK_TYPE_EAP_REQ,       eapGssSmAcceptIdentity           },
598     { TOK_TYPE_EAP_RESP,    TOK_TYPE_EAP_REQ,       eapGssSmAcceptAuthenticate       },
599     { TOK_TYPE_EXT_REQ,     TOK_TYPE_NONE,          eapGssSmAcceptExtensionsReq      },
600     { TOK_TYPE_NONE,        TOK_TYPE_EXT_RESP,      eapGssSmAcceptExtensionsResp     },
601     { TOK_TYPE_NONE,        TOK_TYPE_NONE,          eapGssSmAcceptEstablished        },
602     { TOK_TYPE_NONE,        TOK_TYPE_CONTEXT_ERR,   NULL                             },
603 #ifdef GSSEAP_ENABLE_REAUTH
604     { TOK_TYPE_GSS_REAUTH,  TOK_TYPE_GSS_REAUTH,    eapGssSmAcceptGssReauth          },
605 #endif
606 };
607
608 OM_uint32
609 gss_accept_sec_context(OM_uint32 *minor,
610                        gss_ctx_id_t *context_handle,
611                        gss_cred_id_t cred,
612                        gss_buffer_t input_token,
613                        gss_channel_bindings_t input_chan_bindings,
614                        gss_name_t *src_name,
615                        gss_OID *mech_type,
616                        gss_buffer_t output_token,
617                        OM_uint32 *ret_flags,
618                        OM_uint32 *time_rec,
619                        gss_cred_id_t *delegated_cred_handle)
620 {
621     OM_uint32 major;
622     OM_uint32 tmpMajor, tmpMinor;
623     gss_ctx_id_t ctx = *context_handle;
624     struct gss_eap_acceptor_sm *sm = NULL;
625     gss_buffer_desc innerInputToken = GSS_C_EMPTY_BUFFER;
626     gss_buffer_desc innerOutputToken = GSS_C_EMPTY_BUFFER;
627     enum gss_eap_token_type tokType;
628     int initialContextToken = 0;
629
630     *minor = 0;
631
632     output_token->length = 0;
633     output_token->value = NULL;
634
635     if (src_name != NULL)
636         *src_name = GSS_C_NO_NAME;
637
638     if (input_token == GSS_C_NO_BUFFER || input_token->length == 0) {
639         *minor = GSSEAP_TOK_TRUNC;
640         return GSS_S_DEFECTIVE_TOKEN;
641     }
642
643     if (ctx == GSS_C_NO_CONTEXT) {
644         major = gssEapAllocContext(minor, &ctx);
645         if (GSS_ERROR(major))
646             return major;
647
648         initialContextToken = 1;
649         *context_handle = ctx;
650     }
651
652     GSSEAP_MUTEX_LOCK(&ctx->mutex);
653
654     if (cred == GSS_C_NO_CREDENTIAL) {
655         if (ctx->defaultCred == GSS_C_NO_CREDENTIAL) {
656             major = gssEapAcquireCred(minor,
657                                       GSS_C_NO_NAME,
658                                       GSS_C_NO_BUFFER,
659                                       GSS_C_INDEFINITE,
660                                       GSS_C_NO_OID_SET,
661                                       GSS_C_ACCEPT,
662                                       &ctx->defaultCred,
663                                       NULL,
664                                       NULL);
665             if (GSS_ERROR(major))
666                 goto cleanup;
667         }
668
669         cred = ctx->defaultCred;
670     }
671
672     GSSEAP_MUTEX_LOCK(&cred->mutex);
673
674     sm = &eapGssAcceptorSm[ctx->state];
675
676     major = gssEapVerifyToken(minor, ctx, input_token,
677                               &tokType, &innerInputToken);
678     if (GSS_ERROR(major))
679         goto cleanup;
680
681     if (!gssEapCredAvailable(cred, ctx->mechanismUsed)) {
682         *minor = GSSEAP_CRED_MECH_MISMATCH;
683         major = GSS_S_BAD_MECH;
684         goto cleanup;
685     }
686
687 #ifdef GSSEAP_ENABLE_REAUTH
688     /*
689      * If we're built with fast reauthentication support, it's valid
690      * for an initiator to send a GSS reauthentication token as its
691      * initial context token, causing us to short-circuit the state
692      * machine and process Kerberos GSS messages instead.
693      */
694     if (tokType == TOK_TYPE_GSS_REAUTH && initialContextToken) {
695         ctx->state = GSSEAP_STATE_KRB_REAUTH;
696     } else
697 #endif
698     if (tokType != sm->inputTokenType) {
699         *minor = GSSEAP_WRONG_TOK_ID;
700         major = GSS_S_DEFECTIVE_TOKEN;
701         goto cleanup;
702     }
703
704     do {
705         sm = &eapGssAcceptorSm[ctx->state];
706
707         major = (sm->processToken)(minor,
708                                    ctx,
709                                    cred,
710                                    &innerInputToken,
711                                    input_chan_bindings,
712                                    &innerOutputToken);
713         if (GSS_ERROR(major)) {
714             /* Possibly generate an error token */
715             tmpMajor = makeErrorToken(&tmpMinor, major, *minor, &innerOutputToken);
716             if (GSS_ERROR(tmpMajor)) {
717                 major = tmpMajor;
718                 goto cleanup;
719             }
720
721             sm = &eapGssAcceptorSm[GSSEAP_STATE_ERROR];
722             goto send_token;
723         }
724     } while (major == GSS_S_CONTINUE_NEEDED && innerOutputToken.length == 0);
725
726     if (mech_type != NULL) {
727         if (!gssEapInternalizeOid(ctx->mechanismUsed, mech_type))
728             duplicateOid(&tmpMinor, ctx->mechanismUsed, mech_type);
729     }
730     if (ret_flags != NULL)
731         *ret_flags = ctx->gssFlags;
732     if (delegated_cred_handle != NULL)
733         *delegated_cred_handle = GSS_C_NO_CREDENTIAL;
734
735     if (major == GSS_S_COMPLETE) {
736         if (src_name != NULL && ctx->initiatorName != GSS_C_NO_NAME) {
737             major = gssEapDuplicateName(&tmpMinor, ctx->initiatorName, src_name);
738             if (GSS_ERROR(major))
739                 goto cleanup;
740         }
741         if (time_rec != NULL) {
742             major = gssEapContextTime(&tmpMinor, ctx, time_rec);
743             if (GSS_ERROR(major))
744                 goto cleanup;
745         }
746     }
747
748     assert(ctx->state == GSSEAP_STATE_ESTABLISHED || major == GSS_S_CONTINUE_NEEDED);
749
750 send_token:
751     if (innerOutputToken.value != NULL) {
752         tmpMajor = gssEapMakeToken(&tmpMinor, ctx, &innerOutputToken,
753                                    sm->outputTokenType, output_token);
754         if (GSS_ERROR(tmpMajor)) {
755             major = tmpMajor;
756             *minor = tmpMinor;
757             goto cleanup;
758         }
759     }
760
761 cleanup:
762     if (cred != GSS_C_NO_CREDENTIAL)
763         GSSEAP_MUTEX_UNLOCK(&cred->mutex);
764     GSSEAP_MUTEX_UNLOCK(&ctx->mutex);
765
766     if (GSS_ERROR(major))
767         gssEapReleaseContext(&tmpMinor, context_handle);
768
769     gss_release_buffer(&tmpMinor, &innerOutputToken);
770
771     return major;
772 }
773
774 #ifdef GSSEAP_ENABLE_REAUTH
775 static OM_uint32
776 acceptReadyKrb(OM_uint32 *minor,
777                gss_ctx_id_t ctx,
778                gss_cred_id_t cred,
779                const gss_name_t initiator,
780                const gss_OID mech,
781                OM_uint32 timeRec)
782 {
783     OM_uint32 major;
784
785     major = gssEapGlueToMechName(minor, ctx, initiator, &ctx->initiatorName);
786     if (GSS_ERROR(major))
787         return major;
788
789     if (cred->name != GSS_C_NO_NAME) {
790         major = gssEapDuplicateName(minor, cred->name, &ctx->acceptorName);
791         if (GSS_ERROR(major))
792             return major;
793     }
794
795     major = gssEapReauthComplete(minor, ctx, cred, mech, timeRec);
796     if (GSS_ERROR(major))
797         return major;
798
799     ctx->state = GSSEAP_STATE_ESTABLISHED;
800
801     *minor = 0;
802     return GSS_S_COMPLETE;
803 }
804
805 static OM_uint32
806 eapGssSmAcceptGssReauth(OM_uint32 *minor,
807                         gss_ctx_id_t ctx,
808                         gss_cred_id_t cred,
809                         gss_buffer_t inputToken,
810                         gss_channel_bindings_t chanBindings,
811                         gss_buffer_t outputToken)
812 {
813     OM_uint32 major, tmpMinor;
814     gss_name_t krbInitiator = GSS_C_NO_NAME;
815     gss_OID mech = GSS_C_NO_OID;
816     OM_uint32 gssFlags, timeRec = GSS_C_INDEFINITE;
817
818     ctx->flags |= CTX_FLAG_KRB_REAUTH;
819
820     major = gssAcceptSecContext(minor,
821                                 &ctx->kerberosCtx,
822                                 cred->krbCred,
823                                 inputToken,
824                                 chanBindings,
825                                 &krbInitiator,
826                                 &mech,
827                                 outputToken,
828                                 &gssFlags,
829                                 &timeRec,
830                                 NULL);
831     if (major == GSS_S_COMPLETE) {
832         major = acceptReadyKrb(minor, ctx, cred,
833                                krbInitiator, mech, timeRec);
834     }
835
836     ctx->gssFlags = gssFlags;
837
838     gssReleaseName(&tmpMinor, &krbInitiator);
839
840     return major;
841 }
842 #endif /* GSSEAP_ENABLE_REAUTH */