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