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