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