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