Merge branch 'master' of ssh://moonshot.suchdamage.org:822/srv/git/moonshot
[mech_eap.git] / init_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 /*
34  * Establish a security context on the initiator (client). These functions
35  * wrap around libeap.
36  */
37
38 #include "gssapiP_eap.h"
39
40 #ifdef GSSEAP_ENABLE_REAUTH
41 static OM_uint32
42 eapGssSmInitGssReauth(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 #endif
53
54 static OM_uint32
55 policyVariableToFlag(enum eapol_bool_var variable)
56 {
57     OM_uint32 flag = 0;
58
59     switch (variable) {
60     case EAPOL_eapSuccess:
61         flag = CTX_FLAG_EAP_SUCCESS;
62         break;
63     case EAPOL_eapRestart:
64         flag = CTX_FLAG_EAP_RESTART;
65         break;
66     case EAPOL_eapFail:
67         flag = CTX_FLAG_EAP_FAIL;
68         break;
69     case EAPOL_eapResp:
70         flag = CTX_FLAG_EAP_RESP;
71         break;
72     case EAPOL_eapNoResp:
73         flag = CTX_FLAG_EAP_NO_RESP;
74         break;
75     case EAPOL_eapReq:
76         flag = CTX_FLAG_EAP_REQ;
77         break;
78     case EAPOL_portEnabled:
79         flag = CTX_FLAG_EAP_PORT_ENABLED;
80         break;
81     case EAPOL_altAccept:
82         flag = CTX_FLAG_EAP_ALT_ACCEPT;
83         break;
84     case EAPOL_altReject:
85         flag = CTX_FLAG_EAP_ALT_REJECT;
86         break;
87     }
88
89     return flag;
90 }
91
92 static struct eap_peer_config *
93 peerGetConfig(void *ctx)
94 {
95     gss_ctx_id_t gssCtx = (gss_ctx_id_t)ctx;
96
97     return &gssCtx->initiatorCtx.eapPeerConfig;
98 }
99
100 static Boolean
101 peerGetBool(void *data, enum eapol_bool_var variable)
102 {
103     gss_ctx_id_t ctx = data;
104     OM_uint32 flag;
105
106     if (ctx == GSS_C_NO_CONTEXT)
107         return FALSE;
108
109     flag = policyVariableToFlag(variable);
110
111     return ((ctx->flags & flag) != 0);
112 }
113
114 static void
115 peerSetBool(void *data, enum eapol_bool_var variable,
116             Boolean value)
117 {
118     gss_ctx_id_t ctx = data;
119     OM_uint32 flag;
120
121     if (ctx == GSS_C_NO_CONTEXT)
122         return;
123
124     flag = policyVariableToFlag(variable);
125
126     if (value)
127         ctx->flags |= flag;
128     else
129         ctx->flags &= ~(flag);
130 }
131
132 static unsigned int
133 peerGetInt(void *data, enum eapol_int_var variable)
134 {
135     gss_ctx_id_t ctx = data;
136
137     if (ctx == GSS_C_NO_CONTEXT)
138         return FALSE;
139
140     assert(CTX_IS_INITIATOR(ctx));
141
142     switch (variable) {
143     case EAPOL_idleWhile:
144         return ctx->initiatorCtx.idleWhile;
145         break;
146     }
147
148     return 0;
149 }
150
151 static void
152 peerSetInt(void *data, enum eapol_int_var variable,
153            unsigned int value)
154 {
155     gss_ctx_id_t ctx = data;
156
157     if (ctx == GSS_C_NO_CONTEXT)
158         return;
159
160     assert(CTX_IS_INITIATOR(ctx));
161
162     switch (variable) {
163     case EAPOL_idleWhile:
164         ctx->initiatorCtx.idleWhile = value;
165         break;
166     }
167 }
168
169 static struct wpabuf *
170 peerGetEapReqData(void *ctx)
171 {
172     gss_ctx_id_t gssCtx = (gss_ctx_id_t)ctx;
173
174     return &gssCtx->initiatorCtx.reqData;
175 }
176
177 static void
178 peerSetConfigBlob(void *ctx, struct wpa_config_blob *blob)
179 {
180 }
181
182 static const struct wpa_config_blob *
183 peerGetConfigBlob(void *ctx, const char *name)
184 {
185     return NULL;
186 }
187
188 static void
189 peerNotifyPending(void *ctx)
190 {
191 }
192
193 static struct eapol_callbacks gssEapPolicyCallbacks = {
194     peerGetConfig,
195     peerGetBool,
196     peerSetBool,
197     peerGetInt,
198     peerSetInt,
199     peerGetEapReqData,
200     peerSetConfigBlob,
201     peerGetConfigBlob,
202     peerNotifyPending,
203 };
204
205 extern int wpa_debug_level;
206
207 static OM_uint32
208 peerConfigInit(OM_uint32 *minor,
209                gss_cred_id_t cred,
210                gss_ctx_id_t ctx)
211 {
212     krb5_context krbContext;
213     struct eap_peer_config *eapPeerConfig = &ctx->initiatorCtx.eapPeerConfig;
214     krb5_error_code code;
215     char *identity;
216
217     eapPeerConfig->identity = NULL;
218     eapPeerConfig->identity_len = 0;
219     eapPeerConfig->password = NULL;
220     eapPeerConfig->password_len = 0;
221
222     assert(cred != GSS_C_NO_CREDENTIAL);
223
224     GSSEAP_KRB_INIT(&krbContext);
225
226     eapPeerConfig->fragment_size = 1024;
227     wpa_debug_level = 0;
228
229     assert(cred->name != GSS_C_NO_NAME);
230
231     if ((cred->name->flags & (NAME_FLAG_NAI | NAME_FLAG_SERVICE)) == 0) {
232         *minor = GSSEAP_BAD_INITIATOR_NAME;
233         return GSS_S_BAD_NAME;
234     }
235
236     code = krb5_unparse_name(krbContext, cred->name->krbPrincipal, &identity);
237     if (code != 0) {
238         *minor = code;
239         return GSS_S_FAILURE;
240     }
241
242     eapPeerConfig->identity = (unsigned char *)identity;
243     eapPeerConfig->identity_len = strlen(identity);
244     eapPeerConfig->password = (unsigned char *)cred->password.value;
245     eapPeerConfig->password_len = cred->password.length;
246
247     *minor = 0;
248     return GSS_S_COMPLETE;
249 }
250
251 static OM_uint32
252 peerConfigFree(OM_uint32 *minor,
253                gss_ctx_id_t ctx)
254 {
255     krb5_context krbContext;
256     struct eap_peer_config *eapPeerConfig = &ctx->initiatorCtx.eapPeerConfig;
257
258     GSSEAP_KRB_INIT(&krbContext);
259
260     krb5_free_unparsed_name(krbContext, (char *)eapPeerConfig->identity);
261
262     *minor = 0;
263     return GSS_S_COMPLETE;
264 }
265
266 /*
267  * Mark an initiator context as ready for cryptographic operations
268  */
269 static OM_uint32
270 initReady(OM_uint32 *minor, gss_ctx_id_t ctx, OM_uint32 reqFlags)
271 {
272     OM_uint32 major;
273     const unsigned char *key;
274     size_t keyLength;
275
276 #if 1
277     /* XXX actually check for mutual auth */
278     if (reqFlags & GSS_C_MUTUAL_FLAG)
279         ctx->gssFlags |= GSS_C_MUTUAL_FLAG;
280 #endif
281
282     /* Cache encryption type derived from selected mechanism OID */
283     major = gssEapOidToEnctype(minor, ctx->mechanismUsed, &ctx->encryptionType);
284     if (GSS_ERROR(major))
285         return major;
286
287     if (!eap_key_available(ctx->initiatorCtx.eap)) {
288         *minor = GSSEAP_KEY_UNAVAILABLE;
289         return GSS_S_UNAVAILABLE;
290     }
291
292     key = eap_get_eapKeyData(ctx->initiatorCtx.eap, &keyLength);
293
294     if (keyLength < EAP_EMSK_LEN) {
295         *minor = GSSEAP_KEY_TOO_SHORT;
296         return GSS_S_UNAVAILABLE;
297     }
298
299     major = gssEapDeriveRfc3961Key(minor,
300                                    &key[EAP_EMSK_LEN / 2],
301                                    EAP_EMSK_LEN / 2,
302                                    ctx->encryptionType,
303                                    &ctx->rfc3961Key);
304        if (GSS_ERROR(major))
305            return major;
306
307     major = rfc3961ChecksumTypeForKey(minor, &ctx->rfc3961Key,
308                                       &ctx->checksumType);
309     if (GSS_ERROR(major))
310         return major;
311
312     major = sequenceInit(minor,
313                          &ctx->seqState,
314                          ctx->recvSeq,
315                          ((ctx->gssFlags & GSS_C_REPLAY_FLAG) != 0),
316                          ((ctx->gssFlags & GSS_C_SEQUENCE_FLAG) != 0),
317                          TRUE);
318     if (GSS_ERROR(major))
319         return major;
320
321     *minor = 0;
322     return GSS_S_COMPLETE;
323 }
324
325 static OM_uint32
326 initBegin(OM_uint32 *minor,
327           gss_cred_id_t cred,
328           gss_ctx_id_t ctx,
329           gss_name_t target,
330           gss_OID mech,
331           OM_uint32 reqFlags,
332           OM_uint32 timeReq,
333           gss_channel_bindings_t chanBindings,
334           gss_buffer_t inputToken,
335           gss_buffer_t outputToken)
336 {
337     OM_uint32 major;
338
339     assert(cred != GSS_C_NO_CREDENTIAL);
340
341     if (cred->expiryTime)
342         ctx->expiryTime = cred->expiryTime;
343     else if (timeReq == 0 || timeReq == GSS_C_INDEFINITE)
344         ctx->expiryTime = 0;
345     else
346         ctx->expiryTime = time(NULL) + timeReq;
347
348     /*
349      * The credential mutex protects its name, however we need to
350      * explicitly lock the acceptor name (unlikely as it may be
351      * that it has attributes set on it).
352      */
353     major = gssEapDuplicateName(minor, cred->name, &ctx->initiatorName);
354     if (GSS_ERROR(major))
355         return major;
356
357     GSSEAP_MUTEX_LOCK(&target->mutex);
358
359     major = gssEapDuplicateName(minor, target, &ctx->acceptorName);
360     if (GSS_ERROR(major)) {
361         GSSEAP_MUTEX_UNLOCK(&target->mutex);
362         return major;
363     }
364
365     GSSEAP_MUTEX_UNLOCK(&target->mutex);
366
367     if (mech == GSS_C_NULL_OID) {
368         major = gssEapDefaultMech(minor, &ctx->mechanismUsed);
369     } else if (gssEapIsConcreteMechanismOid(mech)) {
370         if (!gssEapInternalizeOid(mech, &ctx->mechanismUsed))
371             major = duplicateOid(minor, mech, &ctx->mechanismUsed);
372     } else {
373         major = GSS_S_BAD_MECH;
374         *minor = GSSEAP_WRONG_MECH;
375     }
376     if (GSS_ERROR(major))
377         return major;
378
379     /* If credentials were provided, check they're usable with this mech */
380     if (!gssEapCredAvailable(cred, ctx->mechanismUsed)) {
381         *minor = GSSEAP_CRED_MECH_MISMATCH;
382         return GSS_S_BAD_MECH;
383     }
384
385     *minor = 0;
386     return GSS_S_COMPLETE;
387 }
388
389 static OM_uint32
390 eapGssSmInitIdentity(OM_uint32 *minor,
391                      gss_cred_id_t cred,
392                      gss_ctx_id_t ctx,
393                      gss_name_t target,
394                      gss_OID mech,
395                      OM_uint32 reqFlags,
396                      OM_uint32 timeReq,
397                      gss_channel_bindings_t chanBindings,
398                      gss_buffer_t inputToken,
399                      gss_buffer_t outputToken)
400 {
401     OM_uint32 major;
402     int initialContextToken;
403
404     initialContextToken = (inputToken->length == 0);
405     if (!initialContextToken) {
406         *minor = GSSEAP_WRONG_SIZE;
407         return GSS_S_DEFECTIVE_TOKEN;
408     }
409
410     major = initBegin(minor, cred, ctx, target, mech,
411                       reqFlags, timeReq, chanBindings,
412                       inputToken, outputToken);
413     if (GSS_ERROR(major))
414         return major;
415
416     ctx->state = EAP_STATE_AUTHENTICATE;
417
418     *minor = 0;
419     return GSS_S_CONTINUE_NEEDED;
420 }
421
422 static struct wpabuf emptyWpaBuffer;
423
424 static OM_uint32
425 eapGssSmInitAuthenticate(OM_uint32 *minor,
426                          gss_cred_id_t cred,
427                          gss_ctx_id_t ctx,
428                          gss_name_t target,
429                          gss_OID mech,
430                          OM_uint32 reqFlags,
431                          OM_uint32 timeReq,
432                          gss_channel_bindings_t chanBindings,
433                          gss_buffer_t inputToken,
434                          gss_buffer_t outputToken)
435 {
436     OM_uint32 major;
437     OM_uint32 tmpMinor;
438     int code;
439     struct wpabuf *resp = NULL;
440     int initialContextToken;
441
442     *minor = 0;
443
444     initialContextToken = (inputToken == GSS_C_NO_BUFFER ||
445                            inputToken->length == 0);
446
447     major = peerConfigInit(minor, cred, ctx);
448     if (GSS_ERROR(major))
449         goto cleanup;
450
451     if (ctx->initiatorCtx.eap == NULL) {
452         struct eap_config eapConfig;
453
454         memset(&eapConfig, 0, sizeof(eapConfig));
455
456         ctx->initiatorCtx.eap = eap_peer_sm_init(ctx,
457                                                  &gssEapPolicyCallbacks,
458                                                  ctx,
459                                                  &eapConfig);
460         if (ctx->initiatorCtx.eap == NULL) {
461             major = GSS_S_FAILURE;
462             *minor = GSSEAP_PEER_SM_INIT_FAILURE;
463             goto cleanup;
464         }
465
466         ctx->flags |= CTX_FLAG_EAP_RESTART | CTX_FLAG_EAP_PORT_ENABLED;
467     }
468
469     ctx->flags |= CTX_FLAG_EAP_REQ; /* we have a Request from the acceptor */
470
471     wpabuf_set(&ctx->initiatorCtx.reqData,
472                inputToken->value, inputToken->length);
473
474     major = GSS_S_CONTINUE_NEEDED;
475
476     code = eap_peer_sm_step(ctx->initiatorCtx.eap);
477     if (ctx->flags & CTX_FLAG_EAP_RESP) {
478         ctx->flags &= ~(CTX_FLAG_EAP_RESP);
479
480         resp = eap_get_eapRespData(ctx->initiatorCtx.eap);
481     } else if (ctx->flags & CTX_FLAG_EAP_SUCCESS) {
482         major = initReady(minor, ctx, reqFlags);
483         if (GSS_ERROR(major))
484             goto cleanup;
485
486         ctx->flags &= ~(CTX_FLAG_EAP_SUCCESS);
487         major = GSS_S_CONTINUE_NEEDED;
488         ctx->state = EAP_STATE_EXTENSIONS_REQ;
489     } else if (ctx->flags & CTX_FLAG_EAP_FAIL) {
490         major = GSS_S_DEFECTIVE_CREDENTIAL;
491         *minor = GSSEAP_PEER_AUTH_FAILURE;
492     } else if (code == 0 && initialContextToken) {
493         resp = &emptyWpaBuffer;
494         major = GSS_S_CONTINUE_NEEDED;
495     } else {
496         major = GSS_S_DEFECTIVE_TOKEN;
497         *minor = GSSEAP_PEER_BAD_MESSAGE;
498     }
499
500 cleanup:
501     if (resp != NULL) {
502         OM_uint32 tmpMajor;
503         gss_buffer_desc respBuf;
504
505         assert(major == GSS_S_CONTINUE_NEEDED);
506
507         respBuf.length = wpabuf_len(resp);
508         respBuf.value = (void *)wpabuf_head(resp);
509
510         tmpMajor = duplicateBuffer(&tmpMinor, &respBuf, outputToken);
511         if (GSS_ERROR(tmpMajor)) {
512             major = tmpMajor;
513             *minor = tmpMinor;
514         }
515     }
516
517     wpabuf_set(&ctx->initiatorCtx.reqData, NULL, 0);
518     peerConfigFree(&tmpMinor, ctx);
519
520     return major;
521 }
522
523 static OM_uint32
524 eapGssSmInitExtensionsReq(OM_uint32 *minor,
525                           gss_cred_id_t cred,
526                           gss_ctx_id_t ctx,
527                           gss_name_t target,
528                           gss_OID mech,
529                           OM_uint32 reqFlags,
530                           OM_uint32 timeReq,
531                           gss_channel_bindings_t chanBindings,
532                           gss_buffer_t inputToken,
533                           gss_buffer_t outputToken)
534 {
535     OM_uint32 major;
536
537     major = gssEapMakeExtensions(minor, cred, ctx, chanBindings, outputToken);
538     if (GSS_ERROR(major))
539         return major;
540
541     assert(outputToken->value != NULL);
542
543     ctx->state = EAP_STATE_EXTENSIONS_RESP;
544
545     *minor = 0;
546     return GSS_S_CONTINUE_NEEDED;
547 }
548
549 static OM_uint32
550 eapGssSmInitExtensionsResp(OM_uint32 *minor,
551                            gss_cred_id_t cred,
552                            gss_ctx_id_t ctx,
553                            gss_name_t target,
554                            gss_OID mech,
555                            OM_uint32 reqFlags,
556                            OM_uint32 timeReq,
557                            gss_channel_bindings_t chanBindings,
558                            gss_buffer_t inputToken,
559                            gss_buffer_t outputToken)
560 {
561     OM_uint32 major;
562
563     major = gssEapVerifyExtensions(minor, cred, ctx, chanBindings, inputToken);
564     if (GSS_ERROR(major))
565         return major;
566
567     ctx->state = EAP_STATE_ESTABLISHED;
568
569     *minor = 0;
570     return GSS_S_COMPLETE;
571 }
572
573 static OM_uint32
574 eapGssSmInitEstablished(OM_uint32 *minor,
575                         gss_cred_id_t cred,
576                         gss_ctx_id_t ctx,
577                         gss_name_t target,
578                         gss_OID mech,
579                         OM_uint32 reqFlags,
580                         OM_uint32 timeReq,
581                         gss_channel_bindings_t chanBindings,
582                         gss_buffer_t inputToken,
583                         gss_buffer_t outputToken)
584 {
585     /* Called with already established context */
586     *minor = GSSEAP_CONTEXT_ESTABLISHED;
587     return GSS_S_BAD_STATUS;
588 }
589
590 static OM_uint32
591 eapGssSmInitError(OM_uint32 *minor,
592                   gss_cred_id_t cred,
593                   gss_ctx_id_t ctx,
594                   gss_name_t target,
595                   gss_OID mech,
596                   OM_uint32 reqFlags,
597                   OM_uint32 timeReq,
598                   gss_channel_bindings_t chanBindings,
599                   gss_buffer_t inputToken,
600                   gss_buffer_t outputToken)
601 {
602     OM_uint32 major;
603     unsigned char *p;
604
605     if (inputToken->length < 8) {
606         *minor = GSSEAP_TOK_TRUNC;
607         return GSS_S_DEFECTIVE_TOKEN;
608     }
609
610     p = (unsigned char *)inputToken->value;
611
612     major = load_uint32_be(&p[0]);
613     *minor = ERROR_TABLE_BASE_eapg + load_uint32_be(&p[4]);
614
615     if (!GSS_ERROR(major)) {
616         major = GSS_S_FAILURE;
617         *minor = GSSEAP_BAD_ERROR_TOKEN;
618     }
619
620     return major;
621 }
622
623 static struct gss_eap_initiator_sm {
624     enum gss_eap_token_type inputTokenType;
625     enum gss_eap_token_type outputTokenType;
626     OM_uint32 (*processToken)(OM_uint32 *,
627                               gss_cred_id_t,
628                               gss_ctx_id_t,
629                               gss_name_t,
630                               gss_OID,
631                               OM_uint32,
632                               OM_uint32,
633                               gss_channel_bindings_t,
634                               gss_buffer_t,
635                               gss_buffer_t);
636 } eapGssInitiatorSm[] = {
637     { TOK_TYPE_NONE,        TOK_TYPE_EAP_RESP,      eapGssSmInitIdentity            },
638     { TOK_TYPE_EAP_REQ,     TOK_TYPE_EAP_RESP,      eapGssSmInitAuthenticate        },
639     { TOK_TYPE_NONE,        TOK_TYPE_EXT_REQ,       eapGssSmInitExtensionsReq       },
640     { TOK_TYPE_EXT_RESP,    TOK_TYPE_NONE,          eapGssSmInitExtensionsResp      },
641     { TOK_TYPE_NONE,        TOK_TYPE_NONE,          eapGssSmInitEstablished         },
642     { TOK_TYPE_CONTEXT_ERR, TOK_TYPE_NONE,          eapGssSmInitError               },
643 #ifdef GSSEAP_ENABLE_REAUTH
644     { TOK_TYPE_GSS_REAUTH,  TOK_TYPE_GSS_REAUTH,    eapGssSmInitGssReauth           },
645 #endif
646 };
647
648 OM_uint32
649 gss_init_sec_context(OM_uint32 *minor,
650                      gss_cred_id_t cred,
651                      gss_ctx_id_t *context_handle,
652                      gss_name_t target_name,
653                      gss_OID mech_type,
654                      OM_uint32 req_flags,
655                      OM_uint32 time_req,
656                      gss_channel_bindings_t input_chan_bindings,
657                      gss_buffer_t input_token,
658                      gss_OID *actual_mech_type,
659                      gss_buffer_t output_token,
660                      OM_uint32 *ret_flags,
661                      OM_uint32 *time_rec)
662 {
663     OM_uint32 major;
664     OM_uint32 tmpMajor, tmpMinor;
665     gss_ctx_id_t ctx = *context_handle;
666     struct gss_eap_initiator_sm *sm = NULL;
667     gss_buffer_desc innerInputToken;
668     gss_buffer_desc innerOutputToken = GSS_C_EMPTY_BUFFER;
669     enum gss_eap_token_type tokType;
670     gss_cred_id_t defaultCred = GSS_C_NO_CREDENTIAL;
671     int initialContextToken = 0;
672
673     *minor = 0;
674
675     output_token->length = 0;
676     output_token->value = NULL;
677
678     if (ctx == GSS_C_NO_CONTEXT) {
679         if (input_token != GSS_C_NO_BUFFER && input_token->length != 0) {
680             *minor = GSSEAP_WRONG_SIZE;
681             return GSS_S_DEFECTIVE_TOKEN;
682         }
683
684         major = gssEapAllocContext(minor, &ctx);
685         if (GSS_ERROR(major))
686             return major;
687
688         ctx->flags |= CTX_FLAG_INITIATOR;
689
690         initialContextToken = 1;
691         *context_handle = ctx;
692     }
693
694     GSSEAP_MUTEX_LOCK(&ctx->mutex);
695
696     if (cred == GSS_C_NO_CREDENTIAL) {
697         if (ctx->initiatorCtx.defaultCred == GSS_C_NO_CREDENTIAL) {
698             major = gssEapAcquireCred(minor,
699                                       GSS_C_NO_NAME,
700                                       GSS_C_NO_BUFFER,
701                                       time_req,
702                                       GSS_C_NO_OID_SET,
703                                       GSS_C_INITIATE,
704                                       &defaultCred,
705                                       NULL,
706                                       NULL);
707             if (GSS_ERROR(major))
708                 goto cleanup;
709         }
710
711         cred = ctx->initiatorCtx.defaultCred;
712     }
713
714     GSSEAP_MUTEX_LOCK(&cred->mutex);
715
716 #ifdef GSSEAP_ENABLE_REAUTH
717     if (initialContextToken && gssEapCanReauthP(cred, target_name, time_req))
718         ctx->state = EAP_STATE_KRB_REAUTH_GSS;
719 #endif
720
721     if ((cred->flags & CRED_FLAG_INITIATE) == 0) {
722         major = GSS_S_NO_CRED;
723         *minor = GSSEAP_CRED_USAGE_MISMATCH;
724         goto cleanup;
725     }
726
727     sm = &eapGssInitiatorSm[ctx->state];
728
729     if (input_token != GSS_C_NO_BUFFER) {
730         major = gssEapVerifyToken(minor, ctx, input_token,
731                                   &tokType, &innerInputToken);
732         if (GSS_ERROR(major))
733             goto cleanup;
734
735         if (tokType == TOK_TYPE_CONTEXT_ERR) {
736             ctx->state = EAP_STATE_ERROR;
737         } else if (tokType != sm->inputTokenType) {
738             major = GSS_S_DEFECTIVE_TOKEN;
739             *minor = GSSEAP_WRONG_TOK_ID;
740             goto cleanup;
741         }
742     } else {
743         innerInputToken.length = 0;
744         innerInputToken.value = NULL;
745     }
746
747     /*
748      * Advance through state machine whilst empty tokens are emitted and
749      * the status is not GSS_S_COMPLETE or an error status.
750      */
751     do {
752         sm = &eapGssInitiatorSm[ctx->state];
753
754         major = (sm->processToken)(minor,
755                                    cred,
756                                    ctx,
757                                    target_name,
758                                    mech_type,
759                                    req_flags,
760                                    time_req,
761                                    input_chan_bindings,
762                                    &innerInputToken,
763                                    &innerOutputToken);
764         if (GSS_ERROR(major))
765             goto cleanup;
766     } while (major == GSS_S_CONTINUE_NEEDED && innerOutputToken.value == NULL);
767
768     if (actual_mech_type != NULL) {
769         if (!gssEapInternalizeOid(ctx->mechanismUsed, actual_mech_type))
770             duplicateOid(&tmpMinor, ctx->mechanismUsed, actual_mech_type);
771     }
772     if (innerOutputToken.value != NULL) {
773         tmpMajor = gssEapMakeToken(&tmpMinor, ctx, &innerOutputToken,
774                                    sm->outputTokenType, output_token);
775         if (GSS_ERROR(tmpMajor)) {
776             major = tmpMajor;
777             *minor = tmpMinor;
778             goto cleanup;
779         }
780     }
781     if (ret_flags != NULL)
782         *ret_flags = ctx->gssFlags;
783     if (time_rec != NULL)
784         gssEapContextTime(&tmpMinor, ctx, time_rec);
785
786     assert(ctx->state == EAP_STATE_ESTABLISHED || major == GSS_S_CONTINUE_NEEDED);
787
788 cleanup:
789     if (cred != GSS_C_NO_CREDENTIAL)
790         GSSEAP_MUTEX_UNLOCK(&cred->mutex);
791     GSSEAP_MUTEX_UNLOCK(&ctx->mutex);
792
793     if (GSS_ERROR(major))
794         gssEapReleaseContext(&tmpMinor, context_handle);
795
796     gss_release_buffer(&tmpMinor, &innerOutputToken);
797
798     return major;
799 }
800
801 #ifdef GSSEAP_ENABLE_REAUTH
802 static OM_uint32
803 eapGssSmInitGssReauth(OM_uint32 *minor,
804                       gss_cred_id_t cred,
805                       gss_ctx_id_t ctx,
806                       gss_name_t target,
807                       gss_OID mech,
808                       OM_uint32 reqFlags,
809                       OM_uint32 timeReq,
810                       gss_channel_bindings_t chanBindings,
811                       gss_buffer_t inputToken,
812                       gss_buffer_t outputToken)
813 {
814     OM_uint32 major, tmpMinor;
815     gss_name_t mechTarget = GSS_C_NO_NAME;
816     gss_OID actualMech = GSS_C_NO_OID;
817     OM_uint32 gssFlags, timeRec;
818
819     assert(cred != GSS_C_NO_CREDENTIAL);
820
821     ctx->flags |= CTX_FLAG_KRB_REAUTH_GSS;
822
823     if (inputToken->length == 0) {
824         major = initBegin(minor, cred, ctx, target, mech,
825                           reqFlags, timeReq, chanBindings,
826                           inputToken, outputToken);
827         if (GSS_ERROR(major))
828             goto cleanup;
829     }
830
831     major = gssEapMechToGlueName(minor, target, &mechTarget);
832     if (GSS_ERROR(major))
833         goto cleanup;
834
835     major = gssInitSecContext(minor,
836                               cred->krbCred,
837                               &ctx->kerberosCtx,
838                               mechTarget,
839                               (gss_OID)gss_mech_krb5,
840                               reqFlags, /* | GSS_C_DCE_STYLE, */
841                               timeReq,
842                               chanBindings,
843                               inputToken,
844                               &actualMech,
845                               outputToken,
846                               &gssFlags,
847                               &timeRec);
848     if (GSS_ERROR(major))
849         goto cleanup;
850
851     ctx->gssFlags = gssFlags;
852
853     if (major == GSS_S_COMPLETE) {
854         major = gssEapReauthComplete(minor, ctx, cred, actualMech, timeRec);
855         if (GSS_ERROR(major))
856             goto cleanup;
857         ctx->state = EAP_STATE_ESTABLISHED;
858     }
859
860 cleanup:
861     gssReleaseName(&tmpMinor, &mechTarget);
862
863     return major;
864 }
865 #endif /* GSSEAP_ENABLE_REAUTH */