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