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