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