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