b977242b81855ff75455c79985a8674925494997
[mech_eap.orig] / accept_sec_context.c
1 /*
2  * Copyright (c) 2010, 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 #include "gssapiP_eap.h"
34
35 #ifdef GSSEAP_ENABLE_REAUTH
36 static OM_uint32
37 eapGssSmAcceptGssReauth(OM_uint32 *minor,
38                         gss_ctx_id_t ctx,
39                         gss_cred_id_t cred,
40                         gss_buffer_t inputToken,
41                         gss_channel_bindings_t chanBindings,
42                         gss_buffer_t outputToken);
43 #endif
44
45 /*
46  * Mark a context as ready for cryptographic operations
47  */
48 static OM_uint32
49 acceptReadyEap(OM_uint32 *minor, gss_ctx_id_t ctx, gss_cred_id_t cred)
50 {
51     OM_uint32 major, tmpMinor;
52     VALUE_PAIR *vp;
53     gss_buffer_desc nameBuf = GSS_C_EMPTY_BUFFER;
54
55     /* Cache encryption type derived from selected mechanism OID */
56     major = gssEapOidToEnctype(minor, ctx->mechanismUsed,
57                                &ctx->encryptionType);
58     if (GSS_ERROR(major))
59         return major;
60
61     gssEapReleaseName(&tmpMinor, &ctx->initiatorName);
62
63     vp = rc_avpair_get(ctx->acceptorCtx.avps, PW_USER_NAME, 0);
64     if (vp != NULL) {
65         nameBuf.length = vp->lvalue;
66         nameBuf.value = vp->strvalue;
67     } else {
68         ctx->gssFlags |= GSS_C_ANON_FLAG;
69     }
70
71     major = gssEapImportName(minor, &nameBuf, GSS_C_NT_USER_NAME,
72                              &ctx->initiatorName);
73     if (GSS_ERROR(major))
74         return major;
75
76     ctx->initiatorName->attrCtx = gssEapCreateAttrContext(cred, ctx);
77
78     vp = rc_avpair_get(ctx->acceptorCtx.avps,
79                        VENDOR_ATTR_MS_MPPE_SEND_KEY,
80                        VENDOR_ID_MICROSOFT);
81     if (ctx->encryptionType != ENCTYPE_NULL && vp != NULL) {
82         major = gssEapDeriveRfc3961Key(minor,
83                                        (unsigned char *)vp->strvalue,
84                                        vp->lvalue,
85                                        ctx->encryptionType,
86                                        &ctx->rfc3961Key);
87         if (GSS_ERROR(major))
88             return major;
89
90         major = rfc3961ChecksumTypeForKey(minor, &ctx->rfc3961Key,
91                                            &ctx->checksumType);
92         if (GSS_ERROR(major))
93             return major;
94     } else {
95         /*
96          * draft-howlett-eap-gss says that integrity/confidentialty should
97          * always be advertised as available, but if we have no keying
98          * material it seems confusing to the caller to advertise this.
99          */
100         ctx->gssFlags &= ~(GSS_C_INTEG_FLAG | GSS_C_CONF_FLAG);
101         ctx->encryptionType = ENCTYPE_NULL;
102     }
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     return GSS_S_COMPLETE;
113 }
114
115 static OM_uint32
116 eapGssSmAcceptIdentity(OM_uint32 *minor,
117                        gss_ctx_id_t ctx,
118                        gss_cred_id_t cred,
119                        gss_buffer_t inputToken,
120                        gss_channel_bindings_t chanBindings,
121                        gss_buffer_t outputToken)
122 {
123     OM_uint32 major;
124     union {
125         struct eap_hdr pdu;
126         unsigned char data[5];
127     } pkt;
128     gss_buffer_desc pktBuffer;
129
130     if (inputToken != GSS_C_NO_BUFFER && inputToken->length != 0)
131         return GSS_S_DEFECTIVE_TOKEN;
132
133     assert(ctx->acceptorName == GSS_C_NO_NAME);
134
135     if (cred != GSS_C_NO_CREDENTIAL && cred->name != GSS_C_NO_NAME) {
136         major = gssEapDuplicateName(minor, cred->name, &ctx->acceptorName);
137         if (GSS_ERROR(major))
138             return major;
139     }
140
141     pkt.pdu.code = EAP_CODE_REQUEST;
142     pkt.pdu.identifier = 0;
143     pkt.pdu.length = htons(sizeof(pkt.data));
144     pkt.data[4] = EAP_TYPE_IDENTITY;
145
146     pktBuffer.length = sizeof(pkt.data);
147     pktBuffer.value = pkt.data;
148
149     major = duplicateBuffer(minor, &pktBuffer, outputToken);
150     if (GSS_ERROR(major))
151         return major;
152
153     ctx->state = EAP_STATE_AUTHENTICATE;
154
155     return GSS_S_CONTINUE_NEEDED;
156 }
157
158 static OM_uint32
159 setAcceptorIdentity(OM_uint32 *minor,
160                     gss_ctx_id_t ctx,
161                     VALUE_PAIR **avps)
162 {
163     OM_uint32 major;
164     gss_buffer_desc nameBuf;
165     krb5_context krbContext = NULL;
166     krb5_principal krbPrinc;
167     rc_handle *rh = ctx->acceptorCtx.radHandle;
168
169     assert(rh != NULL);
170
171     /* Awaits further specification */
172     if (ctx->acceptorName == GSS_C_NO_NAME)
173         return GSS_S_COMPLETE;
174
175     GSSEAP_KRB_INIT(&krbContext);
176
177     krbPrinc = ctx->acceptorName->krbPrincipal;
178     assert(krbPrinc != NULL);
179
180     if (krb5_princ_size(krbContext, krbPrinc) < 2)
181         return GSS_S_BAD_NAME;
182
183     /* Acceptor-Service-Name */
184     krbDataToGssBuffer(krb5_princ_component(krbContext, krbPrinc, 0), &nameBuf);
185
186     major = addAvpFromBuffer(minor, rh, avps,
187                              VENDOR_ATTR_GSS_ACCEPTOR_SERVICE_NAME,
188                              VENDOR_ID_UKERNA,
189                              &nameBuf);
190     if (GSS_ERROR(major))
191         return major;
192
193     /* Acceptor-Host-Name */
194     krbDataToGssBuffer(krb5_princ_component(krbContext, krbPrinc, 1), &nameBuf);
195
196     major = addAvpFromBuffer(minor, rh, avps,
197                              VENDOR_ATTR_GSS_ACCEPTOR_HOST_NAME,
198                              VENDOR_ID_UKERNA,
199                              &nameBuf);
200     if (GSS_ERROR(major))
201         return major;
202
203     if (krb5_princ_size(krbContext, krbPrinc) > 2) {
204         /* Acceptor-Service-Specific */
205         krb5_principal_data ssiPrinc = *krbPrinc;
206         char *ssi;
207
208         krb5_princ_size(krbContext, &ssiPrinc) -= 2;
209         krb5_princ_name(krbContext, &ssiPrinc) += 2;
210
211         *minor = krb5_unparse_name_flags(krbContext, &ssiPrinc,
212                                          KRB5_PRINCIPAL_UNPARSE_NO_REALM, &ssi);
213         if (*minor != 0)
214             return GSS_S_FAILURE;
215
216         nameBuf.value = ssi;
217         nameBuf.length = strlen(ssi);
218
219         major = addAvpFromBuffer(minor, rh, avps,
220                                  VENDOR_ATTR_GSS_ACCEPTOR_SERVICE_SPECIFIC,
221                                  VENDOR_ID_UKERNA,
222                                  &nameBuf);
223
224         if (GSS_ERROR(major)) {
225             krb5_free_unparsed_name(krbContext, ssi);
226             return major;
227         }
228         krb5_free_unparsed_name(krbContext, ssi);
229     }
230
231     krbDataToGssBuffer(krb5_princ_realm(krbContext, krbPrinc), &nameBuf);
232     if (nameBuf.length != 0) {
233         /* Acceptor-Realm-Name */
234         major = addAvpFromBuffer(minor, rh, avps,
235                                  VENDOR_ATTR_GSS_ACCEPTOR_REALM_NAME,
236                                  VENDOR_ID_UKERNA,
237                                  &nameBuf);
238         if (GSS_ERROR(major))
239             return major;
240     }
241
242     return GSS_S_COMPLETE;
243 }
244
245 static OM_uint32
246 eapGssSmAcceptAuthenticate(OM_uint32 *minor,
247                            gss_ctx_id_t ctx,
248                            gss_cred_id_t cred,
249                            gss_buffer_t inputToken,
250                            gss_channel_bindings_t chanBindings,
251                            gss_buffer_t outputToken)
252 {
253     OM_uint32 major, tmpMinor;
254     rc_handle *rh;
255     int code;
256     VALUE_PAIR *send = NULL;
257     VALUE_PAIR *received = NULL;
258     char msgBuffer[4096];
259     struct eap_hdr *pdu;
260     unsigned char *pos;
261     gss_buffer_desc nameBuf = GSS_C_EMPTY_BUFFER;
262
263     if (ctx->acceptorCtx.radHandle == NULL) {
264         /* May be NULL from an imported partial context */
265         major = gssEapRadiusAllocHandle(minor, cred, ctx);
266         if (GSS_ERROR(major))
267             goto cleanup;
268     }
269
270     rh = ctx->acceptorCtx.radHandle;
271
272     pdu = (struct eap_hdr *)inputToken->value;
273     pos = (unsigned char *)(pdu + 1);
274
275     if (inputToken->length > sizeof(*pdu) &&
276         pdu->code == EAP_CODE_RESPONSE &&
277         pos[0] == EAP_TYPE_IDENTITY) {
278         /*
279          * XXX TODO do we really need to set User-Name? FreeRADIUS does
280          * not require it but some other RADIUS servers might.
281          */
282         major = addAvpFromBuffer(minor, rh, &send, PW_USER_NAME, 0, &nameBuf);
283         if (GSS_ERROR(major))
284             goto cleanup;
285
286         major = setAcceptorIdentity(minor, ctx, &send);
287         if (GSS_ERROR(major))
288             goto cleanup;
289     }
290
291     major = addAvpFromBuffer(minor, rh, &send, PW_EAP_MESSAGE, 0, inputToken);
292     if (GSS_ERROR(major))
293         goto cleanup;
294
295     if (ctx->acceptorCtx.state.length != 0) {
296         major = addAvpFromBuffer(minor, rh, &send, PW_STATE, 0,
297                                  &ctx->acceptorCtx.state);
298         if (GSS_ERROR(major))
299             goto cleanup;
300
301         gss_release_buffer(&tmpMinor, &ctx->acceptorCtx.state);
302     }
303
304     code = rc_auth(rh, 0, send, &received, msgBuffer);
305     switch (code) {
306     case OK_RC:
307     case CHALLENGE_RC:
308         major = GSS_S_CONTINUE_NEEDED;
309         break;
310     case TIMEOUT_RC:
311         major = GSS_S_UNAVAILABLE;
312         break;
313     case REJECT_RC:
314         major = GSS_S_DEFECTIVE_CREDENTIAL;
315         break;
316     default:
317         major = GSS_S_FAILURE;
318         goto cleanup;
319     }
320
321     if (GSS_ERROR(major))
322         goto cleanup;
323
324     major = getBufferFromAvps(minor, received, PW_EAP_MESSAGE, 0,
325                               outputToken, TRUE);
326     if ((major == GSS_S_UNAVAILABLE && code != OK_RC) ||
327         GSS_ERROR(major))
328         goto cleanup;
329
330     if (code == CHALLENGE_RC) {
331         major = getBufferFromAvps(minor, received, PW_STATE, 0,
332                                   &ctx->acceptorCtx.state, TRUE);
333         if (major != GSS_S_UNAVAILABLE && GSS_ERROR(major))
334             goto cleanup;
335     } else {
336         ctx->acceptorCtx.avps = received;
337         received = NULL;
338
339         major = acceptReadyEap(minor, ctx, cred);
340         if (GSS_ERROR(major))
341             goto cleanup;
342
343         ctx->state = EAP_STATE_EXTENSIONS_REQ;
344     }
345
346     major = GSS_S_CONTINUE_NEEDED;
347
348 cleanup:
349     if (received != NULL)
350         rc_avpair_free(received);
351
352     return major;
353 }
354
355 static OM_uint32
356 acceptGssChannelBindings(OM_uint32 *minor,
357                          gss_ctx_id_t ctx,
358                          gss_cred_id_t cred,
359                          gss_buffer_t inputToken,
360                          gss_channel_bindings_t chanBindings)
361 {
362     OM_uint32 major, tmpMinor;
363     gss_iov_buffer_desc iov[2];
364
365     iov[0].type = GSS_IOV_BUFFER_TYPE_DATA | GSS_IOV_BUFFER_FLAG_ALLOCATE;
366     iov[0].buffer.length = 0;
367     iov[0].buffer.value = NULL;
368
369     iov[1].type = GSS_IOV_BUFFER_TYPE_STREAM;
370     iov[1].buffer = *inputToken;
371
372     major = gssEapUnwrapOrVerifyMIC(minor, ctx, NULL, NULL,
373                                     iov, 2, TOK_TYPE_WRAP);
374     if (GSS_ERROR(major))
375         return major;
376
377     if (chanBindings != GSS_C_NO_CHANNEL_BINDINGS &&
378         !bufferEqual(&iov[0].buffer, &chanBindings->application_data)) {
379         major = GSS_S_BAD_BINDINGS;
380     } else {
381         major = GSS_S_CONTINUE_NEEDED;
382     }
383
384     gss_release_buffer(&tmpMinor, &iov[0].buffer);
385
386     return major;
387 }
388
389 static OM_uint32
390 eapGssSmAcceptExtensionsReq(OM_uint32 *minor,
391                             gss_ctx_id_t ctx,
392                             gss_cred_id_t cred,
393                             gss_buffer_t inputToken,
394                             gss_channel_bindings_t chanBindings,
395                             gss_buffer_t outputToken)
396 {
397     OM_uint32 major;
398
399     outputToken->length = 0;
400     outputToken->value = NULL;
401
402     major = acceptGssChannelBindings(minor, ctx, cred, inputToken,
403                                      chanBindings);
404     if (GSS_ERROR(major))
405         return major;
406
407     ctx->state = EAP_STATE_EXTENSIONS_RESP;
408
409     return GSS_S_CONTINUE_NEEDED;
410 }
411
412 static OM_uint32
413 eapGssSmAcceptExtensionsResp(OM_uint32 *minor,
414                              gss_ctx_id_t ctx,
415                              gss_cred_id_t cred,
416                              gss_buffer_t inputToken,
417                              gss_channel_bindings_t chanBindings,
418                              gss_buffer_t outputToken)
419 {
420     OM_uint32 major, tmpMinor;
421     gss_buffer_desc credsToken = GSS_C_EMPTY_BUFFER;
422
423 #ifdef GSSEAP_ENABLE_REAUTH
424     /*
425      * If we're built with fast reauthentication enabled, then
426      * fabricate a ticket from the initiator to ourselves.
427      * Otherwise return an empty token.
428      */
429     major = gssEapMakeReauthCreds(minor, ctx, cred, &credsToken);
430     if (GSS_ERROR(major))
431         return major;
432 #else
433     credsToken.value = "";
434 #endif /* GSSEAP_ENABLE_REAUTH */
435
436     major = duplicateBuffer(minor, &credsToken, outputToken);
437     if (GSS_ERROR(major)) {
438         gss_release_buffer(&tmpMinor, &credsToken);
439         return major;
440     }
441
442 #ifdef GSSEAP_ENABLE_REAUTH
443     gss_release_buffer(&tmpMinor, &credsToken);
444 #endif
445
446     ctx->state = EAP_STATE_ESTABLISHED;
447
448     return GSS_S_COMPLETE;
449 }
450
451 static OM_uint32
452 eapGssSmAcceptEstablished(OM_uint32 *minor,
453                           gss_ctx_id_t ctx,
454                           gss_cred_id_t cred,
455                           gss_buffer_t inputToken,
456                           gss_channel_bindings_t chanBindings,
457                           gss_buffer_t outputToken)
458 {
459     /* Called with already established context */
460     *minor = EINVAL;
461     return GSS_S_BAD_STATUS;
462 }
463
464 static struct gss_eap_acceptor_sm {
465     enum gss_eap_token_type inputTokenType;
466     enum gss_eap_token_type outputTokenType;
467     OM_uint32 (*processToken)(OM_uint32 *,
468                               gss_ctx_id_t,
469                               gss_cred_id_t,
470                               gss_buffer_t,
471                               gss_channel_bindings_t,
472                               gss_buffer_t);
473 } eapGssAcceptorSm[] = {
474     { TOK_TYPE_EAP_RESP,    TOK_TYPE_EAP_REQ,    eapGssSmAcceptIdentity           },
475     { TOK_TYPE_EAP_RESP,    TOK_TYPE_EAP_REQ,    eapGssSmAcceptAuthenticate       },
476     { TOK_TYPE_EXT_REQ,     TOK_TYPE_NONE,       eapGssSmAcceptExtensionsReq      },
477     { TOK_TYPE_NONE,        TOK_TYPE_EXT_RESP,   eapGssSmAcceptExtensionsResp     },
478     { TOK_TYPE_NONE,        TOK_TYPE_NONE,       eapGssSmAcceptEstablished        },
479 #ifdef GSSEAP_ENABLE_REAUTH
480     { TOK_TYPE_GSS_REAUTH,  TOK_TYPE_GSS_REAUTH, eapGssSmAcceptGssReauth          },
481 #endif
482 };
483
484 OM_uint32
485 gss_accept_sec_context(OM_uint32 *minor,
486                        gss_ctx_id_t *context_handle,
487                        gss_cred_id_t cred,
488                        gss_buffer_t input_token,
489                        gss_channel_bindings_t input_chan_bindings,
490                        gss_name_t *src_name,
491                        gss_OID *mech_type,
492                        gss_buffer_t output_token,
493                        OM_uint32 *ret_flags,
494                        OM_uint32 *time_rec,
495                        gss_cred_id_t *delegated_cred_handle)
496 {
497     OM_uint32 major;
498     OM_uint32 tmpMajor, tmpMinor;
499     gss_ctx_id_t ctx = *context_handle;
500     struct gss_eap_acceptor_sm *sm = NULL;
501     gss_buffer_desc innerInputToken = GSS_C_EMPTY_BUFFER;
502     gss_buffer_desc innerOutputToken = GSS_C_EMPTY_BUFFER;
503     enum gss_eap_token_type tokType;
504     int initialContextToken = 0;
505
506     *minor = 0;
507
508     output_token->length = 0;
509     output_token->value = NULL;
510
511     if (input_token == GSS_C_NO_BUFFER || input_token->length == 0) {
512         return GSS_S_DEFECTIVE_TOKEN;
513     }
514
515     if (ctx == GSS_C_NO_CONTEXT) {
516         major = gssEapAllocContext(minor, &ctx);
517         if (GSS_ERROR(major))
518             return major;
519
520         initialContextToken = 1;
521         *context_handle = ctx;
522     }
523
524     GSSEAP_MUTEX_LOCK(&ctx->mutex);
525
526     /* Validate and lock credentials */
527     if (cred != GSS_C_NO_CREDENTIAL) {
528         GSSEAP_MUTEX_LOCK(&cred->mutex);
529
530         if ((cred->flags & CRED_FLAG_ACCEPT) == 0) {
531             major = GSS_S_NO_CRED;
532             goto cleanup;
533         }
534     }
535
536     sm = &eapGssAcceptorSm[ctx->state];
537
538     major = gssEapVerifyToken(minor, ctx, input_token,
539                               &tokType, &innerInputToken);
540     if (GSS_ERROR(major))
541         goto cleanup;
542
543     if (!gssEapCredAvailable(cred, ctx->mechanismUsed)) {
544         major = GSS_S_BAD_MECH;
545         goto cleanup;
546     }
547
548 #ifdef GSSEAP_ENABLE_REAUTH
549     /*
550      * If we're built with fast reauthentication support, it's valid
551      * for an initiator to send a GSS reauthentication token as its
552      * initial context token, causing us to short-circuit the state
553      * machine and process Kerberos GSS messages instead.
554      */
555     if (tokType == TOK_TYPE_GSS_REAUTH && initialContextToken) {
556         ctx->state = EAP_STATE_KRB_REAUTH_GSS;
557     } else
558 #endif
559     if (tokType != sm->inputTokenType) {
560         major = GSS_S_DEFECTIVE_TOKEN;
561         goto cleanup;
562     }
563
564     do {
565         sm = &eapGssAcceptorSm[ctx->state];
566
567         major = (sm->processToken)(minor,
568                                    ctx,
569                                    cred,
570                                    &innerInputToken,
571                                    input_chan_bindings,
572                                    &innerOutputToken);
573         if (GSS_ERROR(major))
574             goto cleanup;
575     } while (major == GSS_S_CONTINUE_NEEDED && innerOutputToken.length == 0);
576
577     if (mech_type != NULL) {
578         if (!gssEapInternalizeOid(ctx->mechanismUsed, mech_type))
579             duplicateOid(&tmpMinor, ctx->mechanismUsed, mech_type);
580     }
581     if (innerOutputToken.value != NULL) {
582         tmpMajor = gssEapMakeToken(&tmpMinor, ctx, &innerOutputToken,
583                                    sm->outputTokenType, output_token);
584         if (GSS_ERROR(tmpMajor)) {
585             major = tmpMajor;
586             *minor = tmpMinor;
587             goto cleanup;
588         }
589     }
590     if (ret_flags != NULL)
591         *ret_flags = ctx->gssFlags;
592     if (delegated_cred_handle != NULL)
593         *delegated_cred_handle = GSS_C_NO_CREDENTIAL;
594
595     if (major == GSS_S_COMPLETE) {
596         if (src_name != NULL && ctx->initiatorName != GSS_C_NO_NAME) {
597             major = gssEapDuplicateName(&tmpMinor, ctx->initiatorName, src_name);
598             if (GSS_ERROR(major))
599                 goto cleanup;
600         }
601         if (time_rec != NULL)
602             gssEapContextTime(&tmpMinor, ctx, time_rec);
603     }
604
605     assert(ctx->state == EAP_STATE_ESTABLISHED || major == GSS_S_CONTINUE_NEEDED);
606
607 cleanup:
608     if (cred != GSS_C_NO_CREDENTIAL)
609         GSSEAP_MUTEX_UNLOCK(&cred->mutex);
610     GSSEAP_MUTEX_UNLOCK(&ctx->mutex);
611
612     if (GSS_ERROR(major))
613         gssEapReleaseContext(&tmpMinor, context_handle);
614
615     gss_release_buffer(&tmpMinor, &innerOutputToken);
616
617     return major;
618 }
619
620 #ifdef GSSEAP_ENABLE_REAUTH
621 static OM_uint32
622 acceptReadyKrb(OM_uint32 *minor,
623                gss_ctx_id_t ctx,
624                gss_cred_id_t cred,
625                const gss_name_t initiator,
626                const gss_OID mech,
627                OM_uint32 timeRec)
628 {
629     OM_uint32 major;
630
631     major = gssEapGlueToMechName(minor, initiator, &ctx->initiatorName);
632     if (GSS_ERROR(major))
633         return major;
634
635     if (cred != GSS_C_NO_CREDENTIAL && cred->name != GSS_C_NO_NAME) {
636         major = gssEapDuplicateName(minor, cred->name, &ctx->acceptorName);
637         if (GSS_ERROR(major))
638             return major;
639     }
640
641     major = gssEapReauthComplete(minor, ctx, cred, mech, timeRec);
642     if (GSS_ERROR(major))
643         return major;
644
645     ctx->state = EAP_STATE_ESTABLISHED;
646
647     return GSS_S_COMPLETE;
648 }
649
650 static OM_uint32
651 eapGssSmAcceptGssReauth(OM_uint32 *minor,
652                         gss_ctx_id_t ctx,
653                         gss_cred_id_t cred,
654                         gss_buffer_t inputToken,
655                         gss_channel_bindings_t chanBindings,
656                         gss_buffer_t outputToken)
657 {
658     OM_uint32 major, tmpMinor;
659     gss_cred_id_t krbCred = GSS_C_NO_CREDENTIAL;
660     gss_name_t krbInitiator = GSS_C_NO_NAME;
661     gss_OID mech = GSS_C_NO_OID;
662     OM_uint32 gssFlags, timeRec = GSS_C_INDEFINITE;
663
664     ctx->flags |= CTX_FLAG_KRB_REAUTH_GSS;
665
666     if (cred != GSS_C_NO_CREDENTIAL)
667         krbCred = cred->krbCred;
668
669     major = gssAcceptSecContext(minor,
670                                 &ctx->kerberosCtx,
671                                 krbCred,
672                                 inputToken,
673                                 chanBindings,
674                                 &krbInitiator,
675                                 &mech,
676                                 outputToken,
677                                 &gssFlags,
678                                 &timeRec,
679                                 NULL);
680     if (major == GSS_S_COMPLETE) {
681         major = acceptReadyKrb(minor, ctx, cred,
682                                krbInitiator, mech, timeRec);
683     }
684
685     ctx->gssFlags = gssFlags;
686
687     gssReleaseName(&tmpMinor, &krbInitiator);
688
689     return major;
690 }
691 #endif /* GSSEAP_ENABLE_REAUTH */