Support EAP-TLS in Moonshot (requires OpenSSL)
[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
40 static OM_uint32
41 policyVariableToFlag(enum eapol_bool_var variable)
42 {
43     OM_uint32 flag = 0;
44
45     switch (variable) {
46     case EAPOL_eapSuccess:
47         flag = CTX_FLAG_EAP_SUCCESS;
48         break;
49     case EAPOL_eapRestart:
50         flag = CTX_FLAG_EAP_RESTART;
51         break;
52     case EAPOL_eapFail:
53         flag = CTX_FLAG_EAP_FAIL;
54         break;
55     case EAPOL_eapResp:
56         flag = CTX_FLAG_EAP_RESP;
57         break;
58     case EAPOL_eapNoResp:
59         flag = CTX_FLAG_EAP_NO_RESP;
60         break;
61     case EAPOL_eapReq:
62         flag = CTX_FLAG_EAP_REQ;
63         break;
64     case EAPOL_portEnabled:
65         flag = CTX_FLAG_EAP_PORT_ENABLED;
66         break;
67     case EAPOL_altAccept:
68         flag = CTX_FLAG_EAP_ALT_ACCEPT;
69         break;
70     case EAPOL_altReject:
71         flag = CTX_FLAG_EAP_ALT_REJECT;
72         break;
73     }
74
75     return flag;
76 }
77
78 static struct eap_peer_config *
79 peerGetConfig(void *ctx)
80 {
81     gss_ctx_id_t gssCtx = (gss_ctx_id_t)ctx;
82
83     return &gssCtx->initiatorCtx.eapPeerConfig;
84 }
85
86 static Boolean
87 peerGetBool(void *data, enum eapol_bool_var variable)
88 {
89     gss_ctx_id_t ctx = data;
90     OM_uint32 flag;
91
92     if (ctx == GSS_C_NO_CONTEXT)
93         return FALSE;
94
95     flag = policyVariableToFlag(variable);
96
97     return ((ctx->flags & flag) != 0);
98 }
99
100 static void
101 peerSetBool(void *data, enum eapol_bool_var variable,
102             Boolean value)
103 {
104     gss_ctx_id_t ctx = data;
105     OM_uint32 flag;
106
107     if (ctx == GSS_C_NO_CONTEXT)
108         return;
109
110     flag = policyVariableToFlag(variable);
111
112     if (value)
113         ctx->flags |= flag;
114     else
115         ctx->flags &= ~(flag);
116 }
117
118 static unsigned int
119 peerGetInt(void *data, enum eapol_int_var variable)
120 {
121     gss_ctx_id_t ctx = data;
122
123     if (ctx == GSS_C_NO_CONTEXT)
124         return FALSE;
125
126     GSSEAP_ASSERT(CTX_IS_INITIATOR(ctx));
127
128     switch (variable) {
129     case EAPOL_idleWhile:
130         return ctx->initiatorCtx.idleWhile;
131         break;
132     }
133
134     return 0;
135 }
136
137 static void
138 peerSetInt(void *data, enum eapol_int_var variable,
139            unsigned int value)
140 {
141     gss_ctx_id_t ctx = data;
142
143     if (ctx == GSS_C_NO_CONTEXT)
144         return;
145
146     GSSEAP_ASSERT(CTX_IS_INITIATOR(ctx));
147
148     switch (variable) {
149     case EAPOL_idleWhile:
150         ctx->initiatorCtx.idleWhile = value;
151         break;
152     }
153 }
154
155 static struct wpabuf *
156 peerGetEapReqData(void *ctx)
157 {
158     gss_ctx_id_t gssCtx = (gss_ctx_id_t)ctx;
159
160     return &gssCtx->initiatorCtx.reqData;
161 }
162
163 static void
164 peerSetConfigBlob(void *ctx GSSEAP_UNUSED,
165                   struct wpa_config_blob *blob GSSEAP_UNUSED)
166 {
167 }
168
169 static const struct wpa_config_blob *
170 peerGetConfigBlob(void *ctx GSSEAP_UNUSED,
171                   const char *name GSSEAP_UNUSED)
172 {
173     return NULL;
174 }
175
176 static void
177 peerNotifyPending(void *ctx GSSEAP_UNUSED)
178 {
179 }
180
181 static struct eapol_callbacks gssEapPolicyCallbacks = {
182     peerGetConfig,
183     peerGetBool,
184     peerSetBool,
185     peerGetInt,
186     peerSetInt,
187     peerGetEapReqData,
188     peerSetConfigBlob,
189     peerGetConfigBlob,
190     peerNotifyPending,
191 };
192
193 #ifdef GSSEAP_DEBUG
194 extern int wpa_debug_level;
195 #endif
196
197 static OM_uint32
198 peerConfigInit(OM_uint32 *minor, gss_ctx_id_t ctx)
199 {
200     OM_uint32 major;
201     krb5_context krbContext;
202     struct eap_peer_config *eapPeerConfig = &ctx->initiatorCtx.eapPeerConfig;
203     gss_buffer_desc identity = GSS_C_EMPTY_BUFFER;
204     gss_buffer_desc realm = GSS_C_EMPTY_BUFFER;
205     gss_cred_id_t cred = ctx->cred;
206
207     eapPeerConfig->identity = NULL;
208     eapPeerConfig->identity_len = 0;
209     eapPeerConfig->anonymous_identity = NULL;
210     eapPeerConfig->anonymous_identity_len = 0;
211     eapPeerConfig->password = NULL;
212     eapPeerConfig->password_len = 0;
213
214     GSSEAP_ASSERT(cred != GSS_C_NO_CREDENTIAL);
215
216     GSSEAP_KRB_INIT(&krbContext);
217
218     eapPeerConfig->fragment_size = 1024;
219 #ifdef GSSEAP_DEBUG
220     wpa_debug_level = 0;
221 #endif
222
223     GSSEAP_ASSERT(cred->name != GSS_C_NO_NAME);
224
225     if ((cred->name->flags & (NAME_FLAG_NAI | NAME_FLAG_SERVICE)) == 0) {
226         *minor = GSSEAP_BAD_INITIATOR_NAME;
227         return GSS_S_BAD_NAME;
228     }
229
230     /* identity */
231     major = gssEapDisplayName(minor, cred->name, &identity, NULL);
232     if (GSS_ERROR(major))
233         return major;
234
235     eapPeerConfig->identity = (unsigned char *)identity.value;
236     eapPeerConfig->identity_len = identity.length;
237
238     krbPrincRealmToGssBuffer(cred->name->krbPrincipal, &realm);
239
240     /* anonymous_identity */
241     eapPeerConfig->anonymous_identity = GSSEAP_MALLOC(realm.length + 2);
242     if (eapPeerConfig->anonymous_identity == NULL) {
243         *minor = ENOMEM;
244         return GSS_S_FAILURE;
245     }
246
247     eapPeerConfig->anonymous_identity[0] = '@';
248     memcpy(eapPeerConfig->anonymous_identity + 1, realm.value, realm.length);
249     eapPeerConfig->anonymous_identity[1 + realm.length] = '\0';
250     eapPeerConfig->anonymous_identity_len = 1 + realm.length;
251
252     /* password */
253     if ((cred->flags & CRED_FLAG_CERTIFICATE) == 0) {
254         eapPeerConfig->password = (unsigned char *)cred->password.value;
255         eapPeerConfig->password_len = cred->password.length;
256     }
257
258     /* certs */
259     eapPeerConfig->ca_cert = (unsigned char *)cred->caCertificate.value;
260     eapPeerConfig->subject_match = (unsigned char *)cred->subjectNameConstraint.value;
261     eapPeerConfig->altsubject_match = (unsigned char *)cred->subjectAltNameConstraint.value;
262
263     if (cred->flags & CRED_FLAG_CERTIFICATE) {
264         eapPeerConfig->client_cert = (unsigned char *)cred->clientCertificate.value;
265         eapPeerConfig->private_key = (unsigned char *)cred->privateKey.value;
266         eapPeerConfig->private_key_passwd = (unsigned char *)cred->password.value;
267     }
268
269     *minor = 0;
270     return GSS_S_COMPLETE;
271 }
272
273 static OM_uint32
274 peerConfigFree(OM_uint32 *minor,
275                gss_ctx_id_t ctx)
276 {
277     struct eap_peer_config *eapPeerConfig = &ctx->initiatorCtx.eapPeerConfig;
278
279     if (eapPeerConfig->identity != NULL) {
280         GSSEAP_FREE(eapPeerConfig->identity);
281         eapPeerConfig->identity = NULL;
282         eapPeerConfig->identity_len = 0;
283     }
284
285     if (eapPeerConfig->anonymous_identity != NULL) {
286         GSSEAP_FREE(eapPeerConfig->anonymous_identity);
287         eapPeerConfig->anonymous_identity = NULL;
288         eapPeerConfig->anonymous_identity_len = 0;
289     }
290
291     *minor = 0;
292     return GSS_S_COMPLETE;
293 }
294
295 /*
296  * Mark an initiator context as ready for cryptographic operations
297  */
298 static OM_uint32
299 initReady(OM_uint32 *minor, gss_ctx_id_t ctx, OM_uint32 reqFlags)
300 {
301     OM_uint32 major;
302     const unsigned char *key;
303     size_t keyLength;
304
305 #if 1
306     /* XXX actually check for mutual auth */
307     if (reqFlags & GSS_C_MUTUAL_FLAG)
308         ctx->gssFlags |= GSS_C_MUTUAL_FLAG;
309 #endif
310
311     /* Cache encryption type derived from selected mechanism OID */
312     major = gssEapOidToEnctype(minor, ctx->mechanismUsed, &ctx->encryptionType);
313     if (GSS_ERROR(major))
314         return major;
315
316     if (!eap_key_available(ctx->initiatorCtx.eap)) {
317         *minor = GSSEAP_KEY_UNAVAILABLE;
318         return GSS_S_UNAVAILABLE;
319     }
320
321     key = eap_get_eapKeyData(ctx->initiatorCtx.eap, &keyLength);
322
323     if (keyLength < EAP_EMSK_LEN) {
324         *minor = GSSEAP_KEY_TOO_SHORT;
325         return GSS_S_UNAVAILABLE;
326     }
327
328     major = gssEapDeriveRfc3961Key(minor,
329                                    &key[EAP_EMSK_LEN / 2],
330                                    EAP_EMSK_LEN / 2,
331                                    ctx->encryptionType,
332                                    &ctx->rfc3961Key);
333        if (GSS_ERROR(major))
334            return major;
335
336     major = rfc3961ChecksumTypeForKey(minor, &ctx->rfc3961Key,
337                                       &ctx->checksumType);
338     if (GSS_ERROR(major))
339         return major;
340
341     major = sequenceInit(minor,
342                          &ctx->seqState,
343                          ctx->recvSeq,
344                          ((ctx->gssFlags & GSS_C_REPLAY_FLAG) != 0),
345                          ((ctx->gssFlags & GSS_C_SEQUENCE_FLAG) != 0),
346                          TRUE);
347     if (GSS_ERROR(major))
348         return major;
349
350     *minor = 0;
351     return GSS_S_COMPLETE;
352 }
353
354 static OM_uint32
355 initBegin(OM_uint32 *minor,
356           gss_ctx_id_t ctx,
357           gss_name_t target,
358           gss_OID mech,
359           OM_uint32 reqFlags GSSEAP_UNUSED,
360           OM_uint32 timeReq,
361           gss_channel_bindings_t chanBindings GSSEAP_UNUSED)
362 {
363     OM_uint32 major;
364     gss_cred_id_t cred = ctx->cred;
365
366     GSSEAP_ASSERT(cred != GSS_C_NO_CREDENTIAL);
367
368     if (cred->expiryTime)
369         ctx->expiryTime = cred->expiryTime;
370     else if (timeReq == 0 || timeReq == GSS_C_INDEFINITE)
371         ctx->expiryTime = 0;
372     else
373         ctx->expiryTime = time(NULL) + timeReq;
374
375     /*
376      * The credential mutex protects its name, however we need to
377      * explicitly lock the acceptor name (unlikely as it may be
378      * that it has attributes set on it).
379      */
380     major = gssEapDuplicateName(minor, cred->name, &ctx->initiatorName);
381     if (GSS_ERROR(major))
382         return major;
383
384     if (target != GSS_C_NO_NAME) {
385         GSSEAP_MUTEX_LOCK(&target->mutex);
386
387         major = gssEapDuplicateName(minor, target, &ctx->acceptorName);
388         if (GSS_ERROR(major)) {
389             GSSEAP_MUTEX_UNLOCK(&target->mutex);
390             return major;
391         }
392
393         GSSEAP_MUTEX_UNLOCK(&target->mutex);
394     }
395
396     major = gssEapCanonicalizeOid(minor,
397                                   mech,
398                                   OID_FLAG_NULL_VALID | OID_FLAG_MAP_NULL_TO_DEFAULT_MECH,
399                                   &ctx->mechanismUsed);
400     if (GSS_ERROR(major))
401         return major;
402
403     /* If credentials were provided, check they're usable with this mech */
404     if (!gssEapCredAvailable(cred, ctx->mechanismUsed)) {
405         *minor = GSSEAP_CRED_MECH_MISMATCH;
406         return GSS_S_BAD_MECH;
407     }
408
409     *minor = 0;
410     return GSS_S_COMPLETE;
411 }
412
413 static OM_uint32
414 eapGssSmInitError(OM_uint32 *minor,
415                   gss_cred_id_t cred GSSEAP_UNUSED,
416                   gss_ctx_id_t ctx GSSEAP_UNUSED,
417                   gss_name_t target GSSEAP_UNUSED,
418                   gss_OID mech GSSEAP_UNUSED,
419                   OM_uint32 reqFlags GSSEAP_UNUSED,
420                   OM_uint32 timeReq GSSEAP_UNUSED,
421                   gss_channel_bindings_t chanBindings GSSEAP_UNUSED,
422                   gss_buffer_t inputToken,
423                   gss_buffer_t outputToken GSSEAP_UNUSED,
424                   OM_uint32 *smFlags GSSEAP_UNUSED)
425 {
426     OM_uint32 major;
427     unsigned char *p;
428
429     if (inputToken->length < 8) {
430         *minor = GSSEAP_TOK_TRUNC;
431         return GSS_S_DEFECTIVE_TOKEN;
432     }
433
434     p = (unsigned char *)inputToken->value;
435
436     major = load_uint32_be(&p[0]);
437     *minor = ERROR_TABLE_BASE_eapg + load_uint32_be(&p[4]);
438
439     if (!GSS_ERROR(major) || !IS_WIRE_ERROR(*minor)) {
440         major = GSS_S_FAILURE;
441         *minor = GSSEAP_BAD_ERROR_TOKEN;
442     }
443
444     GSSEAP_ASSERT(GSS_ERROR(major));
445
446     return major;
447 }
448
449 #ifdef GSSEAP_ENABLE_REAUTH
450 static OM_uint32
451 eapGssSmInitGssReauth(OM_uint32 *minor,
452                       gss_cred_id_t cred,
453                       gss_ctx_id_t ctx,
454                       gss_name_t target,
455                       gss_OID mech GSSEAP_UNUSED,
456                       OM_uint32 reqFlags,
457                       OM_uint32 timeReq,
458                       gss_channel_bindings_t chanBindings,
459                       gss_buffer_t inputToken,
460                       gss_buffer_t outputToken,
461                       OM_uint32 *smFlags GSSEAP_UNUSED)
462 {
463     OM_uint32 major, tmpMinor;
464     gss_name_t mechTarget = GSS_C_NO_NAME;
465     gss_OID actualMech = GSS_C_NO_OID;
466     OM_uint32 gssFlags, timeRec;
467
468     /*
469      * Here we use the passed in credential handle because the resolved
470      * context credential does not currently have the reauth creds.
471      */
472     if (GSSEAP_SM_STATE(ctx) == GSSEAP_STATE_INITIAL) {
473         if (!gssEapCanReauthP(cred, target, timeReq))
474             return GSS_S_CONTINUE_NEEDED;
475
476         ctx->flags |= CTX_FLAG_KRB_REAUTH;
477     } else if ((ctx->flags & CTX_FLAG_KRB_REAUTH) == 0) {
478         major = GSS_S_DEFECTIVE_TOKEN;
479         *minor = GSSEAP_WRONG_ITOK;
480         goto cleanup;
481     }
482
483     GSSEAP_ASSERT(cred != GSS_C_NO_CREDENTIAL);
484
485     major = gssEapMechToGlueName(minor, target, &mechTarget);
486     if (GSS_ERROR(major))
487         goto cleanup;
488
489     major = gssInitSecContext(minor,
490                               cred->reauthCred,
491                               &ctx->reauthCtx,
492                               mechTarget,
493                               (gss_OID)gss_mech_krb5,
494                               reqFlags | GSS_C_MUTUAL_FLAG,
495                               timeReq,
496                               chanBindings,
497                               inputToken,
498                               &actualMech,
499                               outputToken,
500                               &gssFlags,
501                               &timeRec);
502     if (GSS_ERROR(major))
503         goto cleanup;
504
505     ctx->gssFlags = gssFlags;
506
507     if (major == GSS_S_COMPLETE) {
508         GSSEAP_ASSERT(GSSEAP_SM_STATE(ctx) == GSSEAP_STATE_REAUTHENTICATE);
509
510         major = gssEapReauthComplete(minor, ctx, cred, actualMech, timeRec);
511         if (GSS_ERROR(major))
512             goto cleanup;
513         GSSEAP_SM_TRANSITION(ctx, GSSEAP_STATE_ESTABLISHED);
514     } else {
515         GSSEAP_SM_TRANSITION(ctx, GSSEAP_STATE_REAUTHENTICATE);
516     }
517
518 cleanup:
519     gssReleaseName(&tmpMinor, &mechTarget);
520
521     return major;
522 }
523 #endif /* GSSEAP_ENABLE_REAUTH */
524
525 #ifdef GSSEAP_DEBUG
526 static OM_uint32
527 eapGssSmInitVendorInfo(OM_uint32 *minor,
528                        gss_cred_id_t cred GSSEAP_UNUSED,
529                        gss_ctx_id_t ctx GSSEAP_UNUSED,
530                        gss_name_t target GSSEAP_UNUSED,
531                        gss_OID mech GSSEAP_UNUSED,
532                        OM_uint32 reqFlags GSSEAP_UNUSED,
533                        OM_uint32 timeReq GSSEAP_UNUSED,
534                        gss_channel_bindings_t chanBindings GSSEAP_UNUSED,
535                        gss_buffer_t inputToken GSSEAP_UNUSED,
536                        gss_buffer_t outputToken,
537                        OM_uint32 *smFlags GSSEAP_UNUSED)
538 {
539     OM_uint32 major;
540
541     major = makeStringBuffer(minor, "JANET(UK)", outputToken);
542     if (GSS_ERROR(major))
543         return major;
544
545     return GSS_S_CONTINUE_NEEDED;
546 }
547 #endif
548
549 static OM_uint32
550 eapGssSmInitAcceptorName(OM_uint32 *minor,
551                          gss_cred_id_t cred GSSEAP_UNUSED,
552                          gss_ctx_id_t ctx,
553                          gss_name_t target GSSEAP_UNUSED,
554                          gss_OID mech GSSEAP_UNUSED,
555                          OM_uint32 reqFlags GSSEAP_UNUSED,
556                          OM_uint32 timeReq GSSEAP_UNUSED,
557                          gss_channel_bindings_t chanBindings GSSEAP_UNUSED,
558                          gss_buffer_t inputToken GSSEAP_UNUSED,
559                          gss_buffer_t outputToken,
560                          OM_uint32 *smFlags GSSEAP_UNUSED)
561 {
562     OM_uint32 major;
563
564     if (GSSEAP_SM_STATE(ctx) == GSSEAP_STATE_INITIAL &&
565         ctx->acceptorName != GSS_C_NO_NAME) {
566
567         /* Send desired target name to acceptor */
568         major = gssEapDisplayName(minor, ctx->acceptorName,
569                                   outputToken, NULL);
570         if (GSS_ERROR(major))
571             return major;
572     } else if (inputToken != GSS_C_NO_BUFFER &&
573                ctx->acceptorName == GSS_C_NO_NAME) {
574         /* Accept target name hint from acceptor */
575         major = gssEapImportName(minor, inputToken,
576                                  GSS_C_NT_USER_NAME,
577                                  ctx->mechanismUsed,
578                                  &ctx->acceptorName);
579         if (GSS_ERROR(major))
580             return major;
581     }
582
583     /*
584      * Currently, other parts of the code assume that the acceptor name
585      * is available, hence this check.
586      */
587     if (ctx->acceptorName == GSS_C_NO_NAME) {
588         *minor = GSSEAP_NO_ACCEPTOR_NAME;
589         return GSS_S_FAILURE;
590     }
591
592     return GSS_S_CONTINUE_NEEDED;
593 }
594
595 static OM_uint32
596 eapGssSmInitIdentity(OM_uint32 *minor,
597                      gss_cred_id_t cred GSSEAP_UNUSED,
598                      gss_ctx_id_t ctx,
599                      gss_name_t target GSSEAP_UNUSED,
600                      gss_OID mech GSSEAP_UNUSED,
601                      OM_uint32 reqFlags GSSEAP_UNUSED,
602                      OM_uint32 timeReq GSSEAP_UNUSED,
603                      gss_channel_bindings_t chanBindings GSSEAP_UNUSED,
604                      gss_buffer_t inputToken GSSEAP_UNUSED,
605                      gss_buffer_t outputToken GSSEAP_UNUSED,
606                      OM_uint32 *smFlags)
607 {
608     struct eap_config eapConfig;
609
610 #ifdef GSSEAP_ENABLE_REAUTH
611     if (GSSEAP_SM_STATE(ctx) == GSSEAP_STATE_REAUTHENTICATE) {
612         OM_uint32 tmpMinor;
613
614         /* server didn't support reauthentication, sent EAP request */
615         gssDeleteSecContext(&tmpMinor, &ctx->reauthCtx, GSS_C_NO_BUFFER);
616         ctx->flags &= ~(CTX_FLAG_KRB_REAUTH);
617         GSSEAP_SM_TRANSITION(ctx, GSSEAP_STATE_INITIAL);
618     } else
619 #endif
620         *smFlags |= SM_FLAG_FORCE_SEND_TOKEN;
621
622     GSSEAP_ASSERT((ctx->flags & CTX_FLAG_KRB_REAUTH) == 0);
623     GSSEAP_ASSERT(inputToken == GSS_C_NO_BUFFER);
624
625     memset(&eapConfig, 0, sizeof(eapConfig));
626
627     ctx->initiatorCtx.eap = eap_peer_sm_init(ctx,
628                                              &gssEapPolicyCallbacks,
629                                              ctx,
630                                              &eapConfig);
631     if (ctx->initiatorCtx.eap == NULL) {
632         *minor = GSSEAP_PEER_SM_INIT_FAILURE;
633         return GSS_S_FAILURE;
634     }
635
636     ctx->flags |= CTX_FLAG_EAP_RESTART | CTX_FLAG_EAP_PORT_ENABLED;
637
638     /* poke EAP state machine */
639     if (eap_peer_sm_step(ctx->initiatorCtx.eap) != 0) {
640         *minor = GSSEAP_PEER_SM_STEP_FAILURE;
641         return GSS_S_FAILURE;
642     }
643
644     GSSEAP_SM_TRANSITION_NEXT(ctx);
645
646     *minor = 0;
647
648     return GSS_S_CONTINUE_NEEDED;
649 }
650
651 static OM_uint32
652 eapGssSmInitAuthenticate(OM_uint32 *minor,
653                          gss_cred_id_t cred GSSEAP_UNUSED,
654                          gss_ctx_id_t ctx,
655                          gss_name_t target GSSEAP_UNUSED,
656                          gss_OID mech GSSEAP_UNUSED,
657                          OM_uint32 reqFlags GSSEAP_UNUSED,
658                          OM_uint32 timeReq GSSEAP_UNUSED,
659                          gss_channel_bindings_t chanBindings GSSEAP_UNUSED,
660                          gss_buffer_t inputToken GSSEAP_UNUSED,
661                          gss_buffer_t outputToken,
662                          OM_uint32 *smFlags)
663 {
664     OM_uint32 major;
665     OM_uint32 tmpMinor;
666     struct wpabuf *resp = NULL;
667
668     *minor = 0;
669
670     GSSEAP_ASSERT(inputToken != GSS_C_NO_BUFFER);
671
672     major = peerConfigInit(minor, ctx);
673     if (GSS_ERROR(major))
674         goto cleanup;
675
676     GSSEAP_ASSERT(ctx->initiatorCtx.eap != NULL);
677     GSSEAP_ASSERT(ctx->flags & CTX_FLAG_EAP_PORT_ENABLED);
678
679     ctx->flags |= CTX_FLAG_EAP_REQ; /* we have a Request from the acceptor */
680
681     wpabuf_set(&ctx->initiatorCtx.reqData,
682                inputToken->value, inputToken->length);
683
684     major = GSS_S_CONTINUE_NEEDED;
685
686     eap_peer_sm_step(ctx->initiatorCtx.eap);
687     if (ctx->flags & CTX_FLAG_EAP_RESP) {
688         ctx->flags &= ~(CTX_FLAG_EAP_RESP);
689
690         resp = eap_get_eapRespData(ctx->initiatorCtx.eap);
691     } else if (ctx->flags & CTX_FLAG_EAP_SUCCESS) {
692         major = initReady(minor, ctx, reqFlags);
693         if (GSS_ERROR(major))
694             goto cleanup;
695
696         ctx->flags &= ~(CTX_FLAG_EAP_SUCCESS);
697         major = GSS_S_CONTINUE_NEEDED;
698         GSSEAP_SM_TRANSITION_NEXT(ctx);
699     } else if (ctx->flags & CTX_FLAG_EAP_FAIL) {
700         major = GSS_S_DEFECTIVE_CREDENTIAL;
701         *minor = GSSEAP_PEER_AUTH_FAILURE;
702     } else {
703         major = GSS_S_DEFECTIVE_TOKEN;
704         *minor = GSSEAP_PEER_BAD_MESSAGE;
705     }
706
707 cleanup:
708     if (resp != NULL) {
709         OM_uint32 tmpMajor;
710         gss_buffer_desc respBuf;
711
712         GSSEAP_ASSERT(major == GSS_S_CONTINUE_NEEDED);
713
714         respBuf.length = wpabuf_len(resp);
715         respBuf.value = (void *)wpabuf_head(resp);
716
717         tmpMajor = duplicateBuffer(&tmpMinor, &respBuf, outputToken);
718         if (GSS_ERROR(tmpMajor)) {
719             major = tmpMajor;
720             *minor = tmpMinor;
721         }
722
723         *smFlags |= SM_FLAG_OUTPUT_TOKEN_CRITICAL;
724     }
725
726     wpabuf_set(&ctx->initiatorCtx.reqData, NULL, 0);
727     peerConfigFree(&tmpMinor, ctx);
728
729     return major;
730 }
731
732 static OM_uint32
733 eapGssSmInitGssFlags(OM_uint32 *minor,
734                      gss_cred_id_t cred GSSEAP_UNUSED,
735                      gss_ctx_id_t ctx,
736                      gss_name_t target GSSEAP_UNUSED,
737                      gss_OID mech GSSEAP_UNUSED,
738                      OM_uint32 reqFlags GSSEAP_UNUSED,
739                      OM_uint32 timeReq GSSEAP_UNUSED,
740                      gss_channel_bindings_t chanBindings GSSEAP_UNUSED,
741                      gss_buffer_t inputToken GSSEAP_UNUSED,
742                      gss_buffer_t outputToken,
743                      OM_uint32 *smFlags GSSEAP_UNUSED)
744 {
745     unsigned char wireFlags[4];
746     gss_buffer_desc flagsBuf;
747
748     store_uint32_be(ctx->gssFlags & GSSEAP_WIRE_FLAGS_MASK, wireFlags);
749
750     flagsBuf.length = sizeof(wireFlags);
751     flagsBuf.value = wireFlags;
752
753     return duplicateBuffer(minor, &flagsBuf, outputToken);
754 }
755
756 static OM_uint32
757 eapGssSmInitGssChannelBindings(OM_uint32 *minor,
758                                gss_cred_id_t cred GSSEAP_UNUSED,
759                                gss_ctx_id_t ctx,
760                                gss_name_t target GSSEAP_UNUSED,
761                                gss_OID mech GSSEAP_UNUSED,
762                                OM_uint32 reqFlags GSSEAP_UNUSED,
763                                OM_uint32 timeReq GSSEAP_UNUSED,
764                                gss_channel_bindings_t chanBindings,
765                                gss_buffer_t inputToken GSSEAP_UNUSED,
766                                gss_buffer_t outputToken,
767                                OM_uint32 *smFlags)
768 {
769     OM_uint32 major;
770     gss_buffer_desc buffer = GSS_C_EMPTY_BUFFER;
771
772     if (chanBindings != GSS_C_NO_CHANNEL_BINDINGS)
773         buffer = chanBindings->application_data;
774
775     major = gssEapWrap(minor, ctx, TRUE, GSS_C_QOP_DEFAULT,
776                        &buffer, NULL, outputToken);
777     if (GSS_ERROR(major))
778         return major;
779
780     GSSEAP_ASSERT(outputToken->value != NULL);
781
782     *minor = 0;
783     *smFlags |= SM_FLAG_OUTPUT_TOKEN_CRITICAL;
784
785     return GSS_S_CONTINUE_NEEDED;
786 }
787
788 static OM_uint32
789 eapGssSmInitInitiatorMIC(OM_uint32 *minor,
790                          gss_cred_id_t cred GSSEAP_UNUSED,
791                          gss_ctx_id_t ctx,
792                          gss_name_t target GSSEAP_UNUSED,
793                          gss_OID mech GSSEAP_UNUSED,
794                          OM_uint32 reqFlags GSSEAP_UNUSED,
795                          OM_uint32 timeReq GSSEAP_UNUSED,
796                          gss_channel_bindings_t chanBindings GSSEAP_UNUSED,
797                          gss_buffer_t inputToken GSSEAP_UNUSED,
798                          gss_buffer_t outputToken,
799                          OM_uint32 *smFlags)
800 {
801     OM_uint32 major;
802
803     major = gssEapMakeTokenMIC(minor, ctx, outputToken);
804     if (GSS_ERROR(major))
805         return major;
806
807     GSSEAP_SM_TRANSITION_NEXT(ctx);
808
809     *minor = 0;
810     *smFlags |= SM_FLAG_OUTPUT_TOKEN_CRITICAL;
811
812     return GSS_S_CONTINUE_NEEDED;
813 }
814  
815 #ifdef GSSEAP_ENABLE_REAUTH
816 static OM_uint32
817 eapGssSmInitReauthCreds(OM_uint32 *minor,
818                         gss_cred_id_t cred,
819                         gss_ctx_id_t ctx,
820                         gss_name_t target GSSEAP_UNUSED,
821                         gss_OID mech GSSEAP_UNUSED,
822                         OM_uint32 reqFlags GSSEAP_UNUSED,
823                         OM_uint32 timeReq GSSEAP_UNUSED,
824                         gss_channel_bindings_t chanBindings GSSEAP_UNUSED,
825                         gss_buffer_t inputToken,
826                         gss_buffer_t outputToken GSSEAP_UNUSED,
827                         OM_uint32 *smFlags GSSEAP_UNUSED)
828 {
829     OM_uint32 major;
830
831     if (ctx->gssFlags & GSS_C_MUTUAL_FLAG) {
832         major = gssEapStoreReauthCreds(minor, ctx, cred, inputToken);
833         if (GSS_ERROR(major))
834             return major;
835     }
836
837     *minor = 0;
838     return GSS_S_CONTINUE_NEEDED;
839 }
840 #endif /* GSSEAP_ENABLE_REAUTH */
841
842 static OM_uint32
843 eapGssSmInitAcceptorMIC(OM_uint32 *minor,
844                         gss_cred_id_t cred GSSEAP_UNUSED,
845                         gss_ctx_id_t ctx,
846                         gss_name_t target GSSEAP_UNUSED,
847                         gss_OID mech GSSEAP_UNUSED,
848                         OM_uint32 reqFlags GSSEAP_UNUSED,
849                         OM_uint32 timeReq GSSEAP_UNUSED,
850                         gss_channel_bindings_t chanBindings GSSEAP_UNUSED,
851                         gss_buffer_t inputToken,
852                         gss_buffer_t outputToken GSSEAP_UNUSED,
853                         OM_uint32 *smFlags GSSEAP_UNUSED)
854 {
855     OM_uint32 major;
856
857     major = gssEapVerifyTokenMIC(minor, ctx, inputToken);
858     if (GSS_ERROR(major))
859         return major;
860
861     GSSEAP_SM_TRANSITION(ctx, GSSEAP_STATE_ESTABLISHED);
862
863     *minor = 0;
864
865     return GSS_S_COMPLETE;
866 }
867
868 static struct gss_eap_sm eapGssInitiatorSm[] = {
869     {
870         ITOK_TYPE_CONTEXT_ERR,
871         ITOK_TYPE_NONE,
872         GSSEAP_STATE_ALL & ~(GSSEAP_STATE_INITIAL),
873         0,
874         eapGssSmInitError
875     },
876     {
877         ITOK_TYPE_ACCEPTOR_NAME_RESP,
878         ITOK_TYPE_ACCEPTOR_NAME_REQ,
879         GSSEAP_STATE_INITIAL | GSSEAP_STATE_AUTHENTICATE,
880         0,
881         eapGssSmInitAcceptorName
882     },
883 #ifdef GSSEAP_DEBUG
884     {
885         ITOK_TYPE_NONE,
886         ITOK_TYPE_VENDOR_INFO,
887         GSSEAP_STATE_INITIAL,
888         0,
889         eapGssSmInitVendorInfo
890     },
891 #endif
892 #ifdef GSSEAP_ENABLE_REAUTH
893     {
894         ITOK_TYPE_REAUTH_RESP,
895         ITOK_TYPE_REAUTH_REQ,
896         GSSEAP_STATE_INITIAL | GSSEAP_STATE_REAUTHENTICATE,
897         0,
898         eapGssSmInitGssReauth
899     },
900 #endif
901     {
902         ITOK_TYPE_NONE,
903         ITOK_TYPE_NONE,
904 #ifdef GSSEAP_ENABLE_REAUTH
905         GSSEAP_STATE_REAUTHENTICATE |
906 #endif
907         GSSEAP_STATE_INITIAL,
908         SM_ITOK_FLAG_REQUIRED,
909         eapGssSmInitIdentity
910     },
911     {
912         ITOK_TYPE_EAP_REQ,
913         ITOK_TYPE_EAP_RESP,
914         GSSEAP_STATE_AUTHENTICATE,
915         SM_ITOK_FLAG_REQUIRED,
916         eapGssSmInitAuthenticate
917     },
918     {
919         ITOK_TYPE_NONE,
920         ITOK_TYPE_GSS_FLAGS,
921         GSSEAP_STATE_INITIATOR_EXTS,
922         0,
923         eapGssSmInitGssFlags
924     },
925     {
926         ITOK_TYPE_NONE,
927         ITOK_TYPE_GSS_CHANNEL_BINDINGS,
928         GSSEAP_STATE_INITIATOR_EXTS,
929         SM_ITOK_FLAG_REQUIRED,
930         eapGssSmInitGssChannelBindings
931     },
932     {
933         ITOK_TYPE_NONE,
934         ITOK_TYPE_INITIATOR_MIC,
935         GSSEAP_STATE_INITIATOR_EXTS,
936         SM_ITOK_FLAG_REQUIRED,
937         eapGssSmInitInitiatorMIC
938     },
939 #ifdef GSSEAP_ENABLE_REAUTH
940     {
941         ITOK_TYPE_REAUTH_CREDS,
942         ITOK_TYPE_NONE,
943         GSSEAP_STATE_ACCEPTOR_EXTS,
944         0,
945         eapGssSmInitReauthCreds
946     },
947 #endif
948     /* other extensions go here */
949     {
950         ITOK_TYPE_ACCEPTOR_MIC,
951         ITOK_TYPE_NONE,
952         GSSEAP_STATE_ACCEPTOR_EXTS,
953         SM_ITOK_FLAG_REQUIRED,
954         eapGssSmInitAcceptorMIC
955     }
956 };
957
958 OM_uint32
959 gssEapInitSecContext(OM_uint32 *minor,
960                      gss_cred_id_t cred,
961                      gss_ctx_id_t ctx,
962                      gss_name_t target_name,
963                      gss_OID mech_type,
964                      OM_uint32 req_flags,
965                      OM_uint32 time_req,
966                      gss_channel_bindings_t input_chan_bindings,
967                      gss_buffer_t input_token,
968                      gss_OID *actual_mech_type,
969                      gss_buffer_t output_token,
970                      OM_uint32 *ret_flags,
971                      OM_uint32 *time_rec)
972 {
973     OM_uint32 major, tmpMinor;
974     int initialContextToken = (ctx->mechanismUsed == GSS_C_NO_OID);
975
976     /*
977      * XXX is acquiring the credential lock here necessary? The password is
978      * mutable but the contract could specify that this is not updated whilst
979      * a context is being initialized.
980      */
981     if (cred != GSS_C_NO_CREDENTIAL)
982         GSSEAP_MUTEX_LOCK(&cred->mutex);
983
984     if (ctx->cred == GSS_C_NO_CREDENTIAL) {
985         major = gssEapResolveInitiatorCred(minor, cred, target_name, &ctx->cred);
986         if (GSS_ERROR(major))
987             goto cleanup;
988
989         GSSEAP_ASSERT(ctx->cred != GSS_C_NO_CREDENTIAL);
990     }
991
992     GSSEAP_MUTEX_LOCK(&ctx->cred->mutex);
993
994     GSSEAP_ASSERT(ctx->cred->flags & CRED_FLAG_RESOLVED);
995     GSSEAP_ASSERT(ctx->cred->flags & CRED_FLAG_INITIATE);
996
997     if (initialContextToken) {
998         major = initBegin(minor, ctx, target_name, mech_type,
999                           req_flags, time_req, input_chan_bindings);
1000         if (GSS_ERROR(major))
1001             goto cleanup;
1002     }
1003
1004     major = gssEapSmStep(minor,
1005                          cred,
1006                          ctx,
1007                          target_name,
1008                          mech_type,
1009                          req_flags,
1010                          time_req,
1011                          input_chan_bindings,
1012                          input_token,
1013                          output_token,
1014                          eapGssInitiatorSm,
1015                          sizeof(eapGssInitiatorSm) / sizeof(eapGssInitiatorSm[0]));
1016     if (GSS_ERROR(major))
1017         goto cleanup;
1018
1019     if (actual_mech_type != NULL) {
1020         OM_uint32 tmpMajor;
1021
1022         tmpMajor = gssEapCanonicalizeOid(&tmpMinor, ctx->mechanismUsed, 0, actual_mech_type);
1023         if (GSS_ERROR(tmpMajor)) {
1024             major = tmpMajor;
1025             *minor = tmpMinor;
1026             goto cleanup;
1027         }
1028     }
1029     if (ret_flags != NULL)
1030         *ret_flags = ctx->gssFlags;
1031     if (time_rec != NULL)
1032         gssEapContextTime(&tmpMinor, ctx, time_rec);
1033
1034     GSSEAP_ASSERT(CTX_IS_ESTABLISHED(ctx) || major == GSS_S_CONTINUE_NEEDED);
1035
1036 cleanup:
1037     if (cred != GSS_C_NO_CREDENTIAL)
1038         GSSEAP_MUTEX_UNLOCK(&cred->mutex);
1039     if (ctx->cred != GSS_C_NO_CREDENTIAL)
1040         GSSEAP_MUTEX_UNLOCK(&ctx->cred->mutex);
1041
1042     return major;
1043 }
1044
1045 OM_uint32 GSSAPI_CALLCONV
1046 gss_init_sec_context(OM_uint32 *minor,
1047                      gss_cred_id_t cred,
1048                      gss_ctx_id_t *context_handle,
1049                      gss_name_t target_name,
1050                      gss_OID mech_type,
1051                      OM_uint32 req_flags,
1052                      OM_uint32 time_req,
1053                      gss_channel_bindings_t input_chan_bindings,
1054                      gss_buffer_t input_token,
1055                      gss_OID *actual_mech_type,
1056                      gss_buffer_t output_token,
1057                      OM_uint32 *ret_flags,
1058                      OM_uint32 *time_rec)
1059 {
1060     OM_uint32 major, tmpMinor;
1061     gss_ctx_id_t ctx = *context_handle;
1062
1063     *minor = 0;
1064
1065     output_token->length = 0;
1066     output_token->value = NULL;
1067
1068     if (ctx == GSS_C_NO_CONTEXT) {
1069         if (input_token != GSS_C_NO_BUFFER && input_token->length != 0) {
1070             *minor = GSSEAP_WRONG_SIZE;
1071             return GSS_S_DEFECTIVE_TOKEN;
1072         }
1073
1074         major = gssEapAllocContext(minor, &ctx);
1075         if (GSS_ERROR(major))
1076             return major;
1077
1078         ctx->flags |= CTX_FLAG_INITIATOR;
1079
1080         *context_handle = ctx;
1081     }
1082
1083     GSSEAP_MUTEX_LOCK(&ctx->mutex);
1084
1085     major = gssEapInitSecContext(minor,
1086                                  cred,
1087                                  ctx,
1088                                  target_name,
1089                                  mech_type,
1090                                  req_flags,
1091                                  time_req,
1092                                  input_chan_bindings,
1093                                  input_token,
1094                                  actual_mech_type,
1095                                  output_token,
1096                                  ret_flags,
1097                                  time_rec);
1098
1099     GSSEAP_MUTEX_UNLOCK(&ctx->mutex);
1100
1101     if (GSS_ERROR(major))
1102         gssEapReleaseContext(&tmpMinor, context_handle);
1103
1104     return major;
1105 }