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