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