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