EAP Channel binding
[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 /* */
201 static u8 componentToAttrMap[] =
202 {
203     128, /* GSS-Acceptor-Service-Name */
204     129, /* GSS-Acceptor-Host-Name    */
205     130  /* GSS-Acceptor-Service-specific */
206 };
207 #define CHBIND_REALM_FLAG (1 << sizeof(componentToAttrMap))
208
209 static OM_uint32
210 peerInitEapChannelBinding(OM_uint32 *minor, gss_ctx_id_t ctx)
211 {
212     struct wpabuf *buf;
213     radius_vendor_attr vendor_attr;
214     int component, components = 0;
215     unsigned int requested = 0;
216     krb5_principal princ;
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     if (KRB_PRINC_LENGTH(princ) > sizeof(componentToAttrMap)) {
227         *minor = GSSEAP_BAD_ACCEPTOR_NAME;
228         return GSS_S_BAD_NAME;
229     }
230
231     /* allocate a buffer to hold channel binding data to be used by libeap */
232     buf = wpabuf_alloc(256);
233     if (!buf) {
234         *minor = ENOMEM;
235         return GSS_S_FAILURE;
236     }
237
238     for (component=0; component < KRB_PRINC_LENGTH(princ); component++)
239     {
240         krb5_data* name_data = KRB_PRINC_COMPONENT(princ, component);
241         if (name_data->length > 0)
242         {
243             components++;
244             vendor_attr = radius_vendor_attr_start(buf, VENDORPEC_UKERNA);
245             vendor_attr = radius_vendor_attr_add_subtype(vendor_attr,
246                 componentToAttrMap[component],
247                 name_data->data,
248                 name_data->length);
249             requested |= 1<<component;
250             vendor_attr = radius_vendor_attr_finish(vendor_attr);
251         }
252     }
253
254     if (KRB_PRINC_REALM(princ) && (KRB_PRINC_REALM(princ)->length > 0)) {
255         components++;
256         requested |= CHBIND_REALM_FLAG;
257         vendor_attr = radius_vendor_attr_start(buf, VENDORPEC_UKERNA);
258         vendor_attr = radius_vendor_attr_add_subtype(vendor_attr, 131,
259                                             KRB_PRINC_REALM(princ)->data,
260                                             KRB_PRINC_REALM(princ)->length);
261         vendor_attr = radius_vendor_attr_finish(vendor_attr);
262     }
263
264     if ((components==0) || (vendor_attr == VENDOR_ATTR_INVALID)) {
265         wpabuf_free(buf);
266         *minor = GSSEAP_BAD_ACCEPTOR_NAME;
267         return GSS_S_BAD_NAME;
268     }
269     /* @TODO: realloc buf to actual size? */
270     ctx->initiatorCtx.chbindData = buf;
271     ctx->initiatorCtx.chbindReqFlags = requested;
272     return GSS_S_COMPLETE;
273 }
274
275 static void
276 peerProcessChbindResponse(void *context, int code, int nsid,
277                           u8 *data, size_t len)
278 {
279     radius_parser msg, vendor_specific;
280     gss_ctx_id_t ctx = (gss_ctx_id_t )context;
281     void *vsadata;
282     u8 type;
283     u32 vendor_id;
284     u32 accepted = 0;
285     size_t vsadata_len;
286     int i;
287
288     if (nsid != CHBIND_NSID_RADIUS)
289         return;
290     msg = radius_parser_start(data, len);
291     if (!msg)
292         return;
293     while (radius_parser_parse_tlv(msg, &type, &vendor_id, &vsadata,
294                                    &vsadata_len) == 0) {
295         void *unused_data;
296         size_t unused_len;
297         u8 vendor_type;
298
299         if ((type != RADIUS_ATTR_VENDOR_SPECIFIC) ||
300             (vendor_id != VENDORPEC_UKERNA))
301             continue;
302         vendor_specific = radius_parser_start(vsadata, vsadata_len);
303         if (!vendor_specific)
304             continue;
305         while (radius_parser_parse_vendor_specific(vendor_specific,
306                                                    &vendor_type,
307                                                    &unused_data,
308                                                    &unused_len) == 0) {
309             if (vendor_type == 131) {
310                 accepted |= CHBIND_REALM_FLAG;
311             } else {
312                 for (i=0; i<sizeof(componentToAttrMap); i++) {
313                     if (componentToAttrMap[i]==vendor_type) {
314                         accepted |= 1<<i;
315                         break;
316                     }
317                 }
318             }
319         }
320         radius_parser_finish(vendor_specific);
321         break;
322     }
323     radius_parser_finish(msg);
324     if ((code == CHBIND_CODE_SUCCESS) &&
325         (accepted == ctx->initiatorCtx.chbindReqFlags)) {
326         /* Accepted! */
327     } else {
328         /* log failures? */
329     }
330 }
331
332 static OM_uint32
333 peerConfigInit(OM_uint32 *minor, gss_ctx_id_t ctx)
334 {
335     OM_uint32 major;
336     krb5_context krbContext;
337     struct eap_peer_config *eapPeerConfig = &ctx->initiatorCtx.eapPeerConfig;
338     gss_buffer_desc identity = GSS_C_EMPTY_BUFFER;
339     gss_buffer_desc realm = GSS_C_EMPTY_BUFFER;
340     gss_cred_id_t cred = ctx->cred;
341
342     eapPeerConfig->identity = NULL;
343     eapPeerConfig->identity_len = 0;
344     eapPeerConfig->anonymous_identity = NULL;
345     eapPeerConfig->anonymous_identity_len = 0;
346     eapPeerConfig->password = NULL;
347     eapPeerConfig->password_len = 0;
348
349     GSSEAP_ASSERT(cred != GSS_C_NO_CREDENTIAL);
350
351     GSSEAP_KRB_INIT(&krbContext);
352
353     eapPeerConfig->fragment_size = 1024;
354 #ifdef GSSEAP_DEBUG
355     wpa_debug_level = 0;
356 #endif
357
358     GSSEAP_ASSERT(cred->name != GSS_C_NO_NAME);
359
360     if ((cred->name->flags & (NAME_FLAG_NAI | NAME_FLAG_SERVICE)) == 0) {
361         *minor = GSSEAP_BAD_INITIATOR_NAME;
362         return GSS_S_BAD_NAME;
363     }
364
365     /* identity */
366     major = gssEapDisplayName(minor, cred->name, &identity, NULL);
367     if (GSS_ERROR(major))
368         return major;
369
370     eapPeerConfig->identity = (unsigned char *)identity.value;
371     eapPeerConfig->identity_len = identity.length;
372
373     krbPrincRealmToGssBuffer(cred->name->krbPrincipal, &realm);
374
375     /* anonymous_identity */
376     eapPeerConfig->anonymous_identity = GSSEAP_MALLOC(realm.length + 2);
377     if (eapPeerConfig->anonymous_identity == NULL) {
378         *minor = ENOMEM;
379         return GSS_S_FAILURE;
380     }
381
382     eapPeerConfig->anonymous_identity[0] = '@';
383     memcpy(eapPeerConfig->anonymous_identity + 1, realm.value, realm.length);
384     eapPeerConfig->anonymous_identity[1 + realm.length] = '\0';
385     eapPeerConfig->anonymous_identity_len = 1 + realm.length;
386
387     /* password */
388     eapPeerConfig->password = (unsigned char *)cred->password.value;
389     eapPeerConfig->password_len = cred->password.length;
390
391     /* certs */
392     eapPeerConfig->ca_cert = (unsigned char *)cred->caCertificate.value;
393     eapPeerConfig->subject_match = (unsigned char *)cred->subjectNameConstraint.value;
394     eapPeerConfig->altsubject_match = (unsigned char *)cred->subjectAltNameConstraint.value;
395
396     /* eap channel binding */
397     if (ctx->initiatorCtx.chbindData)
398     {
399         struct eap_peer_chbind_config *chbind_config =
400             (struct eap_peer_chbind_config *)
401             GSSEAP_MALLOC(sizeof(struct eap_peer_chbind_config));
402         if (chbind_config == NULL) {
403             *minor = ENOMEM;
404             return GSS_S_FAILURE;
405         }
406
407         chbind_config->req_data = wpabuf_mhead_u8(ctx->initiatorCtx.chbindData);
408         chbind_config->req_data_len = wpabuf_len(ctx->initiatorCtx.chbindData);
409         chbind_config->nsid = CHBIND_NSID_RADIUS;
410         chbind_config->response_cb = &peerProcessChbindResponse;
411         chbind_config->ctx = ctx;
412         eapPeerConfig->chbind_config = chbind_config;
413         eapPeerConfig->chbind_config_len = 1;
414     } else {
415         eapPeerConfig->chbind_config = NULL;
416         eapPeerConfig->chbind_config_len = 0;
417     }
418     *minor = 0;
419     return GSS_S_COMPLETE;
420 }
421
422 static OM_uint32
423 peerConfigFree(OM_uint32 *minor,
424                gss_ctx_id_t ctx)
425 {
426     struct eap_peer_config *eapPeerConfig = &ctx->initiatorCtx.eapPeerConfig;
427
428     if (eapPeerConfig->identity != NULL) {
429         GSSEAP_FREE(eapPeerConfig->identity);
430         eapPeerConfig->identity = NULL;
431         eapPeerConfig->identity_len = 0;
432     }
433
434     if (eapPeerConfig->anonymous_identity != NULL) {
435         GSSEAP_FREE(eapPeerConfig->anonymous_identity);
436         eapPeerConfig->anonymous_identity = NULL;
437         eapPeerConfig->anonymous_identity_len = 0;
438     }
439
440     *minor = 0;
441     return GSS_S_COMPLETE;
442 }
443
444 /*
445  * Mark an initiator context as ready for cryptographic operations
446  */
447 static OM_uint32
448 initReady(OM_uint32 *minor, gss_ctx_id_t ctx, OM_uint32 reqFlags)
449 {
450     OM_uint32 major;
451     const unsigned char *key;
452     size_t keyLength;
453
454 #if 1
455     /* XXX actually check for mutual auth */
456     if (reqFlags & GSS_C_MUTUAL_FLAG)
457         ctx->gssFlags |= GSS_C_MUTUAL_FLAG;
458 #endif
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     {
746         major = peerInitEapChannelBinding(minor, ctx);
747         if (GSS_ERROR(major))
748             return major;
749     }
750
751     return GSS_S_CONTINUE_NEEDED;
752 }
753
754 static OM_uint32
755 eapGssSmInitIdentity(OM_uint32 *minor,
756                      gss_cred_id_t cred GSSEAP_UNUSED,
757                      gss_ctx_id_t ctx,
758                      gss_name_t target GSSEAP_UNUSED,
759                      gss_OID mech GSSEAP_UNUSED,
760                      OM_uint32 reqFlags GSSEAP_UNUSED,
761                      OM_uint32 timeReq GSSEAP_UNUSED,
762                      gss_channel_bindings_t chanBindings GSSEAP_UNUSED,
763                      gss_buffer_t inputToken GSSEAP_UNUSED,
764                      gss_buffer_t outputToken GSSEAP_UNUSED,
765                      OM_uint32 *smFlags)
766 {
767     struct eap_config eapConfig;
768
769 #ifdef GSSEAP_ENABLE_REAUTH
770     if (GSSEAP_SM_STATE(ctx) == GSSEAP_STATE_REAUTHENTICATE) {
771         OM_uint32 tmpMinor;
772
773         /* server didn't support reauthentication, sent EAP request */
774         gssDeleteSecContext(&tmpMinor, &ctx->reauthCtx, GSS_C_NO_BUFFER);
775         ctx->flags &= ~(CTX_FLAG_KRB_REAUTH);
776         GSSEAP_SM_TRANSITION(ctx, GSSEAP_STATE_INITIAL);
777     } else
778 #endif
779         *smFlags |= SM_FLAG_FORCE_SEND_TOKEN;
780
781     GSSEAP_ASSERT((ctx->flags & CTX_FLAG_KRB_REAUTH) == 0);
782     GSSEAP_ASSERT(inputToken == GSS_C_NO_BUFFER);
783
784     memset(&eapConfig, 0, sizeof(eapConfig));
785
786     ctx->initiatorCtx.eap = eap_peer_sm_init(ctx,
787                                              &gssEapPolicyCallbacks,
788                                              ctx,
789                                              &eapConfig);
790     if (ctx->initiatorCtx.eap == NULL) {
791         *minor = GSSEAP_PEER_SM_INIT_FAILURE;
792         return GSS_S_FAILURE;
793     }
794
795     ctx->flags |= CTX_FLAG_EAP_RESTART | CTX_FLAG_EAP_PORT_ENABLED;
796
797     /* poke EAP state machine */
798     if (eap_peer_sm_step(ctx->initiatorCtx.eap) != 0) {
799         *minor = GSSEAP_PEER_SM_STEP_FAILURE;
800         return GSS_S_FAILURE;
801     }
802
803     GSSEAP_SM_TRANSITION_NEXT(ctx);
804
805     *minor = 0;
806
807     return GSS_S_CONTINUE_NEEDED;
808 }
809
810 static OM_uint32
811 eapGssSmInitAuthenticate(OM_uint32 *minor,
812                          gss_cred_id_t cred GSSEAP_UNUSED,
813                          gss_ctx_id_t ctx,
814                          gss_name_t target GSSEAP_UNUSED,
815                          gss_OID mech GSSEAP_UNUSED,
816                          OM_uint32 reqFlags GSSEAP_UNUSED,
817                          OM_uint32 timeReq GSSEAP_UNUSED,
818                          gss_channel_bindings_t chanBindings GSSEAP_UNUSED,
819                          gss_buffer_t inputToken GSSEAP_UNUSED,
820                          gss_buffer_t outputToken,
821                          OM_uint32 *smFlags)
822 {
823     OM_uint32 major;
824     OM_uint32 tmpMinor;
825     struct wpabuf *resp = NULL;
826
827     *minor = 0;
828
829     GSSEAP_ASSERT(inputToken != GSS_C_NO_BUFFER);
830
831     major = peerConfigInit(minor, ctx);
832     if (GSS_ERROR(major))
833         goto cleanup;
834
835     GSSEAP_ASSERT(ctx->initiatorCtx.eap != NULL);
836     GSSEAP_ASSERT(ctx->flags & CTX_FLAG_EAP_PORT_ENABLED);
837
838     ctx->flags |= CTX_FLAG_EAP_REQ; /* we have a Request from the acceptor */
839
840     wpabuf_set(&ctx->initiatorCtx.reqData,
841                inputToken->value, inputToken->length);
842
843     major = GSS_S_CONTINUE_NEEDED;
844
845     eap_peer_sm_step(ctx->initiatorCtx.eap);
846     if (ctx->flags & CTX_FLAG_EAP_RESP) {
847         ctx->flags &= ~(CTX_FLAG_EAP_RESP);
848
849         resp = eap_get_eapRespData(ctx->initiatorCtx.eap);
850     } else if (ctx->flags & CTX_FLAG_EAP_SUCCESS) {
851         major = initReady(minor, ctx, reqFlags);
852         if (GSS_ERROR(major))
853             goto cleanup;
854
855         ctx->flags &= ~(CTX_FLAG_EAP_SUCCESS);
856         major = GSS_S_CONTINUE_NEEDED;
857         GSSEAP_SM_TRANSITION_NEXT(ctx);
858     } else if (ctx->flags & CTX_FLAG_EAP_FAIL) {
859         major = GSS_S_DEFECTIVE_CREDENTIAL;
860         *minor = GSSEAP_PEER_AUTH_FAILURE;
861     } else {
862         major = GSS_S_DEFECTIVE_TOKEN;
863         *minor = GSSEAP_PEER_BAD_MESSAGE;
864     }
865
866 cleanup:
867     if (resp != NULL) {
868         OM_uint32 tmpMajor;
869         gss_buffer_desc respBuf;
870
871         GSSEAP_ASSERT(major == GSS_S_CONTINUE_NEEDED);
872
873         respBuf.length = wpabuf_len(resp);
874         respBuf.value = (void *)wpabuf_head(resp);
875
876         tmpMajor = duplicateBuffer(&tmpMinor, &respBuf, outputToken);
877         if (GSS_ERROR(tmpMajor)) {
878             major = tmpMajor;
879             *minor = tmpMinor;
880         }
881
882         *smFlags |= SM_FLAG_OUTPUT_TOKEN_CRITICAL;
883     }
884
885     wpabuf_set(&ctx->initiatorCtx.reqData, NULL, 0);
886     peerConfigFree(&tmpMinor, ctx);
887
888     return major;
889 }
890
891 static OM_uint32
892 eapGssSmInitGssFlags(OM_uint32 *minor,
893                      gss_cred_id_t cred GSSEAP_UNUSED,
894                      gss_ctx_id_t ctx,
895                      gss_name_t target GSSEAP_UNUSED,
896                      gss_OID mech GSSEAP_UNUSED,
897                      OM_uint32 reqFlags GSSEAP_UNUSED,
898                      OM_uint32 timeReq GSSEAP_UNUSED,
899                      gss_channel_bindings_t chanBindings GSSEAP_UNUSED,
900                      gss_buffer_t inputToken GSSEAP_UNUSED,
901                      gss_buffer_t outputToken,
902                      OM_uint32 *smFlags GSSEAP_UNUSED)
903 {
904     unsigned char wireFlags[4];
905     gss_buffer_desc flagsBuf;
906
907     store_uint32_be(ctx->gssFlags & GSSEAP_WIRE_FLAGS_MASK, wireFlags);
908
909     flagsBuf.length = sizeof(wireFlags);
910     flagsBuf.value = wireFlags;
911
912     return duplicateBuffer(minor, &flagsBuf, outputToken);
913 }
914
915 static OM_uint32
916 eapGssSmInitGssChannelBindings(OM_uint32 *minor,
917                                gss_cred_id_t cred GSSEAP_UNUSED,
918                                gss_ctx_id_t ctx,
919                                gss_name_t target GSSEAP_UNUSED,
920                                gss_OID mech GSSEAP_UNUSED,
921                                OM_uint32 reqFlags GSSEAP_UNUSED,
922                                OM_uint32 timeReq GSSEAP_UNUSED,
923                                gss_channel_bindings_t chanBindings,
924                                gss_buffer_t inputToken GSSEAP_UNUSED,
925                                gss_buffer_t outputToken,
926                                OM_uint32 *smFlags)
927 {
928     OM_uint32 major;
929     gss_buffer_desc buffer = GSS_C_EMPTY_BUFFER;
930
931     if (chanBindings != GSS_C_NO_CHANNEL_BINDINGS)
932         buffer = chanBindings->application_data;
933
934     major = gssEapWrap(minor, ctx, TRUE, GSS_C_QOP_DEFAULT,
935                        &buffer, NULL, outputToken);
936     if (GSS_ERROR(major))
937         return major;
938
939     GSSEAP_ASSERT(outputToken->value != NULL);
940
941     *minor = 0;
942     *smFlags |= SM_FLAG_OUTPUT_TOKEN_CRITICAL;
943
944     return GSS_S_CONTINUE_NEEDED;
945 }
946
947 static OM_uint32
948 eapGssSmInitInitiatorMIC(OM_uint32 *minor,
949                          gss_cred_id_t cred GSSEAP_UNUSED,
950                          gss_ctx_id_t ctx,
951                          gss_name_t target GSSEAP_UNUSED,
952                          gss_OID mech GSSEAP_UNUSED,
953                          OM_uint32 reqFlags GSSEAP_UNUSED,
954                          OM_uint32 timeReq GSSEAP_UNUSED,
955                          gss_channel_bindings_t chanBindings GSSEAP_UNUSED,
956                          gss_buffer_t inputToken GSSEAP_UNUSED,
957                          gss_buffer_t outputToken,
958                          OM_uint32 *smFlags)
959 {
960     OM_uint32 major;
961
962     major = gssEapMakeTokenMIC(minor, ctx, outputToken);
963     if (GSS_ERROR(major))
964         return major;
965
966     GSSEAP_SM_TRANSITION_NEXT(ctx);
967
968     *minor = 0;
969     *smFlags |= SM_FLAG_OUTPUT_TOKEN_CRITICAL;
970
971     return GSS_S_CONTINUE_NEEDED;
972 }
973  
974 #ifdef GSSEAP_ENABLE_REAUTH
975 static OM_uint32
976 eapGssSmInitReauthCreds(OM_uint32 *minor,
977                         gss_cred_id_t cred,
978                         gss_ctx_id_t ctx,
979                         gss_name_t target GSSEAP_UNUSED,
980                         gss_OID mech GSSEAP_UNUSED,
981                         OM_uint32 reqFlags GSSEAP_UNUSED,
982                         OM_uint32 timeReq GSSEAP_UNUSED,
983                         gss_channel_bindings_t chanBindings GSSEAP_UNUSED,
984                         gss_buffer_t inputToken,
985                         gss_buffer_t outputToken GSSEAP_UNUSED,
986                         OM_uint32 *smFlags GSSEAP_UNUSED)
987 {
988     OM_uint32 major;
989
990     if (ctx->gssFlags & GSS_C_MUTUAL_FLAG) {
991         major = gssEapStoreReauthCreds(minor, ctx, cred, inputToken);
992         if (GSS_ERROR(major))
993             return major;
994     }
995
996     *minor = 0;
997     return GSS_S_CONTINUE_NEEDED;
998 }
999 #endif /* GSSEAP_ENABLE_REAUTH */
1000
1001 static OM_uint32
1002 eapGssSmInitAcceptorMIC(OM_uint32 *minor,
1003                         gss_cred_id_t cred GSSEAP_UNUSED,
1004                         gss_ctx_id_t ctx,
1005                         gss_name_t target GSSEAP_UNUSED,
1006                         gss_OID mech GSSEAP_UNUSED,
1007                         OM_uint32 reqFlags GSSEAP_UNUSED,
1008                         OM_uint32 timeReq GSSEAP_UNUSED,
1009                         gss_channel_bindings_t chanBindings GSSEAP_UNUSED,
1010                         gss_buffer_t inputToken,
1011                         gss_buffer_t outputToken GSSEAP_UNUSED,
1012                         OM_uint32 *smFlags GSSEAP_UNUSED)
1013 {
1014     OM_uint32 major;
1015
1016     major = gssEapVerifyTokenMIC(minor, ctx, inputToken);
1017     if (GSS_ERROR(major))
1018         return major;
1019
1020     GSSEAP_SM_TRANSITION(ctx, GSSEAP_STATE_ESTABLISHED);
1021
1022     *minor = 0;
1023
1024     return GSS_S_COMPLETE;
1025 }
1026
1027 static struct gss_eap_sm eapGssInitiatorSm[] = {
1028     {
1029         ITOK_TYPE_CONTEXT_ERR,
1030         ITOK_TYPE_NONE,
1031         GSSEAP_STATE_ALL & ~(GSSEAP_STATE_INITIAL),
1032         0,
1033         eapGssSmInitError
1034     },
1035     {
1036         ITOK_TYPE_ACCEPTOR_NAME_RESP,
1037         ITOK_TYPE_ACCEPTOR_NAME_REQ,
1038         GSSEAP_STATE_INITIAL | GSSEAP_STATE_AUTHENTICATE,
1039         0,
1040         eapGssSmInitAcceptorName
1041     },
1042 #ifdef GSSEAP_DEBUG
1043     {
1044         ITOK_TYPE_NONE,
1045         ITOK_TYPE_VENDOR_INFO,
1046         GSSEAP_STATE_INITIAL,
1047         0,
1048         eapGssSmInitVendorInfo
1049     },
1050 #endif
1051 #ifdef GSSEAP_ENABLE_REAUTH
1052     {
1053         ITOK_TYPE_REAUTH_RESP,
1054         ITOK_TYPE_REAUTH_REQ,
1055         GSSEAP_STATE_INITIAL | GSSEAP_STATE_REAUTHENTICATE,
1056         0,
1057         eapGssSmInitGssReauth
1058     },
1059 #endif
1060     {
1061         ITOK_TYPE_NONE,
1062         ITOK_TYPE_NONE,
1063 #ifdef GSSEAP_ENABLE_REAUTH
1064         GSSEAP_STATE_REAUTHENTICATE |
1065 #endif
1066         GSSEAP_STATE_INITIAL,
1067         SM_ITOK_FLAG_REQUIRED,
1068         eapGssSmInitIdentity
1069     },
1070     {
1071         ITOK_TYPE_EAP_REQ,
1072         ITOK_TYPE_EAP_RESP,
1073         GSSEAP_STATE_AUTHENTICATE,
1074         SM_ITOK_FLAG_REQUIRED,
1075         eapGssSmInitAuthenticate
1076     },
1077     {
1078         ITOK_TYPE_NONE,
1079         ITOK_TYPE_GSS_FLAGS,
1080         GSSEAP_STATE_INITIATOR_EXTS,
1081         0,
1082         eapGssSmInitGssFlags
1083     },
1084     {
1085         ITOK_TYPE_NONE,
1086         ITOK_TYPE_GSS_CHANNEL_BINDINGS,
1087         GSSEAP_STATE_INITIATOR_EXTS,
1088         SM_ITOK_FLAG_REQUIRED,
1089         eapGssSmInitGssChannelBindings
1090     },
1091     {
1092         ITOK_TYPE_NONE,
1093         ITOK_TYPE_INITIATOR_MIC,
1094         GSSEAP_STATE_INITIATOR_EXTS,
1095         SM_ITOK_FLAG_REQUIRED,
1096         eapGssSmInitInitiatorMIC
1097     },
1098 #ifdef GSSEAP_ENABLE_REAUTH
1099     {
1100         ITOK_TYPE_REAUTH_CREDS,
1101         ITOK_TYPE_NONE,
1102         GSSEAP_STATE_ACCEPTOR_EXTS,
1103         0,
1104         eapGssSmInitReauthCreds
1105     },
1106 #endif
1107     /* other extensions go here */
1108     {
1109         ITOK_TYPE_ACCEPTOR_MIC,
1110         ITOK_TYPE_NONE,
1111         GSSEAP_STATE_ACCEPTOR_EXTS,
1112         SM_ITOK_FLAG_REQUIRED,
1113         eapGssSmInitAcceptorMIC
1114     }
1115 };
1116
1117 OM_uint32
1118 gssEapInitSecContext(OM_uint32 *minor,
1119                      gss_cred_id_t cred,
1120                      gss_ctx_id_t ctx,
1121                      gss_name_t target_name,
1122                      gss_OID mech_type,
1123                      OM_uint32 req_flags,
1124                      OM_uint32 time_req,
1125                      gss_channel_bindings_t input_chan_bindings,
1126                      gss_buffer_t input_token,
1127                      gss_OID *actual_mech_type,
1128                      gss_buffer_t output_token,
1129                      OM_uint32 *ret_flags,
1130                      OM_uint32 *time_rec)
1131 {
1132     OM_uint32 major, tmpMinor;
1133     int initialContextToken = (ctx->mechanismUsed == GSS_C_NO_OID);
1134
1135     /*
1136      * XXX is acquiring the credential lock here necessary? The password is
1137      * mutable but the contract could specify that this is not updated whilst
1138      * a context is being initialized.
1139      */
1140     if (cred != GSS_C_NO_CREDENTIAL)
1141         GSSEAP_MUTEX_LOCK(&cred->mutex);
1142
1143     if (ctx->cred == GSS_C_NO_CREDENTIAL) {
1144         major = gssEapResolveInitiatorCred(minor, cred, target_name, &ctx->cred);
1145         if (GSS_ERROR(major))
1146             goto cleanup;
1147
1148         GSSEAP_ASSERT(ctx->cred != GSS_C_NO_CREDENTIAL);
1149     }
1150
1151     GSSEAP_MUTEX_LOCK(&ctx->cred->mutex);
1152
1153     GSSEAP_ASSERT(ctx->cred->flags & CRED_FLAG_RESOLVED);
1154     GSSEAP_ASSERT(ctx->cred->flags & CRED_FLAG_INITIATE);
1155
1156     if (initialContextToken) {
1157         major = initBegin(minor, ctx, target_name, mech_type,
1158                           req_flags, time_req, input_chan_bindings);
1159         if (GSS_ERROR(major))
1160             goto cleanup;
1161     }
1162
1163     major = gssEapSmStep(minor,
1164                          cred,
1165                          ctx,
1166                          target_name,
1167                          mech_type,
1168                          req_flags,
1169                          time_req,
1170                          input_chan_bindings,
1171                          input_token,
1172                          output_token,
1173                          eapGssInitiatorSm,
1174                          sizeof(eapGssInitiatorSm) / sizeof(eapGssInitiatorSm[0]));
1175     if (GSS_ERROR(major))
1176         goto cleanup;
1177
1178     if (actual_mech_type != NULL) {
1179         OM_uint32 tmpMajor;
1180
1181         tmpMajor = gssEapCanonicalizeOid(&tmpMinor, ctx->mechanismUsed, 0, actual_mech_type);
1182         if (GSS_ERROR(tmpMajor)) {
1183             major = tmpMajor;
1184             *minor = tmpMinor;
1185             goto cleanup;
1186         }
1187     }
1188     if (ret_flags != NULL)
1189         *ret_flags = ctx->gssFlags;
1190     if (time_rec != NULL)
1191         gssEapContextTime(&tmpMinor, ctx, time_rec);
1192
1193     GSSEAP_ASSERT(CTX_IS_ESTABLISHED(ctx) || major == GSS_S_CONTINUE_NEEDED);
1194
1195 cleanup:
1196     if (cred != GSS_C_NO_CREDENTIAL)
1197         GSSEAP_MUTEX_UNLOCK(&cred->mutex);
1198     if (ctx->cred != GSS_C_NO_CREDENTIAL)
1199         GSSEAP_MUTEX_UNLOCK(&ctx->cred->mutex);
1200
1201     return major;
1202 }
1203
1204 OM_uint32 GSSAPI_CALLCONV
1205 gss_init_sec_context(OM_uint32 *minor,
1206                      gss_cred_id_t cred,
1207                      gss_ctx_id_t *context_handle,
1208                      gss_name_t target_name,
1209                      gss_OID mech_type,
1210                      OM_uint32 req_flags,
1211                      OM_uint32 time_req,
1212                      gss_channel_bindings_t input_chan_bindings,
1213                      gss_buffer_t input_token,
1214                      gss_OID *actual_mech_type,
1215                      gss_buffer_t output_token,
1216                      OM_uint32 *ret_flags,
1217                      OM_uint32 *time_rec)
1218 {
1219     OM_uint32 major, tmpMinor;
1220     gss_ctx_id_t ctx = *context_handle;
1221
1222     *minor = 0;
1223
1224     output_token->length = 0;
1225     output_token->value = NULL;
1226
1227     if (ctx == GSS_C_NO_CONTEXT) {
1228         if (input_token != GSS_C_NO_BUFFER && input_token->length != 0) {
1229             *minor = GSSEAP_WRONG_SIZE;
1230             return GSS_S_DEFECTIVE_TOKEN;
1231         }
1232
1233         major = gssEapAllocContext(minor, &ctx);
1234         if (GSS_ERROR(major))
1235             return major;
1236
1237         ctx->flags |= CTX_FLAG_INITIATOR;
1238
1239         *context_handle = ctx;
1240     }
1241
1242     GSSEAP_MUTEX_LOCK(&ctx->mutex);
1243
1244     major = gssEapInitSecContext(minor,
1245                                  cred,
1246                                  ctx,
1247                                  target_name,
1248                                  mech_type,
1249                                  req_flags,
1250                                  time_req,
1251                                  input_chan_bindings,
1252                                  input_token,
1253                                  actual_mech_type,
1254                                  output_token,
1255                                  ret_flags,
1256                                  time_rec);
1257
1258     GSSEAP_MUTEX_UNLOCK(&ctx->mutex);
1259
1260     if (GSS_ERROR(major))
1261         gssEapReleaseContext(&tmpMinor, context_handle);
1262
1263     return major;
1264 }