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