Updated through tag hostap_2_5 from git://w1.fi/hostap.git
[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     case EAPOL_eapTriggerStart:
82         flag = CTX_FLAG_EAP_TRIGGER_START;
83         break;
84     }
85
86     return flag;
87 }
88
89 static struct eap_peer_config *
90 peerGetConfig(void *ctx)
91 {
92     gss_ctx_id_t gssCtx = (gss_ctx_id_t)ctx;
93
94     return &gssCtx->initiatorCtx.eapPeerConfig;
95 }
96
97 static Boolean
98 peerGetBool(void *data, enum eapol_bool_var variable)
99 {
100     gss_ctx_id_t ctx = data;
101     OM_uint32 flag;
102
103     if (ctx == GSS_C_NO_CONTEXT)
104         return FALSE;
105
106     flag = policyVariableToFlag(variable);
107
108     return ((ctx->flags & flag) != 0);
109 }
110
111 static void
112 peerSetBool(void *data, enum eapol_bool_var variable,
113             Boolean value)
114 {
115     gss_ctx_id_t ctx = data;
116     OM_uint32 flag;
117
118     if (ctx == GSS_C_NO_CONTEXT)
119         return;
120
121     flag = policyVariableToFlag(variable);
122
123     if (value)
124         ctx->flags |= flag;
125     else
126         ctx->flags &= ~(flag);
127 }
128
129 static unsigned int
130 peerGetInt(void *data, enum eapol_int_var variable)
131 {
132     gss_ctx_id_t ctx = data;
133
134     if (ctx == GSS_C_NO_CONTEXT)
135         return FALSE;
136
137     GSSEAP_ASSERT(CTX_IS_INITIATOR(ctx));
138
139     switch (variable) {
140     case EAPOL_idleWhile:
141         return ctx->initiatorCtx.idleWhile;
142         break;
143     }
144
145     return 0;
146 }
147
148 static void
149 peerSetInt(void *data, enum eapol_int_var variable,
150            unsigned int value)
151 {
152     gss_ctx_id_t ctx = data;
153
154     if (ctx == GSS_C_NO_CONTEXT)
155         return;
156
157     GSSEAP_ASSERT(CTX_IS_INITIATOR(ctx));
158
159     switch (variable) {
160     case EAPOL_idleWhile:
161         ctx->initiatorCtx.idleWhile = value;
162         break;
163     }
164 }
165
166 static struct wpabuf *
167 peerGetEapReqData(void *ctx)
168 {
169     gss_ctx_id_t gssCtx = (gss_ctx_id_t)ctx;
170
171     return &gssCtx->initiatorCtx.reqData;
172 }
173
174 static void
175 peerSetConfigBlob(void *ctx GSSEAP_UNUSED,
176                   struct wpa_config_blob *blob GSSEAP_UNUSED)
177 {
178 }
179
180 static const struct wpa_config_blob *
181 peerGetConfigBlob(void *ctx,
182                   const char *name)
183 {
184     gss_ctx_id_t gssCtx = (gss_ctx_id_t)ctx;
185     size_t index;
186
187     if (strcmp(name, "client-cert") == 0)
188         index = CONFIG_BLOB_CLIENT_CERT;
189     else if (strcmp(name, "private-key") == 0)
190         index = CONFIG_BLOB_PRIVATE_KEY;
191     else if (strcmp(name, "ca-cert") == 0)
192         index = CONFIG_BLOB_CA_CERT;
193     else
194         return NULL;
195
196     return &gssCtx->initiatorCtx.configBlobs[index];
197 }
198
199 static void
200 peerNotifyPending(void *ctx GSSEAP_UNUSED)
201 {
202 }
203
204 static struct eapol_callbacks gssEapPolicyCallbacks = {
205     peerGetConfig,
206     peerGetBool,
207     peerSetBool,
208     peerGetInt,
209     peerSetInt,
210     peerGetEapReqData,
211     peerSetConfigBlob,
212     peerGetConfigBlob,
213     peerNotifyPending,
214 };
215
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     
384     GSSEAP_ASSERT(cred->name != GSS_C_NO_NAME);
385
386     if ((cred->name->flags & (NAME_FLAG_NAI | NAME_FLAG_SERVICE)) == 0) {
387         *minor = GSSEAP_BAD_INITIATOR_NAME;
388         return GSS_S_BAD_NAME;
389     }
390
391     /* identity */
392     major = gssEapDisplayName(minor, cred->name, &identity, NULL);
393     if (GSS_ERROR(major))
394         return major;
395
396     eapPeerConfig->identity = (unsigned char *)identity.value;
397     eapPeerConfig->identity_len = identity.length;
398
399     krbPrincRealmToGssBuffer(cred->name->krbPrincipal, &realm);
400
401     /* anonymous_identity */
402     eapPeerConfig->anonymous_identity = GSSEAP_MALLOC(realm.length + 2);
403     if (eapPeerConfig->anonymous_identity == NULL) {
404         *minor = ENOMEM;
405         return GSS_S_FAILURE;
406     }
407
408     eapPeerConfig->anonymous_identity[0] = '@';
409     memcpy(eapPeerConfig->anonymous_identity + 1, realm.value, realm.length);
410     eapPeerConfig->anonymous_identity[1 + realm.length] = '\0';
411     eapPeerConfig->anonymous_identity_len = 1 + realm.length;
412
413     /* password */
414     if ((cred->flags & CRED_FLAG_CERTIFICATE) == 0) {
415         eapPeerConfig->password = (unsigned char *)cred->password.value;
416         eapPeerConfig->password_len = cred->password.length;
417     }
418
419     /* certs */
420     eapPeerConfig->ca_cert = (unsigned char *)cred->caCertificate.value;
421     eapPeerConfig->subject_match = (unsigned char *)cred->subjectNameConstraint.value;
422     eapPeerConfig->altsubject_match = (unsigned char *)cred->subjectAltNameConstraint.value;
423     configBlobs[CONFIG_BLOB_CA_CERT].data = cred->caCertificateBlob.value;
424     configBlobs[CONFIG_BLOB_CA_CERT].len = cred->caCertificateBlob.length;
425
426     /* eap channel binding */
427     if (ctx->initiatorCtx.chbindData != NULL) {
428         struct eap_peer_chbind_config *chbind_config =
429             (struct eap_peer_chbind_config *)GSSEAP_MALLOC(sizeof(struct eap_peer_chbind_config));
430         if (chbind_config == NULL) {
431             *minor = ENOMEM;
432             return GSS_S_FAILURE;
433         }
434
435         chbind_config->req_data = wpabuf_mhead_u8(ctx->initiatorCtx.chbindData);
436         chbind_config->req_data_len = wpabuf_len(ctx->initiatorCtx.chbindData);
437         chbind_config->nsid = CHBIND_NSID_RADIUS;
438         chbind_config->response_cb = &peerProcessChbindResponse;
439         chbind_config->ctx = ctx;
440         eapPeerConfig->chbind_config = chbind_config;
441         eapPeerConfig->chbind_config_len = 1;
442     } else {
443         eapPeerConfig->chbind_config = NULL;
444         eapPeerConfig->chbind_config_len = 0;
445     }
446     if (cred->flags & CRED_FLAG_CERTIFICATE) {
447         /*
448          * CRED_FLAG_CONFIG_BLOB is an internal flag which will be used in the
449          * future to directly pass certificate and private key data to the
450          * EAP implementation, rather than an indirected string pointer.
451          */
452         if (cred->flags & CRED_FLAG_CONFIG_BLOB) {
453             eapPeerConfig->client_cert = (unsigned char *)"blob://client-cert";
454             configBlobs[CONFIG_BLOB_CLIENT_CERT].data = cred->clientCertificate.value;
455             configBlobs[CONFIG_BLOB_CLIENT_CERT].len  = cred->clientCertificate.length;
456
457             eapPeerConfig->client_cert = (unsigned char *)"blob://private-key";
458             configBlobs[CONFIG_BLOB_PRIVATE_KEY].data = cred->clientCertificate.value;
459             configBlobs[CONFIG_BLOB_PRIVATE_KEY].len  = cred->privateKey.length;
460         } else {
461             eapPeerConfig->client_cert = (unsigned char *)cred->clientCertificate.value;
462             eapPeerConfig->private_key = (unsigned char *)cred->privateKey.value;
463         }
464         eapPeerConfig->private_key_passwd = (char *)cred->password.value;
465     }
466
467     *minor = 0;
468     return GSS_S_COMPLETE;
469 }
470
471 static OM_uint32
472 peerConfigFree(OM_uint32 *minor,
473                gss_ctx_id_t ctx)
474 {
475     struct eap_peer_config *eapPeerConfig = &ctx->initiatorCtx.eapPeerConfig;
476
477     if (eapPeerConfig->identity != NULL) {
478         GSSEAP_FREE(eapPeerConfig->identity);
479         eapPeerConfig->identity = NULL;
480         eapPeerConfig->identity_len = 0;
481     }
482
483     if (eapPeerConfig->anonymous_identity != NULL) {
484         GSSEAP_FREE(eapPeerConfig->anonymous_identity);
485         eapPeerConfig->anonymous_identity = NULL;
486         eapPeerConfig->anonymous_identity_len = 0;
487     }
488
489     *minor = 0;
490     return GSS_S_COMPLETE;
491 }
492
493 /*
494  * Mark an initiator context as ready for cryptographic operations
495  */
496 static OM_uint32
497 initReady(OM_uint32 *minor, gss_ctx_id_t ctx)
498 {
499     OM_uint32 major;
500     const unsigned char *key;
501     size_t keyLength;
502
503     /* Cache encryption type derived from selected mechanism OID */
504     major = gssEapOidToEnctype(minor, ctx->mechanismUsed, &ctx->encryptionType);
505     if (GSS_ERROR(major))
506         return major;
507
508     if (!eap_key_available(ctx->initiatorCtx.eap)) {
509         *minor = GSSEAP_KEY_UNAVAILABLE;
510         return GSS_S_UNAVAILABLE;
511     }
512
513     key = eap_get_eapKeyData(ctx->initiatorCtx.eap, &keyLength);
514
515     if (keyLength < EAP_EMSK_LEN) {
516         *minor = GSSEAP_KEY_TOO_SHORT;
517         return GSS_S_UNAVAILABLE;
518     }
519
520     major = gssEapDeriveRfc3961Key(minor,
521                                    &key[EAP_EMSK_LEN / 2],
522                                    EAP_EMSK_LEN / 2,
523                                    ctx->encryptionType,
524                                    &ctx->rfc3961Key);
525        if (GSS_ERROR(major))
526            return major;
527
528     major = rfc3961ChecksumTypeForKey(minor, &ctx->rfc3961Key,
529                                       &ctx->checksumType);
530     if (GSS_ERROR(major))
531         return major;
532
533     major = sequenceInit(minor,
534                          &ctx->seqState,
535                          ctx->recvSeq,
536                          ((ctx->gssFlags & GSS_C_REPLAY_FLAG) != 0),
537                          ((ctx->gssFlags & GSS_C_SEQUENCE_FLAG) != 0),
538                          TRUE);
539     if (GSS_ERROR(major))
540         return major;
541
542     *minor = 0;
543     return GSS_S_COMPLETE;
544 }
545
546 static OM_uint32
547 initBegin(OM_uint32 *minor,
548           gss_ctx_id_t ctx,
549           gss_name_t target,
550           gss_OID mech,
551           OM_uint32 reqFlags GSSEAP_UNUSED,
552           OM_uint32 timeReq,
553           gss_channel_bindings_t chanBindings GSSEAP_UNUSED)
554 {
555     OM_uint32 major;
556     gss_cred_id_t cred = ctx->cred;
557
558     GSSEAP_ASSERT(cred != GSS_C_NO_CREDENTIAL);
559
560     if (cred->expiryTime)
561         ctx->expiryTime = cred->expiryTime;
562     else if (timeReq == 0 || timeReq == GSS_C_INDEFINITE)
563         ctx->expiryTime = 0;
564     else
565         ctx->expiryTime = time(NULL) + timeReq;
566
567     /*
568      * The credential mutex protects its name, however we need to
569      * explicitly lock the acceptor name (unlikely as it may be
570      * that it has attributes set on it).
571      */
572     major = gssEapDuplicateName(minor, cred->name, &ctx->initiatorName);
573     if (GSS_ERROR(major))
574         return major;
575
576     if (target != GSS_C_NO_NAME) {
577         GSSEAP_MUTEX_LOCK(&target->mutex);
578
579         major = gssEapDuplicateName(minor, target, &ctx->acceptorName);
580         if (GSS_ERROR(major)) {
581             GSSEAP_MUTEX_UNLOCK(&target->mutex);
582             return major;
583         }
584
585         GSSEAP_MUTEX_UNLOCK(&target->mutex);
586     }
587
588     major = gssEapCanonicalizeOid(minor,
589                                   mech,
590                                   OID_FLAG_NULL_VALID | OID_FLAG_MAP_NULL_TO_DEFAULT_MECH,
591                                   &ctx->mechanismUsed);
592     if (GSS_ERROR(major))
593         return major;
594
595     /* If credentials were provided, check they're usable with this mech */
596     if (!gssEapCredAvailable(cred, ctx->mechanismUsed)) {
597         *minor = GSSEAP_CRED_MECH_MISMATCH;
598         return GSS_S_BAD_MECH;
599     }
600
601     *minor = 0;
602     return GSS_S_COMPLETE;
603 }
604
605 static OM_uint32
606 eapGssSmInitError(OM_uint32 *minor,
607                   gss_cred_id_t cred GSSEAP_UNUSED,
608                   gss_ctx_id_t ctx GSSEAP_UNUSED,
609                   gss_name_t target GSSEAP_UNUSED,
610                   gss_OID mech GSSEAP_UNUSED,
611                   OM_uint32 reqFlags GSSEAP_UNUSED,
612                   OM_uint32 timeReq GSSEAP_UNUSED,
613                   gss_channel_bindings_t chanBindings GSSEAP_UNUSED,
614                   gss_buffer_t inputToken,
615                   gss_buffer_t outputToken GSSEAP_UNUSED,
616                   OM_uint32 *smFlags GSSEAP_UNUSED)
617 {
618     OM_uint32 major;
619     unsigned char *p;
620
621     if (inputToken->length < 8) {
622         *minor = GSSEAP_TOK_TRUNC;
623         return GSS_S_DEFECTIVE_TOKEN;
624     }
625
626     p = (unsigned char *)inputToken->value;
627
628     major = load_uint32_be(&p[0]);
629     *minor =  load_uint32_be(&p[4]);
630     if ((*minor >0) && (*minor < 128))
631       * minor += ERROR_TABLE_BASE_eapg;
632     else *minor = 0;
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     gssEapTraceStatus( "gss_init_sec_context", major, *minor);
1369     return major;
1370 }