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