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