Chbind cleanups
[mech_eap.git] / mech_eap / init_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 initiator (client). These functions
35  * wrap around libeap.
36  */
37
38 #include "gssapiP_eap.h"
39 #include "radius/radius.h"
40 #include "util_radius.h"
41 #include "utils/radius_utils.h"
42
43 static OM_uint32
44 policyVariableToFlag(enum eapol_bool_var variable)
45 {
46     OM_uint32 flag = 0;
47
48     switch (variable) {
49     case EAPOL_eapSuccess:
50         flag = CTX_FLAG_EAP_SUCCESS;
51         break;
52     case EAPOL_eapRestart:
53         flag = CTX_FLAG_EAP_RESTART;
54         break;
55     case EAPOL_eapFail:
56         flag = CTX_FLAG_EAP_FAIL;
57         break;
58     case EAPOL_eapResp:
59         flag = CTX_FLAG_EAP_RESP;
60         break;
61     case EAPOL_eapNoResp:
62         flag = CTX_FLAG_EAP_NO_RESP;
63         break;
64     case EAPOL_eapReq:
65         flag = CTX_FLAG_EAP_REQ;
66         break;
67     case EAPOL_portEnabled:
68         flag = CTX_FLAG_EAP_PORT_ENABLED;
69         break;
70     case EAPOL_altAccept:
71         flag = CTX_FLAG_EAP_ALT_ACCEPT;
72         break;
73     case EAPOL_altReject:
74         flag = CTX_FLAG_EAP_ALT_REJECT;
75         break;
76     }
77
78     return flag;
79 }
80
81 static struct eap_peer_config *
82 peerGetConfig(void *ctx)
83 {
84     gss_ctx_id_t gssCtx = (gss_ctx_id_t)ctx;
85
86     return &gssCtx->initiatorCtx.eapPeerConfig;
87 }
88
89 static Boolean
90 peerGetBool(void *data, enum eapol_bool_var variable)
91 {
92     gss_ctx_id_t ctx = data;
93     OM_uint32 flag;
94
95     if (ctx == GSS_C_NO_CONTEXT)
96         return FALSE;
97
98     flag = policyVariableToFlag(variable);
99
100     return ((ctx->flags & flag) != 0);
101 }
102
103 static void
104 peerSetBool(void *data, enum eapol_bool_var variable,
105             Boolean value)
106 {
107     gss_ctx_id_t ctx = data;
108     OM_uint32 flag;
109
110     if (ctx == GSS_C_NO_CONTEXT)
111         return;
112
113     flag = policyVariableToFlag(variable);
114
115     if (value)
116         ctx->flags |= flag;
117     else
118         ctx->flags &= ~(flag);
119 }
120
121 static unsigned int
122 peerGetInt(void *data, enum eapol_int_var variable)
123 {
124     gss_ctx_id_t ctx = data;
125
126     if (ctx == GSS_C_NO_CONTEXT)
127         return FALSE;
128
129     GSSEAP_ASSERT(CTX_IS_INITIATOR(ctx));
130
131     switch (variable) {
132     case EAPOL_idleWhile:
133         return ctx->initiatorCtx.idleWhile;
134         break;
135     }
136
137     return 0;
138 }
139
140 static void
141 peerSetInt(void *data, enum eapol_int_var variable,
142            unsigned int value)
143 {
144     gss_ctx_id_t ctx = data;
145
146     if (ctx == GSS_C_NO_CONTEXT)
147         return;
148
149     GSSEAP_ASSERT(CTX_IS_INITIATOR(ctx));
150
151     switch (variable) {
152     case EAPOL_idleWhile:
153         ctx->initiatorCtx.idleWhile = value;
154         break;
155     }
156 }
157
158 static struct wpabuf *
159 peerGetEapReqData(void *ctx)
160 {
161     gss_ctx_id_t gssCtx = (gss_ctx_id_t)ctx;
162
163     return &gssCtx->initiatorCtx.reqData;
164 }
165
166 static void
167 peerSetConfigBlob(void *ctx GSSEAP_UNUSED,
168                   struct wpa_config_blob *blob GSSEAP_UNUSED)
169 {
170 }
171
172 static const struct wpa_config_blob *
173 peerGetConfigBlob(void *ctx GSSEAP_UNUSED,
174                   const char *name GSSEAP_UNUSED)
175 {
176     return NULL;
177 }
178
179 static void
180 peerNotifyPending(void *ctx GSSEAP_UNUSED)
181 {
182 }
183
184 static struct eapol_callbacks gssEapPolicyCallbacks = {
185     peerGetConfig,
186     peerGetBool,
187     peerSetBool,
188     peerGetInt,
189     peerSetInt,
190     peerGetEapReqData,
191     peerSetConfigBlob,
192     peerGetConfigBlob,
193     peerNotifyPending,
194 };
195
196 #ifdef GSSEAP_DEBUG
197 extern int wpa_debug_level;
198 #endif
199
200 #define CHBIND_SERVICE_NAME_FLAG        0x01
201 #define CHBIND_HOST_NAME_FLAG           0x02
202 #define CHBIND_SERVICE_SPECIFIC_FLAG    0x04
203 #define CHBIND_REALM_NAME_FLAG          0x08
204
205 extern void TestFunc();
206
207 static OM_uint32
208 peerInitEapChannelBinding(OM_uint32 *minor, gss_ctx_id_t ctx)
209 {
210     struct wpabuf *buf = NULL;
211     unsigned int chbindReqFlags = 0;
212     krb5_principal princ = NULL;
213     gss_buffer_desc nameBuf = GSS_C_EMPTY_BUFFER;
214     OM_uint32 major = GSS_S_COMPLETE;
215     krb5_context krbContext = NULL;
216
217     /* XXX is this check redundant? */
218     if (ctx->acceptorName == GSS_C_NO_NAME) {
219         major = GSS_S_BAD_NAME;
220         *minor = GSSEAP_NO_ACCEPTOR_NAME;
221         goto cleanup;
222     }
223
224     princ = ctx->acceptorName->krbPrincipal;
225
226     krbPrincComponentToGssBuffer(princ, 0, &nameBuf);
227     if (nameBuf.length > 0) {
228         major = gssEapRadiusAddAttr(minor, &buf, PW_GSS_ACCEPTOR_SERVICE_NAME,
229                                     0, &nameBuf);
230         if (GSS_ERROR(major))
231             goto cleanup;
232
233         chbindReqFlags |= CHBIND_SERVICE_NAME_FLAG;
234     }
235
236     krbPrincComponentToGssBuffer(princ, 1, &nameBuf);
237     if (nameBuf.length > 0) {
238         major = gssEapRadiusAddAttr(minor, &buf, PW_GSS_ACCEPTOR_HOST_NAME,
239                                     0, &nameBuf);
240         if (GSS_ERROR(major))
241             goto cleanup;
242
243         chbindReqFlags |= CHBIND_HOST_NAME_FLAG;
244     }
245
246     GSSEAP_KRB_INIT(&krbContext);
247
248     *minor = krbPrincUnparseServiceSpecifics(krbContext, princ, &nameBuf);
249     if (*minor != 0)
250         goto cleanup;
251
252     if (nameBuf.length > 0) {
253         major = gssEapRadiusAddAttr(minor, &buf,
254                                     PW_GSS_ACCEPTOR_SERVICE_SPECIFICS,
255                                     0, &nameBuf);
256         if (GSS_ERROR(major))
257             goto cleanup;
258
259         chbindReqFlags |= CHBIND_SERVICE_SPECIFIC_FLAG;
260     }
261
262     krbFreeUnparsedName(krbContext, &nameBuf);
263     krbPrincRealmToGssBuffer(princ, &nameBuf);
264
265     if (nameBuf.length > 0) {
266         major = gssEapRadiusAddAttr(minor, &buf,
267                                     PW_GSS_ACCEPTOR_REALM_NAME,
268                                     0, &nameBuf);
269         chbindReqFlags |= CHBIND_REALM_NAME_FLAG;
270     }
271
272     if (chbindReqFlags == 0) {
273         major = GSS_S_BAD_NAME;
274         *minor = GSSEAP_BAD_ACCEPTOR_NAME;
275         goto cleanup;
276     }
277
278     ctx->initiatorCtx.chbindData = buf;
279     ctx->initiatorCtx.chbindReqFlags = chbindReqFlags;
280
281     buf = NULL;
282
283     major = GSS_S_COMPLETE;
284     *minor = 0;
285
286 cleanup:
287     krbFreeUnparsedName(krbContext, &nameBuf);
288     wpabuf_free(buf);
289
290     return major;
291 }
292
293 static void
294 peerProcessChbindResponse(void *context, int code, int nsid,
295                           u8 *data, size_t len)
296 {
297     radius_parser msg;
298     gss_ctx_id_t ctx = (gss_ctx_id_t )context;
299     void *vsadata;
300     u8 type;
301     u32 vendor_id;
302     u32 chbindRetFlags = 0;
303     size_t vsadata_len;
304
305     if (nsid != CHBIND_NSID_RADIUS)
306         return;
307
308     msg = radius_parser_start(data, len);
309     if (msg == NULL)
310         return;
311
312     while (radius_parser_parse_tlv(msg, &type, &vendor_id, &vsadata,
313                                    &vsadata_len) == 0) {
314         switch (type) {
315         case PW_GSS_ACCEPTOR_SERVICE_NAME:
316             chbindRetFlags |= CHBIND_SERVICE_NAME_FLAG;
317             break;
318         case PW_GSS_ACCEPTOR_HOST_NAME:
319             chbindRetFlags |= CHBIND_HOST_NAME_FLAG;
320             break;
321         case PW_GSS_ACCEPTOR_SERVICE_SPECIFICS:
322             chbindRetFlags |= CHBIND_SERVICE_SPECIFIC_FLAG;
323             break;
324         case PW_GSS_ACCEPTOR_REALM_NAME:
325             chbindRetFlags |= CHBIND_REALM_NAME_FLAG;
326             break;
327         }
328     }
329
330     radius_parser_finish(msg);
331
332     if (code == CHBIND_CODE_SUCCESS &&
333         ((chbindRetFlags & ctx->initiatorCtx.chbindReqFlags) == ctx->initiatorCtx.chbindReqFlags)) {
334         ctx->flags |= CTX_FLAG_EAP_CHBIND_ACCEPT;
335         ctx->gssFlags |= GSS_C_MUTUAL_FLAG;
336     } /* else log failures? */
337 }
338
339 static OM_uint32
340 peerConfigInit(OM_uint32 *minor, gss_ctx_id_t ctx)
341 {
342     OM_uint32 major;
343     krb5_context krbContext;
344     struct eap_peer_config *eapPeerConfig = &ctx->initiatorCtx.eapPeerConfig;
345     gss_buffer_desc identity = GSS_C_EMPTY_BUFFER;
346     gss_buffer_desc realm = GSS_C_EMPTY_BUFFER;
347     gss_cred_id_t cred = ctx->cred;
348
349     eapPeerConfig->identity = NULL;
350     eapPeerConfig->identity_len = 0;
351     eapPeerConfig->anonymous_identity = NULL;
352     eapPeerConfig->anonymous_identity_len = 0;
353     eapPeerConfig->password = NULL;
354     eapPeerConfig->password_len = 0;
355
356     GSSEAP_ASSERT(cred != GSS_C_NO_CREDENTIAL);
357
358     GSSEAP_KRB_INIT(&krbContext);
359
360     eapPeerConfig->fragment_size = 1024;
361 #ifdef GSSEAP_DEBUG
362     wpa_debug_level = 0;
363 #endif
364
365     GSSEAP_ASSERT(cred->name != GSS_C_NO_NAME);
366
367     if ((cred->name->flags & (NAME_FLAG_NAI | NAME_FLAG_SERVICE)) == 0) {
368         *minor = GSSEAP_BAD_INITIATOR_NAME;
369         return GSS_S_BAD_NAME;
370     }
371
372     /* identity */
373     major = gssEapDisplayName(minor, cred->name, &identity, NULL);
374     if (GSS_ERROR(major))
375         return major;
376
377     eapPeerConfig->identity = (unsigned char *)identity.value;
378     eapPeerConfig->identity_len = identity.length;
379
380     krbPrincRealmToGssBuffer(cred->name->krbPrincipal, &realm);
381
382     /* anonymous_identity */
383     eapPeerConfig->anonymous_identity = GSSEAP_MALLOC(realm.length + 2);
384     if (eapPeerConfig->anonymous_identity == NULL) {
385         *minor = ENOMEM;
386         return GSS_S_FAILURE;
387     }
388
389     eapPeerConfig->anonymous_identity[0] = '@';
390     memcpy(eapPeerConfig->anonymous_identity + 1, realm.value, realm.length);
391     eapPeerConfig->anonymous_identity[1 + realm.length] = '\0';
392     eapPeerConfig->anonymous_identity_len = 1 + realm.length;
393
394     /* password */
395     eapPeerConfig->password = (unsigned char *)cred->password.value;
396     eapPeerConfig->password_len = cred->password.length;
397
398     /* certs */
399     eapPeerConfig->ca_cert = (unsigned char *)cred->caCertificate.value;
400     eapPeerConfig->subject_match = (unsigned char *)cred->subjectNameConstraint.value;
401     eapPeerConfig->altsubject_match = (unsigned char *)cred->subjectAltNameConstraint.value;
402
403     /* eap channel binding */
404     if (ctx->initiatorCtx.chbindData != NULL) {
405         struct eap_peer_chbind_config *chbind_config =
406             (struct eap_peer_chbind_config *)GSSEAP_MALLOC(sizeof(struct eap_peer_chbind_config));
407         if (chbind_config == NULL) {
408             *minor = ENOMEM;
409             return GSS_S_FAILURE;
410         }
411
412         chbind_config->req_data = wpabuf_mhead_u8(ctx->initiatorCtx.chbindData);
413         chbind_config->req_data_len = wpabuf_len(ctx->initiatorCtx.chbindData);
414         chbind_config->nsid = CHBIND_NSID_RADIUS;
415         chbind_config->response_cb = &peerProcessChbindResponse;
416         chbind_config->ctx = ctx;
417         eapPeerConfig->chbind_config = chbind_config;
418         eapPeerConfig->chbind_config_len = 1;
419     } else {
420         eapPeerConfig->chbind_config = NULL;
421         eapPeerConfig->chbind_config_len = 0;
422     }
423
424     *minor = 0;
425     return GSS_S_COMPLETE;
426 }
427
428 static OM_uint32
429 peerConfigFree(OM_uint32 *minor,
430                gss_ctx_id_t ctx)
431 {
432     struct eap_peer_config *eapPeerConfig = &ctx->initiatorCtx.eapPeerConfig;
433
434     if (eapPeerConfig->identity != NULL) {
435         GSSEAP_FREE(eapPeerConfig->identity);
436         eapPeerConfig->identity = NULL;
437         eapPeerConfig->identity_len = 0;
438     }
439
440     if (eapPeerConfig->anonymous_identity != NULL) {
441         GSSEAP_FREE(eapPeerConfig->anonymous_identity);
442         eapPeerConfig->anonymous_identity = NULL;
443         eapPeerConfig->anonymous_identity_len = 0;
444     }
445
446     *minor = 0;
447     return GSS_S_COMPLETE;
448 }
449
450 /*
451  * Mark an initiator context as ready for cryptographic operations
452  */
453 static OM_uint32
454 initReady(OM_uint32 *minor, gss_ctx_id_t ctx)
455 {
456     OM_uint32 major;
457     const unsigned char *key;
458     size_t keyLength;
459
460     /* Cache encryption type derived from selected mechanism OID */
461     major = gssEapOidToEnctype(minor, ctx->mechanismUsed, &ctx->encryptionType);
462     if (GSS_ERROR(major))
463         return major;
464
465     if (!eap_key_available(ctx->initiatorCtx.eap)) {
466         *minor = GSSEAP_KEY_UNAVAILABLE;
467         return GSS_S_UNAVAILABLE;
468     }
469
470     key = eap_get_eapKeyData(ctx->initiatorCtx.eap, &keyLength);
471
472     if (keyLength < EAP_EMSK_LEN) {
473         *minor = GSSEAP_KEY_TOO_SHORT;
474         return GSS_S_UNAVAILABLE;
475     }
476
477     major = gssEapDeriveRfc3961Key(minor,
478                                    &key[EAP_EMSK_LEN / 2],
479                                    EAP_EMSK_LEN / 2,
480                                    ctx->encryptionType,
481                                    &ctx->rfc3961Key);
482        if (GSS_ERROR(major))
483            return major;
484
485     major = rfc3961ChecksumTypeForKey(minor, &ctx->rfc3961Key,
486                                       &ctx->checksumType);
487     if (GSS_ERROR(major))
488         return major;
489
490     major = sequenceInit(minor,
491                          &ctx->seqState,
492                          ctx->recvSeq,
493                          ((ctx->gssFlags & GSS_C_REPLAY_FLAG) != 0),
494                          ((ctx->gssFlags & GSS_C_SEQUENCE_FLAG) != 0),
495                          TRUE);
496     if (GSS_ERROR(major))
497         return major;
498
499     *minor = 0;
500     return GSS_S_COMPLETE;
501 }
502
503 static OM_uint32
504 initBegin(OM_uint32 *minor,
505           gss_ctx_id_t ctx,
506           gss_name_t target,
507           gss_OID mech,
508           OM_uint32 reqFlags GSSEAP_UNUSED,
509           OM_uint32 timeReq,
510           gss_channel_bindings_t chanBindings GSSEAP_UNUSED)
511 {
512     OM_uint32 major;
513     gss_cred_id_t cred = ctx->cred;
514
515     GSSEAP_ASSERT(cred != GSS_C_NO_CREDENTIAL);
516
517     if (cred->expiryTime)
518         ctx->expiryTime = cred->expiryTime;
519     else if (timeReq == 0 || timeReq == GSS_C_INDEFINITE)
520         ctx->expiryTime = 0;
521     else
522         ctx->expiryTime = time(NULL) + timeReq;
523
524     /*
525      * The credential mutex protects its name, however we need to
526      * explicitly lock the acceptor name (unlikely as it may be
527      * that it has attributes set on it).
528      */
529     major = gssEapDuplicateName(minor, cred->name, &ctx->initiatorName);
530     if (GSS_ERROR(major))
531         return major;
532
533     if (target != GSS_C_NO_NAME) {
534         GSSEAP_MUTEX_LOCK(&target->mutex);
535
536         major = gssEapDuplicateName(minor, target, &ctx->acceptorName);
537         if (GSS_ERROR(major)) {
538             GSSEAP_MUTEX_UNLOCK(&target->mutex);
539             return major;
540         }
541
542         GSSEAP_MUTEX_UNLOCK(&target->mutex);
543     }
544
545     major = gssEapCanonicalizeOid(minor,
546                                   mech,
547                                   OID_FLAG_NULL_VALID | OID_FLAG_MAP_NULL_TO_DEFAULT_MECH,
548                                   &ctx->mechanismUsed);
549     if (GSS_ERROR(major))
550         return major;
551
552     /* If credentials were provided, check they're usable with this mech */
553     if (!gssEapCredAvailable(cred, ctx->mechanismUsed)) {
554         *minor = GSSEAP_CRED_MECH_MISMATCH;
555         return GSS_S_BAD_MECH;
556     }
557
558     *minor = 0;
559     return GSS_S_COMPLETE;
560 }
561
562 static OM_uint32
563 eapGssSmInitError(OM_uint32 *minor,
564                   gss_cred_id_t cred GSSEAP_UNUSED,
565                   gss_ctx_id_t ctx GSSEAP_UNUSED,
566                   gss_name_t target GSSEAP_UNUSED,
567                   gss_OID mech GSSEAP_UNUSED,
568                   OM_uint32 reqFlags GSSEAP_UNUSED,
569                   OM_uint32 timeReq GSSEAP_UNUSED,
570                   gss_channel_bindings_t chanBindings GSSEAP_UNUSED,
571                   gss_buffer_t inputToken,
572                   gss_buffer_t outputToken GSSEAP_UNUSED,
573                   OM_uint32 *smFlags GSSEAP_UNUSED)
574 {
575     OM_uint32 major;
576     unsigned char *p;
577
578     if (inputToken->length < 8) {
579         *minor = GSSEAP_TOK_TRUNC;
580         return GSS_S_DEFECTIVE_TOKEN;
581     }
582
583     p = (unsigned char *)inputToken->value;
584
585     major = load_uint32_be(&p[0]);
586     *minor = ERROR_TABLE_BASE_eapg + load_uint32_be(&p[4]);
587
588     if (!GSS_ERROR(major) || !IS_WIRE_ERROR(*minor)) {
589         major = GSS_S_FAILURE;
590         *minor = GSSEAP_BAD_ERROR_TOKEN;
591     }
592
593     GSSEAP_ASSERT(GSS_ERROR(major));
594
595     return major;
596 }
597
598 #ifdef GSSEAP_ENABLE_REAUTH
599 static OM_uint32
600 eapGssSmInitGssReauth(OM_uint32 *minor,
601                       gss_cred_id_t cred,
602                       gss_ctx_id_t ctx,
603                       gss_name_t target,
604                       gss_OID mech GSSEAP_UNUSED,
605                       OM_uint32 reqFlags,
606                       OM_uint32 timeReq,
607                       gss_channel_bindings_t chanBindings,
608                       gss_buffer_t inputToken,
609                       gss_buffer_t outputToken,
610                       OM_uint32 *smFlags GSSEAP_UNUSED)
611 {
612     OM_uint32 major, tmpMinor;
613     gss_name_t mechTarget = GSS_C_NO_NAME;
614     gss_OID actualMech = GSS_C_NO_OID;
615     OM_uint32 gssFlags, timeRec;
616
617     /*
618      * Here we use the passed in credential handle because the resolved
619      * context credential does not currently have the reauth creds.
620      */
621     if (GSSEAP_SM_STATE(ctx) == GSSEAP_STATE_INITIAL) {
622         if (!gssEapCanReauthP(cred, target, timeReq))
623             return GSS_S_CONTINUE_NEEDED;
624
625         ctx->flags |= CTX_FLAG_KRB_REAUTH;
626     } else if ((ctx->flags & CTX_FLAG_KRB_REAUTH) == 0) {
627         major = GSS_S_DEFECTIVE_TOKEN;
628         *minor = GSSEAP_WRONG_ITOK;
629         goto cleanup;
630     }
631
632     GSSEAP_ASSERT(cred != GSS_C_NO_CREDENTIAL);
633
634     major = gssEapMechToGlueName(minor, target, &mechTarget);
635     if (GSS_ERROR(major))
636         goto cleanup;
637
638     major = gssInitSecContext(minor,
639                               cred->reauthCred,
640                               &ctx->reauthCtx,
641                               mechTarget,
642                               (gss_OID)gss_mech_krb5,
643                               reqFlags | GSS_C_MUTUAL_FLAG,
644                               timeReq,
645                               chanBindings,
646                               inputToken,
647                               &actualMech,
648                               outputToken,
649                               &gssFlags,
650                               &timeRec);
651     if (GSS_ERROR(major))
652         goto cleanup;
653
654     ctx->gssFlags = gssFlags;
655
656     if (major == GSS_S_COMPLETE) {
657         GSSEAP_ASSERT(GSSEAP_SM_STATE(ctx) == GSSEAP_STATE_REAUTHENTICATE);
658
659         major = gssEapReauthComplete(minor, ctx, cred, actualMech, timeRec);
660         if (GSS_ERROR(major))
661             goto cleanup;
662         GSSEAP_SM_TRANSITION(ctx, GSSEAP_STATE_ESTABLISHED);
663     } else {
664         GSSEAP_SM_TRANSITION(ctx, GSSEAP_STATE_REAUTHENTICATE);
665     }
666
667 cleanup:
668     gssReleaseName(&tmpMinor, &mechTarget);
669
670     return major;
671 }
672 #endif /* GSSEAP_ENABLE_REAUTH */
673
674 #ifdef GSSEAP_DEBUG
675 static OM_uint32
676 eapGssSmInitVendorInfo(OM_uint32 *minor,
677                        gss_cred_id_t cred GSSEAP_UNUSED,
678                        gss_ctx_id_t ctx GSSEAP_UNUSED,
679                        gss_name_t target GSSEAP_UNUSED,
680                        gss_OID mech GSSEAP_UNUSED,
681                        OM_uint32 reqFlags GSSEAP_UNUSED,
682                        OM_uint32 timeReq GSSEAP_UNUSED,
683                        gss_channel_bindings_t chanBindings GSSEAP_UNUSED,
684                        gss_buffer_t inputToken GSSEAP_UNUSED,
685                        gss_buffer_t outputToken,
686                        OM_uint32 *smFlags GSSEAP_UNUSED)
687 {
688     OM_uint32 major;
689
690     major = makeStringBuffer(minor, "JANET(UK)", outputToken);
691     if (GSS_ERROR(major))
692         return major;
693
694     return GSS_S_CONTINUE_NEEDED;
695 }
696 #endif
697
698 static OM_uint32
699 eapGssSmInitAcceptorName(OM_uint32 *minor,
700                          gss_cred_id_t cred GSSEAP_UNUSED,
701                          gss_ctx_id_t ctx,
702                          gss_name_t target GSSEAP_UNUSED,
703                          gss_OID mech GSSEAP_UNUSED,
704                          OM_uint32 reqFlags GSSEAP_UNUSED,
705                          OM_uint32 timeReq GSSEAP_UNUSED,
706                          gss_channel_bindings_t chanBindings GSSEAP_UNUSED,
707                          gss_buffer_t inputToken GSSEAP_UNUSED,
708                          gss_buffer_t outputToken,
709                          OM_uint32 *smFlags GSSEAP_UNUSED)
710 {
711     OM_uint32 major;
712
713     if (GSSEAP_SM_STATE(ctx) == GSSEAP_STATE_INITIAL &&
714         ctx->acceptorName != GSS_C_NO_NAME) {
715
716         /* Send desired target name to acceptor */
717         major = gssEapDisplayName(minor, ctx->acceptorName,
718                                   outputToken, NULL);
719         if (GSS_ERROR(major))
720             return major;
721     } else if (inputToken != GSS_C_NO_BUFFER &&
722                ctx->acceptorName == GSS_C_NO_NAME) {
723         /* Accept target name hint from acceptor */
724         major = gssEapImportName(minor, inputToken,
725                                  GSS_C_NT_USER_NAME,
726                                  ctx->mechanismUsed,
727                                  &ctx->acceptorName);
728         if (GSS_ERROR(major))
729             return major;
730     }
731
732     /*
733      * Currently, other parts of the code assume that the acceptor name
734      * is available, hence this check.
735      */
736     if (ctx->acceptorName == GSS_C_NO_NAME) {
737         *minor = GSSEAP_NO_ACCEPTOR_NAME;
738         return GSS_S_FAILURE;
739     }
740
741     /*
742      * Generate channel binding data
743      */
744     if (ctx->initiatorCtx.chbindData == NULL) {
745         major = peerInitEapChannelBinding(minor, ctx);
746         if (GSS_ERROR(major))
747             return major;
748     }
749
750     return GSS_S_CONTINUE_NEEDED;
751 }
752
753 static OM_uint32
754 eapGssSmInitIdentity(OM_uint32 *minor,
755                      gss_cred_id_t cred GSSEAP_UNUSED,
756                      gss_ctx_id_t ctx,
757                      gss_name_t target GSSEAP_UNUSED,
758                      gss_OID mech GSSEAP_UNUSED,
759                      OM_uint32 reqFlags GSSEAP_UNUSED,
760                      OM_uint32 timeReq GSSEAP_UNUSED,
761                      gss_channel_bindings_t chanBindings GSSEAP_UNUSED,
762                      gss_buffer_t inputToken GSSEAP_UNUSED,
763                      gss_buffer_t outputToken GSSEAP_UNUSED,
764                      OM_uint32 *smFlags)
765 {
766     struct eap_config eapConfig;
767
768 #ifdef GSSEAP_ENABLE_REAUTH
769     if (GSSEAP_SM_STATE(ctx) == GSSEAP_STATE_REAUTHENTICATE) {
770         OM_uint32 tmpMinor;
771
772         /* server didn't support reauthentication, sent EAP request */
773         gssDeleteSecContext(&tmpMinor, &ctx->reauthCtx, GSS_C_NO_BUFFER);
774         ctx->flags &= ~(CTX_FLAG_KRB_REAUTH);
775         GSSEAP_SM_TRANSITION(ctx, GSSEAP_STATE_INITIAL);
776     } else
777 #endif
778         *smFlags |= SM_FLAG_FORCE_SEND_TOKEN;
779
780     GSSEAP_ASSERT((ctx->flags & CTX_FLAG_KRB_REAUTH) == 0);
781     GSSEAP_ASSERT(inputToken == GSS_C_NO_BUFFER);
782
783     memset(&eapConfig, 0, sizeof(eapConfig));
784
785     ctx->initiatorCtx.eap = eap_peer_sm_init(ctx,
786                                              &gssEapPolicyCallbacks,
787                                              ctx,
788                                              &eapConfig);
789     if (ctx->initiatorCtx.eap == NULL) {
790         *minor = GSSEAP_PEER_SM_INIT_FAILURE;
791         return GSS_S_FAILURE;
792     }
793
794     ctx->flags |= CTX_FLAG_EAP_RESTART | CTX_FLAG_EAP_PORT_ENABLED;
795
796     /* poke EAP state machine */
797     if (eap_peer_sm_step(ctx->initiatorCtx.eap) != 0) {
798         *minor = GSSEAP_PEER_SM_STEP_FAILURE;
799         return GSS_S_FAILURE;
800     }
801
802     GSSEAP_SM_TRANSITION_NEXT(ctx);
803
804     *minor = 0;
805
806     return GSS_S_CONTINUE_NEEDED;
807 }
808
809 static OM_uint32
810 eapGssSmInitAuthenticate(OM_uint32 *minor,
811                          gss_cred_id_t cred GSSEAP_UNUSED,
812                          gss_ctx_id_t ctx,
813                          gss_name_t target GSSEAP_UNUSED,
814                          gss_OID mech GSSEAP_UNUSED,
815                          OM_uint32 reqFlags GSSEAP_UNUSED,
816                          OM_uint32 timeReq GSSEAP_UNUSED,
817                          gss_channel_bindings_t chanBindings GSSEAP_UNUSED,
818                          gss_buffer_t inputToken GSSEAP_UNUSED,
819                          gss_buffer_t outputToken,
820                          OM_uint32 *smFlags)
821 {
822     OM_uint32 major;
823     OM_uint32 tmpMinor;
824     struct wpabuf *resp = NULL;
825
826     *minor = 0;
827
828     GSSEAP_ASSERT(inputToken != GSS_C_NO_BUFFER);
829
830     major = peerConfigInit(minor, ctx);
831     if (GSS_ERROR(major))
832         goto cleanup;
833
834     GSSEAP_ASSERT(ctx->initiatorCtx.eap != NULL);
835     GSSEAP_ASSERT(ctx->flags & CTX_FLAG_EAP_PORT_ENABLED);
836
837     ctx->flags |= CTX_FLAG_EAP_REQ; /* we have a Request from the acceptor */
838
839     wpabuf_set(&ctx->initiatorCtx.reqData,
840                inputToken->value, inputToken->length);
841
842     major = GSS_S_CONTINUE_NEEDED;
843
844     eap_peer_sm_step(ctx->initiatorCtx.eap);
845     if (ctx->flags & CTX_FLAG_EAP_RESP) {
846         ctx->flags &= ~(CTX_FLAG_EAP_RESP);
847
848         resp = eap_get_eapRespData(ctx->initiatorCtx.eap);
849     } else if (ctx->flags & CTX_FLAG_EAP_SUCCESS) {
850         major = initReady(minor, ctx);
851         if (GSS_ERROR(major))
852             goto cleanup;
853
854         ctx->flags &= ~(CTX_FLAG_EAP_SUCCESS);
855         major = GSS_S_CONTINUE_NEEDED;
856         GSSEAP_SM_TRANSITION_NEXT(ctx);
857     } else if (ctx->flags & CTX_FLAG_EAP_FAIL) {
858         major = GSS_S_DEFECTIVE_CREDENTIAL;
859         *minor = GSSEAP_PEER_AUTH_FAILURE;
860     } else {
861         major = GSS_S_DEFECTIVE_TOKEN;
862         *minor = GSSEAP_PEER_BAD_MESSAGE;
863     }
864
865 cleanup:
866     if (resp != NULL) {
867         OM_uint32 tmpMajor;
868         gss_buffer_desc respBuf;
869
870         GSSEAP_ASSERT(major == GSS_S_CONTINUE_NEEDED);
871
872         respBuf.length = wpabuf_len(resp);
873         respBuf.value = (void *)wpabuf_head(resp);
874
875         tmpMajor = duplicateBuffer(&tmpMinor, &respBuf, outputToken);
876         if (GSS_ERROR(tmpMajor)) {
877             major = tmpMajor;
878             *minor = tmpMinor;
879         }
880
881         *smFlags |= SM_FLAG_OUTPUT_TOKEN_CRITICAL;
882     }
883
884     wpabuf_set(&ctx->initiatorCtx.reqData, NULL, 0);
885     peerConfigFree(&tmpMinor, ctx);
886
887     return major;
888 }
889
890 static OM_uint32
891 eapGssSmInitGssFlags(OM_uint32 *minor,
892                      gss_cred_id_t cred GSSEAP_UNUSED,
893                      gss_ctx_id_t ctx,
894                      gss_name_t target GSSEAP_UNUSED,
895                      gss_OID mech GSSEAP_UNUSED,
896                      OM_uint32 reqFlags GSSEAP_UNUSED,
897                      OM_uint32 timeReq GSSEAP_UNUSED,
898                      gss_channel_bindings_t chanBindings GSSEAP_UNUSED,
899                      gss_buffer_t inputToken GSSEAP_UNUSED,
900                      gss_buffer_t outputToken,
901                      OM_uint32 *smFlags GSSEAP_UNUSED)
902 {
903     unsigned char wireFlags[4];
904     gss_buffer_desc flagsBuf;
905
906     store_uint32_be(ctx->gssFlags & GSSEAP_WIRE_FLAGS_MASK, wireFlags);
907
908     flagsBuf.length = sizeof(wireFlags);
909     flagsBuf.value = wireFlags;
910
911     return duplicateBuffer(minor, &flagsBuf, outputToken);
912 }
913
914 static OM_uint32
915 eapGssSmInitGssChannelBindings(OM_uint32 *minor,
916                                gss_cred_id_t cred GSSEAP_UNUSED,
917                                gss_ctx_id_t ctx,
918                                gss_name_t target GSSEAP_UNUSED,
919                                gss_OID mech GSSEAP_UNUSED,
920                                OM_uint32 reqFlags GSSEAP_UNUSED,
921                                OM_uint32 timeReq GSSEAP_UNUSED,
922                                gss_channel_bindings_t chanBindings,
923                                gss_buffer_t inputToken GSSEAP_UNUSED,
924                                gss_buffer_t outputToken,
925                                OM_uint32 *smFlags)
926 {
927     OM_uint32 major;
928     gss_buffer_desc buffer = GSS_C_EMPTY_BUFFER;
929
930     if (chanBindings != GSS_C_NO_CHANNEL_BINDINGS)
931         buffer = chanBindings->application_data;
932
933     major = gssEapWrap(minor, ctx, TRUE, GSS_C_QOP_DEFAULT,
934                        &buffer, NULL, outputToken);
935     if (GSS_ERROR(major))
936         return major;
937
938     GSSEAP_ASSERT(outputToken->value != NULL);
939
940     *minor = 0;
941     *smFlags |= SM_FLAG_OUTPUT_TOKEN_CRITICAL;
942
943     return GSS_S_CONTINUE_NEEDED;
944 }
945
946 static OM_uint32
947 eapGssSmInitInitiatorMIC(OM_uint32 *minor,
948                          gss_cred_id_t cred GSSEAP_UNUSED,
949                          gss_ctx_id_t ctx,
950                          gss_name_t target GSSEAP_UNUSED,
951                          gss_OID mech GSSEAP_UNUSED,
952                          OM_uint32 reqFlags GSSEAP_UNUSED,
953                          OM_uint32 timeReq GSSEAP_UNUSED,
954                          gss_channel_bindings_t chanBindings GSSEAP_UNUSED,
955                          gss_buffer_t inputToken GSSEAP_UNUSED,
956                          gss_buffer_t outputToken,
957                          OM_uint32 *smFlags)
958 {
959     OM_uint32 major;
960
961     major = gssEapMakeTokenMIC(minor, ctx, outputToken);
962     if (GSS_ERROR(major))
963         return major;
964
965     GSSEAP_SM_TRANSITION_NEXT(ctx);
966
967     *minor = 0;
968     *smFlags |= SM_FLAG_OUTPUT_TOKEN_CRITICAL;
969
970     return GSS_S_CONTINUE_NEEDED;
971 }
972  
973 #ifdef GSSEAP_ENABLE_REAUTH
974 static OM_uint32
975 eapGssSmInitReauthCreds(OM_uint32 *minor,
976                         gss_cred_id_t cred,
977                         gss_ctx_id_t ctx,
978                         gss_name_t target GSSEAP_UNUSED,
979                         gss_OID mech GSSEAP_UNUSED,
980                         OM_uint32 reqFlags GSSEAP_UNUSED,
981                         OM_uint32 timeReq GSSEAP_UNUSED,
982                         gss_channel_bindings_t chanBindings GSSEAP_UNUSED,
983                         gss_buffer_t inputToken,
984                         gss_buffer_t outputToken GSSEAP_UNUSED,
985                         OM_uint32 *smFlags GSSEAP_UNUSED)
986 {
987     OM_uint32 major;
988
989     if (ctx->gssFlags & GSS_C_MUTUAL_FLAG) {
990         major = gssEapStoreReauthCreds(minor, ctx, cred, inputToken);
991         if (GSS_ERROR(major))
992             return major;
993     }
994
995     *minor = 0;
996     return GSS_S_CONTINUE_NEEDED;
997 }
998 #endif /* GSSEAP_ENABLE_REAUTH */
999
1000 static OM_uint32
1001 eapGssSmInitAcceptorMIC(OM_uint32 *minor,
1002                         gss_cred_id_t cred GSSEAP_UNUSED,
1003                         gss_ctx_id_t ctx,
1004                         gss_name_t target GSSEAP_UNUSED,
1005                         gss_OID mech GSSEAP_UNUSED,
1006                         OM_uint32 reqFlags GSSEAP_UNUSED,
1007                         OM_uint32 timeReq GSSEAP_UNUSED,
1008                         gss_channel_bindings_t chanBindings GSSEAP_UNUSED,
1009                         gss_buffer_t inputToken,
1010                         gss_buffer_t outputToken GSSEAP_UNUSED,
1011                         OM_uint32 *smFlags GSSEAP_UNUSED)
1012 {
1013     OM_uint32 major;
1014
1015     major = gssEapVerifyTokenMIC(minor, ctx, inputToken);
1016     if (GSS_ERROR(major))
1017         return major;
1018
1019     GSSEAP_SM_TRANSITION(ctx, GSSEAP_STATE_ESTABLISHED);
1020
1021     *minor = 0;
1022
1023     return GSS_S_COMPLETE;
1024 }
1025
1026 static struct gss_eap_sm eapGssInitiatorSm[] = {
1027     {
1028         ITOK_TYPE_CONTEXT_ERR,
1029         ITOK_TYPE_NONE,
1030         GSSEAP_STATE_ALL & ~(GSSEAP_STATE_INITIAL),
1031         0,
1032         eapGssSmInitError
1033     },
1034     {
1035         ITOK_TYPE_ACCEPTOR_NAME_RESP,
1036         ITOK_TYPE_ACCEPTOR_NAME_REQ,
1037         GSSEAP_STATE_INITIAL | GSSEAP_STATE_AUTHENTICATE,
1038         0,
1039         eapGssSmInitAcceptorName
1040     },
1041 #ifdef GSSEAP_DEBUG
1042     {
1043         ITOK_TYPE_NONE,
1044         ITOK_TYPE_VENDOR_INFO,
1045         GSSEAP_STATE_INITIAL,
1046         0,
1047         eapGssSmInitVendorInfo
1048     },
1049 #endif
1050 #ifdef GSSEAP_ENABLE_REAUTH
1051     {
1052         ITOK_TYPE_REAUTH_RESP,
1053         ITOK_TYPE_REAUTH_REQ,
1054         GSSEAP_STATE_INITIAL | GSSEAP_STATE_REAUTHENTICATE,
1055         0,
1056         eapGssSmInitGssReauth
1057     },
1058 #endif
1059     {
1060         ITOK_TYPE_NONE,
1061         ITOK_TYPE_NONE,
1062 #ifdef GSSEAP_ENABLE_REAUTH
1063         GSSEAP_STATE_REAUTHENTICATE |
1064 #endif
1065         GSSEAP_STATE_INITIAL,
1066         SM_ITOK_FLAG_REQUIRED,
1067         eapGssSmInitIdentity
1068     },
1069     {
1070         ITOK_TYPE_EAP_REQ,
1071         ITOK_TYPE_EAP_RESP,
1072         GSSEAP_STATE_AUTHENTICATE,
1073         SM_ITOK_FLAG_REQUIRED,
1074         eapGssSmInitAuthenticate
1075     },
1076     {
1077         ITOK_TYPE_NONE,
1078         ITOK_TYPE_GSS_FLAGS,
1079         GSSEAP_STATE_INITIATOR_EXTS,
1080         0,
1081         eapGssSmInitGssFlags
1082     },
1083     {
1084         ITOK_TYPE_NONE,
1085         ITOK_TYPE_GSS_CHANNEL_BINDINGS,
1086         GSSEAP_STATE_INITIATOR_EXTS,
1087         SM_ITOK_FLAG_REQUIRED,
1088         eapGssSmInitGssChannelBindings
1089     },
1090     {
1091         ITOK_TYPE_NONE,
1092         ITOK_TYPE_INITIATOR_MIC,
1093         GSSEAP_STATE_INITIATOR_EXTS,
1094         SM_ITOK_FLAG_REQUIRED,
1095         eapGssSmInitInitiatorMIC
1096     },
1097 #ifdef GSSEAP_ENABLE_REAUTH
1098     {
1099         ITOK_TYPE_REAUTH_CREDS,
1100         ITOK_TYPE_NONE,
1101         GSSEAP_STATE_ACCEPTOR_EXTS,
1102         0,
1103         eapGssSmInitReauthCreds
1104     },
1105 #endif
1106     /* other extensions go here */
1107     {
1108         ITOK_TYPE_ACCEPTOR_MIC,
1109         ITOK_TYPE_NONE,
1110         GSSEAP_STATE_ACCEPTOR_EXTS,
1111         SM_ITOK_FLAG_REQUIRED,
1112         eapGssSmInitAcceptorMIC
1113     }
1114 };
1115
1116 OM_uint32
1117 gssEapInitSecContext(OM_uint32 *minor,
1118                      gss_cred_id_t cred,
1119                      gss_ctx_id_t ctx,
1120                      gss_name_t target_name,
1121                      gss_OID mech_type,
1122                      OM_uint32 req_flags,
1123                      OM_uint32 time_req,
1124                      gss_channel_bindings_t input_chan_bindings,
1125                      gss_buffer_t input_token,
1126                      gss_OID *actual_mech_type,
1127                      gss_buffer_t output_token,
1128                      OM_uint32 *ret_flags,
1129                      OM_uint32 *time_rec)
1130 {
1131     OM_uint32 major, tmpMinor;
1132     int initialContextToken = (ctx->mechanismUsed == GSS_C_NO_OID);
1133
1134     /*
1135      * XXX is acquiring the credential lock here necessary? The password is
1136      * mutable but the contract could specify that this is not updated whilst
1137      * a context is being initialized.
1138      */
1139     if (cred != GSS_C_NO_CREDENTIAL)
1140         GSSEAP_MUTEX_LOCK(&cred->mutex);
1141
1142     if (ctx->cred == GSS_C_NO_CREDENTIAL) {
1143         major = gssEapResolveInitiatorCred(minor, cred, target_name, &ctx->cred);
1144         if (GSS_ERROR(major))
1145             goto cleanup;
1146
1147         GSSEAP_ASSERT(ctx->cred != GSS_C_NO_CREDENTIAL);
1148     }
1149
1150     GSSEAP_MUTEX_LOCK(&ctx->cred->mutex);
1151
1152     GSSEAP_ASSERT(ctx->cred->flags & CRED_FLAG_RESOLVED);
1153     GSSEAP_ASSERT(ctx->cred->flags & CRED_FLAG_INITIATE);
1154
1155     if (initialContextToken) {
1156         major = initBegin(minor, ctx, target_name, mech_type,
1157                           req_flags, time_req, input_chan_bindings);
1158         if (GSS_ERROR(major))
1159             goto cleanup;
1160     }
1161
1162     major = gssEapSmStep(minor,
1163                          cred,
1164                          ctx,
1165                          target_name,
1166                          mech_type,
1167                          req_flags,
1168                          time_req,
1169                          input_chan_bindings,
1170                          input_token,
1171                          output_token,
1172                          eapGssInitiatorSm,
1173                          sizeof(eapGssInitiatorSm) / sizeof(eapGssInitiatorSm[0]));
1174     if (GSS_ERROR(major))
1175         goto cleanup;
1176
1177     if (actual_mech_type != NULL) {
1178         OM_uint32 tmpMajor;
1179
1180         tmpMajor = gssEapCanonicalizeOid(&tmpMinor, ctx->mechanismUsed, 0, actual_mech_type);
1181         if (GSS_ERROR(tmpMajor)) {
1182             major = tmpMajor;
1183             *minor = tmpMinor;
1184             goto cleanup;
1185         }
1186     }
1187
1188     if (ret_flags != NULL)
1189         *ret_flags = ctx->gssFlags;
1190
1191     if (time_rec != NULL)
1192         gssEapContextTime(&tmpMinor, ctx, time_rec);
1193
1194     GSSEAP_ASSERT(CTX_IS_ESTABLISHED(ctx) || major == GSS_S_CONTINUE_NEEDED);
1195
1196 cleanup:
1197     if (cred != GSS_C_NO_CREDENTIAL)
1198         GSSEAP_MUTEX_UNLOCK(&cred->mutex);
1199     if (ctx->cred != GSS_C_NO_CREDENTIAL)
1200         GSSEAP_MUTEX_UNLOCK(&ctx->cred->mutex);
1201
1202     return major;
1203 }
1204
1205 OM_uint32 GSSAPI_CALLCONV
1206 gss_init_sec_context(OM_uint32 *minor,
1207                      gss_cred_id_t cred,
1208                      gss_ctx_id_t *context_handle,
1209                      gss_name_t target_name,
1210                      gss_OID mech_type,
1211                      OM_uint32 req_flags,
1212                      OM_uint32 time_req,
1213                      gss_channel_bindings_t input_chan_bindings,
1214                      gss_buffer_t input_token,
1215                      gss_OID *actual_mech_type,
1216                      gss_buffer_t output_token,
1217                      OM_uint32 *ret_flags,
1218                      OM_uint32 *time_rec)
1219 {
1220     OM_uint32 major, tmpMinor;
1221     gss_ctx_id_t ctx = *context_handle;
1222
1223     *minor = 0;
1224
1225     output_token->length = 0;
1226     output_token->value = NULL;
1227
1228     if (ctx == GSS_C_NO_CONTEXT) {
1229         if (input_token != GSS_C_NO_BUFFER && input_token->length != 0) {
1230             *minor = GSSEAP_WRONG_SIZE;
1231             return GSS_S_DEFECTIVE_TOKEN;
1232         }
1233
1234         major = gssEapAllocContext(minor, &ctx);
1235         if (GSS_ERROR(major))
1236             return major;
1237
1238         ctx->flags |= CTX_FLAG_INITIATOR;
1239
1240         *context_handle = ctx;
1241     }
1242
1243     GSSEAP_MUTEX_LOCK(&ctx->mutex);
1244
1245     major = gssEapInitSecContext(minor,
1246                                  cred,
1247                                  ctx,
1248                                  target_name,
1249                                  mech_type,
1250                                  req_flags,
1251                                  time_req,
1252                                  input_chan_bindings,
1253                                  input_token,
1254                                  actual_mech_type,
1255                                  output_token,
1256                                  ret_flags,
1257                                  time_rec);
1258
1259     GSSEAP_MUTEX_UNLOCK(&ctx->mutex);
1260
1261     if (GSS_ERROR(major))
1262         gssEapReleaseContext(&tmpMinor, context_handle);
1263
1264     return major;
1265 }