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     if (mech == GSS_C_NULL_OID) {
366         major = gssEapDefaultMech(minor, &ctx->mechanismUsed);
367     } else if (gssEapIsConcreteMechanismOid(mech)) {
368         if (!gssEapInternalizeOid(mech, &ctx->mechanismUsed))
369             major = duplicateOid(minor, mech, &ctx->mechanismUsed);
370     } else {
371         major = GSS_S_BAD_MECH;
372         *minor = GSSEAP_WRONG_MECH;
373     }
374     if (GSS_ERROR(major))
375         return major;
376
377     /* If credentials were provided, check they're usable with this mech */
378     if (!gssEapCredAvailable(cred, ctx->mechanismUsed)) {
379         *minor = GSSEAP_CRED_MECH_MISMATCH;
380         return GSS_S_BAD_MECH;
381     }
382
383     *minor = 0;
384     return GSS_S_COMPLETE;
385 }
386
387 static OM_uint32
388 eapGssSmInitError(OM_uint32 *minor,
389                   gss_cred_id_t cred GSSEAP_UNUSED,
390                   gss_ctx_id_t ctx GSSEAP_UNUSED,
391                   gss_name_t target GSSEAP_UNUSED,
392                   gss_OID mech GSSEAP_UNUSED,
393                   OM_uint32 reqFlags GSSEAP_UNUSED,
394                   OM_uint32 timeReq GSSEAP_UNUSED,
395                   gss_channel_bindings_t chanBindings GSSEAP_UNUSED,
396                   gss_buffer_t inputToken,
397                   gss_buffer_t outputToken GSSEAP_UNUSED,
398                   OM_uint32 *smFlags GSSEAP_UNUSED)
399 {
400     OM_uint32 major;
401     unsigned char *p;
402
403     if (inputToken->length < 8) {
404         *minor = GSSEAP_TOK_TRUNC;
405         return GSS_S_DEFECTIVE_TOKEN;
406     }
407
408     p = (unsigned char *)inputToken->value;
409
410     major = load_uint32_be(&p[0]);
411     *minor = ERROR_TABLE_BASE_eapg + load_uint32_be(&p[4]);
412
413     if (!GSS_ERROR(major) || !IS_WIRE_ERROR(*minor)) {
414         major = GSS_S_FAILURE;
415         *minor = GSSEAP_BAD_ERROR_TOKEN;
416     }
417
418     assert(GSS_ERROR(major));
419
420     return major;
421 }
422
423 #ifdef GSSEAP_ENABLE_REAUTH
424 static OM_uint32
425 eapGssSmInitGssReauth(OM_uint32 *minor,
426                       gss_cred_id_t cred,
427                       gss_ctx_id_t ctx,
428                       gss_name_t target,
429                       gss_OID mech GSSEAP_UNUSED,
430                       OM_uint32 reqFlags,
431                       OM_uint32 timeReq,
432                       gss_channel_bindings_t userChanBindings,
433                       gss_buffer_t inputToken,
434                       gss_buffer_t outputToken,
435                       OM_uint32 *smFlags GSSEAP_UNUSED)
436 {
437     OM_uint32 major, tmpMinor;
438     gss_name_t mechTarget = GSS_C_NO_NAME;
439     gss_OID actualMech = GSS_C_NO_OID;
440     OM_uint32 gssFlags, timeRec;
441     struct gss_channel_bindings_struct wireChanBindings = { 0 };
442
443     assert(cred != GSS_C_NO_CREDENTIAL);
444
445     if (GSSEAP_SM_STATE(ctx) == GSSEAP_STATE_INITIAL) {
446         if (!gssEapCanReauthP(cred, target, timeReq)) {
447             major = GSS_S_CONTINUE_NEEDED;
448             goto cleanup;
449         }
450
451         major = gssEapMakeTokenChannelBindings(minor, ctx,
452                                                userChanBindings,
453                                                GSS_C_NO_BUFFER,
454                                                &wireChanBindings);
455         if (GSS_ERROR(major))
456             goto cleanup;
457
458         ctx->flags |= CTX_FLAG_KRB_REAUTH;
459     } else if ((ctx->flags & CTX_FLAG_KRB_REAUTH) == 0) {
460         major = GSS_S_DEFECTIVE_TOKEN;
461         *minor = GSSEAP_WRONG_ITOK;
462         goto cleanup;
463     }
464
465     major = gssEapMechToGlueName(minor, target, &mechTarget);
466     if (GSS_ERROR(major))
467         goto cleanup;
468
469     major = gssInitSecContext(minor,
470                               cred->krbCred,
471                               &ctx->kerberosCtx,
472                               mechTarget,
473                               (gss_OID)gss_mech_krb5,
474                               reqFlags | GSS_C_MUTUAL_FLAG,
475                               timeReq,
476                               &wireChanBindings,
477                               inputToken,
478                               &actualMech,
479                               outputToken,
480                               &gssFlags,
481                               &timeRec);
482     if (GSS_ERROR(major))
483         goto cleanup;
484
485     ctx->gssFlags = gssFlags;
486
487     if (major == GSS_S_COMPLETE) {
488         assert(GSSEAP_SM_STATE(ctx) == GSSEAP_STATE_REAUTHENTICATE);
489
490         major = gssEapReauthComplete(minor, ctx, cred, actualMech, timeRec);
491         if (GSS_ERROR(major))
492             goto cleanup;
493
494         GSSEAP_SM_TRANSITION(ctx, GSSEAP_STATE_ACCEPTOR_EXTS);
495     } else {
496         GSSEAP_SM_TRANSITION(ctx, GSSEAP_STATE_REAUTHENTICATE);
497         *smFlags |= SM_FLAG_SEND_TOKEN;
498     }
499
500     major = GSS_S_CONTINUE_NEEDED;
501
502 cleanup:
503     gssReleaseName(&tmpMinor, &mechTarget);
504     gss_release_buffer(&tmpMinor, &wireChanBindings.application_data);
505
506     return major;
507 }
508 #endif /* GSSEAP_ENABLE_REAUTH */
509
510 #ifdef GSSEAP_DEBUG
511 static OM_uint32
512 eapGssSmInitVendorInfo(OM_uint32 *minor,
513                        gss_cred_id_t cred GSSEAP_UNUSED,
514                        gss_ctx_id_t ctx GSSEAP_UNUSED,
515                        gss_name_t target GSSEAP_UNUSED,
516                        gss_OID mech GSSEAP_UNUSED,
517                        OM_uint32 reqFlags GSSEAP_UNUSED,
518                        OM_uint32 timeReq GSSEAP_UNUSED,
519                        gss_channel_bindings_t chanBindings GSSEAP_UNUSED,
520                        gss_buffer_t inputToken GSSEAP_UNUSED,
521                        gss_buffer_t outputToken,
522                        OM_uint32 *smFlags GSSEAP_UNUSED)
523 {
524     OM_uint32 major;
525
526     major = makeStringBuffer(minor, "JANET(UK)", outputToken);
527     if (GSS_ERROR(major))
528         return major;
529
530     return GSS_S_CONTINUE_NEEDED;
531 }
532 #endif
533
534 static OM_uint32
535 eapGssSmInitAcceptorName(OM_uint32 *minor,
536                          gss_cred_id_t cred GSSEAP_UNUSED,
537                          gss_ctx_id_t ctx,
538                          gss_name_t target GSSEAP_UNUSED,
539                          gss_OID mech GSSEAP_UNUSED,
540                          OM_uint32 reqFlags GSSEAP_UNUSED,
541                          OM_uint32 timeReq GSSEAP_UNUSED,
542                          gss_channel_bindings_t chanBindings GSSEAP_UNUSED,
543                          gss_buffer_t inputToken GSSEAP_UNUSED,
544                          gss_buffer_t outputToken,
545                          OM_uint32 *smFlags GSSEAP_UNUSED)
546 {
547     OM_uint32 major;
548
549     if (GSSEAP_SM_STATE(ctx) == GSSEAP_STATE_INITIAL &&
550         ctx->acceptorName != GSS_C_NO_NAME) {
551
552         /* Send desired target name to acceptor */
553         major = gssEapDisplayName(minor, ctx->acceptorName,
554                                   outputToken, NULL);
555         if (GSS_ERROR(major))
556             return major;
557     } else if (inputToken != GSS_C_NO_BUFFER &&
558                ctx->acceptorName == GSS_C_NO_NAME) {
559         /* Accept target name hint from acceptor */
560         major = gssEapImportName(minor, inputToken,
561                                  GSS_C_NT_USER_NAME, &ctx->acceptorName);
562         if (GSS_ERROR(major))
563             return major;
564     }
565
566     /*
567      * Currently, other parts of the code assume that the acceptor name
568      * is available, hence this check.
569      */
570     if (ctx->acceptorName == GSS_C_NO_NAME) {
571         *minor = GSSEAP_NO_ACCEPTOR_NAME;
572         return GSS_S_FAILURE;
573     }
574
575     return GSS_S_CONTINUE_NEEDED;
576 }
577
578 static OM_uint32
579 gssEapSupportedAcceptorExts[] = {
580     ITOK_TYPE_REAUTH_CREDS,
581 };
582
583 static struct gss_eap_itok_map
584 gssEapInitiatorExtsFlagMap[] = {
585 };
586
587 static OM_uint32
588 eapGssSmInitExts(OM_uint32 *minor,
589                  gss_cred_id_t cred GSSEAP_UNUSED,
590                  gss_ctx_id_t ctx GSSEAP_UNUSED,
591                  gss_name_t target GSSEAP_UNUSED,
592                  gss_OID mech GSSEAP_UNUSED,
593                  OM_uint32 reqFlags GSSEAP_UNUSED,
594                  OM_uint32 timeReq GSSEAP_UNUSED,
595                  gss_channel_bindings_t chanBindings GSSEAP_UNUSED,
596                  gss_buffer_t inputToken,
597                  gss_buffer_t outputToken,
598                  OM_uint32 *smFlags GSSEAP_UNUSED)
599 {
600     OM_uint32 major = GSS_S_COMPLETE;
601
602     if (GSSEAP_SM_STATE(ctx) == GSSEAP_STATE_INITIAL) {
603         major = gssEapEncodeSupportedExts(minor,
604                                           gssEapSupportedAcceptorExts,
605                                           sizeof(gssEapSupportedAcceptorExts) /
606                                             sizeof(gssEapSupportedAcceptorExts[0]),
607                                           outputToken);
608     } else if (inputToken != GSS_C_NO_BUFFER) {
609         major = gssEapProcessSupportedExts(minor, inputToken,
610                                           gssEapInitiatorExtsFlagMap,
611                                           sizeof(gssEapInitiatorExtsFlagMap) /
612                                             sizeof(gssEapInitiatorExtsFlagMap[0]),
613                                           &ctx->flags);
614     }
615
616     if (GSS_ERROR(major))
617         return major;
618
619     return GSS_S_CONTINUE_NEEDED;
620 }
621
622 static OM_uint32
623 eapGssSmInitIdentity(OM_uint32 *minor,
624                      gss_cred_id_t cred GSSEAP_UNUSED,
625                      gss_ctx_id_t ctx,
626                      gss_name_t target GSSEAP_UNUSED,
627                      gss_OID mech GSSEAP_UNUSED,
628                      OM_uint32 reqFlags GSSEAP_UNUSED,
629                      OM_uint32 timeReq GSSEAP_UNUSED,
630                      gss_channel_bindings_t chanBindings GSSEAP_UNUSED,
631                      gss_buffer_t inputToken GSSEAP_UNUSED,
632                      gss_buffer_t outputToken GSSEAP_UNUSED,
633                      OM_uint32 *smFlags)
634 {
635     struct eap_config eapConfig;
636
637 #ifdef GSSEAP_ENABLE_REAUTH
638     if (GSSEAP_SM_STATE(ctx) == GSSEAP_STATE_REAUTHENTICATE) {
639         OM_uint32 tmpMinor;
640
641         /* server didn't support reauthentication, sent EAP request */
642         gssDeleteSecContext(&tmpMinor, &ctx->kerberosCtx, GSS_C_NO_BUFFER);
643         ctx->flags &= ~(CTX_FLAG_KRB_REAUTH);
644         GSSEAP_SM_TRANSITION(ctx, GSSEAP_STATE_INITIAL);
645     } else
646 #endif
647         *smFlags |= SM_FLAG_SEND_TOKEN;
648
649     assert((ctx->flags & CTX_FLAG_KRB_REAUTH) == 0);
650     assert(inputToken == GSS_C_NO_BUFFER);
651
652     memset(&eapConfig, 0, sizeof(eapConfig));
653
654     ctx->initiatorCtx.eap = eap_peer_sm_init(ctx,
655                                              &gssEapPolicyCallbacks,
656                                              ctx,
657                                              &eapConfig);
658     if (ctx->initiatorCtx.eap == NULL) {
659         *minor = GSSEAP_PEER_SM_INIT_FAILURE;
660         return GSS_S_FAILURE;
661     }
662
663     ctx->flags |= CTX_FLAG_EAP_RESTART | CTX_FLAG_EAP_PORT_ENABLED;
664
665     /* poke EAP state machine */
666     if (eap_peer_sm_step(ctx->initiatorCtx.eap) != 0) {
667         *minor = GSSEAP_PEER_SM_STEP_FAILURE;
668         return GSS_S_FAILURE;
669     }
670
671     GSSEAP_SM_TRANSITION_NEXT(ctx);
672
673     *minor = 0;
674
675     return GSS_S_CONTINUE_NEEDED;
676 }
677
678 static OM_uint32
679 eapGssSmInitAuthenticate(OM_uint32 *minor,
680                          gss_cred_id_t cred,
681                          gss_ctx_id_t ctx,
682                          gss_name_t target GSSEAP_UNUSED,
683                          gss_OID mech GSSEAP_UNUSED,
684                          OM_uint32 reqFlags GSSEAP_UNUSED,
685                          OM_uint32 timeReq GSSEAP_UNUSED,
686                          gss_channel_bindings_t chanBindings GSSEAP_UNUSED,
687                          gss_buffer_t inputToken GSSEAP_UNUSED,
688                          gss_buffer_t outputToken,
689                          OM_uint32 *smFlags)
690 {
691     OM_uint32 major;
692     OM_uint32 tmpMinor;
693     int code;
694     struct wpabuf *resp = NULL;
695
696     *minor = 0;
697
698     assert(inputToken != GSS_C_NO_BUFFER);
699
700     major = peerConfigInit(minor, cred, ctx);
701     if (GSS_ERROR(major))
702         goto cleanup;
703
704     assert(ctx->initiatorCtx.eap != NULL);
705     assert(ctx->flags & CTX_FLAG_EAP_PORT_ENABLED);
706
707     ctx->flags |= CTX_FLAG_EAP_REQ; /* we have a Request from the acceptor */
708
709     wpabuf_set(&ctx->initiatorCtx.reqData,
710                inputToken->value, inputToken->length);
711
712     major = GSS_S_CONTINUE_NEEDED;
713
714     code = eap_peer_sm_step(ctx->initiatorCtx.eap);
715     if (ctx->flags & CTX_FLAG_EAP_RESP) {
716         ctx->flags &= ~(CTX_FLAG_EAP_RESP);
717
718         resp = eap_get_eapRespData(ctx->initiatorCtx.eap);
719     } else if (ctx->flags & CTX_FLAG_EAP_SUCCESS) {
720         major = initReady(minor, ctx, reqFlags);
721         if (GSS_ERROR(major))
722             goto cleanup;
723
724         ctx->flags &= ~(CTX_FLAG_EAP_SUCCESS);
725         major = GSS_S_CONTINUE_NEEDED;
726         GSSEAP_SM_TRANSITION_NEXT(ctx);
727     } else if (ctx->flags & CTX_FLAG_EAP_FAIL) {
728         major = GSS_S_DEFECTIVE_CREDENTIAL;
729         *minor = GSSEAP_PEER_AUTH_FAILURE;
730     } else {
731         major = GSS_S_DEFECTIVE_TOKEN;
732         *minor = GSSEAP_PEER_BAD_MESSAGE;
733     }
734
735 cleanup:
736     if (resp != NULL) {
737         OM_uint32 tmpMajor;
738         gss_buffer_desc respBuf;
739
740         assert(major == GSS_S_CONTINUE_NEEDED);
741
742         respBuf.length = wpabuf_len(resp);
743         respBuf.value = (void *)wpabuf_head(resp);
744
745         tmpMajor = duplicateBuffer(&tmpMinor, &respBuf, outputToken);
746         if (GSS_ERROR(tmpMajor)) {
747             major = tmpMajor;
748             *minor = tmpMinor;
749         }
750
751         *smFlags |= SM_FLAG_SEND_TOKEN | SM_FLAG_OUTPUT_TOKEN_CRITICAL;
752     }
753
754     wpabuf_set(&ctx->initiatorCtx.reqData, NULL, 0);
755     peerConfigFree(&tmpMinor, ctx);
756
757     return major;
758 }
759
760 static OM_uint32
761 eapGssSmInitGssChannelBindings(OM_uint32 *minor,
762                                gss_cred_id_t cred GSSEAP_UNUSED,
763                                gss_ctx_id_t ctx,
764                                gss_name_t target GSSEAP_UNUSED,
765                                gss_OID mech GSSEAP_UNUSED,
766                                OM_uint32 reqFlags GSSEAP_UNUSED,
767                                OM_uint32 timeReq GSSEAP_UNUSED,
768                                gss_channel_bindings_t chanBindings,
769                                gss_buffer_t inputToken GSSEAP_UNUSED,
770                                gss_buffer_t outputToken,
771                                OM_uint32 *smFlags)
772 {
773     OM_uint32 major;
774     gss_buffer_desc buffer = GSS_C_EMPTY_BUFFER;
775
776     if (ctx->flags & CTX_FLAG_KRB_REAUTH)
777         return GSS_S_CONTINUE_NEEDED;
778
779     if (chanBindings != GSS_C_NO_CHANNEL_BINDINGS)
780         buffer = chanBindings->application_data;
781
782     major = gssEapWrap(minor, ctx, TRUE, GSS_C_QOP_DEFAULT,
783                        &buffer, NULL, outputToken);
784     if (GSS_ERROR(major))
785         return major;
786
787     assert(outputToken->value != NULL);
788
789     *minor = 0;
790     *smFlags |= SM_FLAG_OUTPUT_TOKEN_CRITICAL;
791
792     return GSS_S_CONTINUE_NEEDED;
793 }
794
795 #ifdef GSSEAP_ENABLE_REAUTH
796 static OM_uint32
797 eapGssSmInitReauthCreds(OM_uint32 *minor,
798                         gss_cred_id_t cred,
799                         gss_ctx_id_t ctx,
800                         gss_name_t target GSSEAP_UNUSED,
801                         gss_OID mech GSSEAP_UNUSED,
802                         OM_uint32 reqFlags GSSEAP_UNUSED,
803                         OM_uint32 timeReq GSSEAP_UNUSED,
804                         gss_channel_bindings_t chanBindings GSSEAP_UNUSED,
805                         gss_buffer_t inputToken,
806                         gss_buffer_t outputToken GSSEAP_UNUSED,
807                         OM_uint32 *smFlags GSSEAP_UNUSED)
808 {
809     OM_uint32 major;
810
811     if (ctx->gssFlags & GSS_C_MUTUAL_FLAG) {
812         major = gssEapStoreReauthCreds(minor, ctx, cred, inputToken);
813         if (GSS_ERROR(major))
814             return major;
815     }
816
817     *minor = 0;
818     return GSS_S_CONTINUE_NEEDED;
819 }
820 #endif /* GSSEAP_ENABLE_REAUTH */
821
822 static OM_uint32
823 eapGssSmInitInitiatorMIC(OM_uint32 *minor,
824                          gss_cred_id_t cred GSSEAP_UNUSED,
825                          gss_ctx_id_t ctx,
826                          gss_name_t target GSSEAP_UNUSED,
827                          gss_OID mech GSSEAP_UNUSED,
828                          OM_uint32 reqFlags GSSEAP_UNUSED,
829                          OM_uint32 timeReq GSSEAP_UNUSED,
830                          gss_channel_bindings_t chanBindings GSSEAP_UNUSED,
831                          gss_buffer_t inputToken GSSEAP_UNUSED,
832                          gss_buffer_t outputToken,
833                          OM_uint32 *smFlags)
834 {
835     OM_uint32 major;
836
837     major = gssEapGetConversationMIC(minor, ctx, outputToken);
838     if (GSS_ERROR(major))
839         return major;
840
841     GSSEAP_SM_TRANSITION_NEXT(ctx);
842
843     *minor = 0;
844     *smFlags |= SM_FLAG_SEND_TOKEN | SM_FLAG_OUTPUT_TOKEN_CRITICAL;
845
846     return GSS_S_CONTINUE_NEEDED;
847 }
848
849 static OM_uint32
850 eapGssSmInitAcceptorMIC(OM_uint32 *minor,
851                         gss_cred_id_t cred GSSEAP_UNUSED,
852                         gss_ctx_id_t ctx,
853                         gss_name_t target GSSEAP_UNUSED,
854                         gss_OID mech GSSEAP_UNUSED,
855                         OM_uint32 reqFlags GSSEAP_UNUSED,
856                         OM_uint32 timeReq GSSEAP_UNUSED,
857                         gss_channel_bindings_t chanBindings GSSEAP_UNUSED,
858                         gss_buffer_t inputToken,
859                         gss_buffer_t outputToken GSSEAP_UNUSED,
860                         OM_uint32 *smFlags GSSEAP_UNUSED)
861 {
862     OM_uint32 major;
863
864     major = gssEapVerifyConversationMIC(minor, ctx, inputToken);
865     if (GSS_ERROR(major))
866         return major;
867
868     GSSEAP_SM_TRANSITION(ctx, GSSEAP_STATE_ESTABLISHED);
869
870     *minor = 0;
871
872     return GSS_S_COMPLETE;
873 }
874
875 /*
876  * Initiator state machine.
877  */
878 static struct gss_eap_sm eapGssInitiatorSm[] = {
879     {
880         ITOK_TYPE_CONTEXT_ERR,
881         ITOK_TYPE_NONE,
882         GSSEAP_STATE_ALL & ~(GSSEAP_STATE_INITIAL),
883         0,
884         eapGssSmInitError
885     },
886     {
887         ITOK_TYPE_ACCEPTOR_NAME_RESP,
888         ITOK_TYPE_ACCEPTOR_NAME_REQ,
889         GSSEAP_STATE_INITIAL | GSSEAP_STATE_AUTHENTICATE,
890         0,
891         eapGssSmInitAcceptorName
892     },
893     {
894         ITOK_TYPE_SUPPORTED_INITIATOR_EXTS,
895         ITOK_TYPE_SUPPORTED_ACCEPTOR_EXTS,
896         GSSEAP_STATE_INITIAL | GSSEAP_STATE_AUTHENTICATE,
897         0,
898         eapGssSmInitExts
899     },
900 #ifdef GSSEAP_DEBUG
901     {
902         ITOK_TYPE_NONE,
903         ITOK_TYPE_VENDOR_INFO,
904         GSSEAP_STATE_INITIAL,
905         0,
906         eapGssSmInitVendorInfo
907     },
908 #endif
909 #ifdef GSSEAP_ENABLE_REAUTH
910     {
911         ITOK_TYPE_REAUTH_RESP,
912         ITOK_TYPE_REAUTH_REQ,
913         GSSEAP_STATE_INITIAL | GSSEAP_STATE_REAUTHENTICATE,
914         0,
915         eapGssSmInitGssReauth
916     },
917 #endif
918     {
919         ITOK_TYPE_NONE,
920         ITOK_TYPE_NONE,
921 #ifdef GSSEAP_ENABLE_REAUTH
922         GSSEAP_STATE_REAUTHENTICATE |
923 #endif
924         GSSEAP_STATE_INITIAL,
925         SM_ITOK_FLAG_REQUIRED,
926         eapGssSmInitIdentity
927     },
928     {
929         ITOK_TYPE_EAP_REQ,
930         ITOK_TYPE_EAP_RESP,
931         GSSEAP_STATE_AUTHENTICATE,
932         SM_ITOK_FLAG_REQUIRED,
933         eapGssSmInitAuthenticate
934     },
935     {
936         ITOK_TYPE_NONE,
937         ITOK_TYPE_GSS_CHANNEL_BINDINGS,
938         GSSEAP_STATE_INITIATOR_EXTS,
939         0,
940         eapGssSmInitGssChannelBindings
941     },
942     {
943         ITOK_TYPE_NONE,
944         ITOK_TYPE_INITIATOR_MIC,
945         GSSEAP_STATE_INITIATOR_EXTS,
946         0,
947         eapGssSmInitInitiatorMIC
948     },
949 #ifdef GSSEAP_ENABLE_REAUTH
950     {
951         ITOK_TYPE_REAUTH_CREDS,
952         ITOK_TYPE_NONE,
953         GSSEAP_STATE_ACCEPTOR_EXTS,
954         0,
955         eapGssSmInitReauthCreds
956     },
957 #endif
958     /* other extensions go here */
959     {
960         ITOK_TYPE_ACCEPTOR_MIC,
961         ITOK_TYPE_NONE,
962         GSSEAP_STATE_ACCEPTOR_EXTS,
963         SM_ITOK_FLAG_REQUIRED,
964         eapGssSmInitAcceptorMIC
965     }
966 };
967
968 OM_uint32
969 gss_init_sec_context(OM_uint32 *minor,
970                      gss_cred_id_t cred,
971                      gss_ctx_id_t *context_handle,
972                      gss_name_t target_name,
973                      gss_OID mech_type,
974                      OM_uint32 req_flags,
975                      OM_uint32 time_req,
976                      gss_channel_bindings_t input_chan_bindings,
977                      gss_buffer_t input_token,
978                      gss_OID *actual_mech_type,
979                      gss_buffer_t output_token,
980                      OM_uint32 *ret_flags,
981                      OM_uint32 *time_rec)
982 {
983     OM_uint32 major, tmpMinor;
984     gss_ctx_id_t ctx = *context_handle;
985
986     *minor = 0;
987
988     output_token->length = 0;
989     output_token->value = NULL;
990
991     if (ctx == GSS_C_NO_CONTEXT) {
992         if (input_token != GSS_C_NO_BUFFER && input_token->length != 0) {
993             *minor = GSSEAP_WRONG_SIZE;
994             return GSS_S_DEFECTIVE_TOKEN;
995         }
996
997         major = gssEapAllocContext(minor, &ctx);
998         if (GSS_ERROR(major))
999             return major;
1000
1001         ctx->flags |= CTX_FLAG_INITIATOR;
1002
1003         major = initBegin(minor, cred, ctx, target_name, mech_type,
1004                           req_flags, time_req, input_chan_bindings);
1005         if (GSS_ERROR(major)) {
1006             gssEapReleaseContext(minor, &ctx);
1007             return major;
1008         }
1009
1010         *context_handle = ctx;
1011     }
1012
1013     GSSEAP_MUTEX_LOCK(&ctx->mutex);
1014
1015     if (cred == GSS_C_NO_CREDENTIAL) {
1016         if (ctx->defaultCred == GSS_C_NO_CREDENTIAL) {
1017             major = gssEapAcquireCred(minor,
1018                                       GSS_C_NO_NAME,
1019                                       GSS_C_NO_BUFFER,
1020                                       time_req,
1021                                       GSS_C_NO_OID_SET,
1022                                       GSS_C_INITIATE,
1023                                       &ctx->defaultCred,
1024                                       NULL,
1025                                       NULL);
1026             if (GSS_ERROR(major))
1027                 goto cleanup;
1028         }
1029
1030         cred = ctx->defaultCred;
1031     }
1032
1033     GSSEAP_MUTEX_LOCK(&cred->mutex);
1034
1035
1036     if ((cred->flags & CRED_FLAG_INITIATE) == 0) {
1037         major = GSS_S_NO_CRED;
1038         *minor = GSSEAP_CRED_USAGE_MISMATCH;
1039         goto cleanup;
1040     }
1041
1042     major = gssEapSmStep(minor,
1043                          cred,
1044                          ctx,
1045                          target_name,
1046                          mech_type,
1047                          req_flags,
1048                          time_req,
1049                          input_chan_bindings,
1050                          input_token,
1051                          output_token,
1052                          eapGssInitiatorSm,
1053                          sizeof(eapGssInitiatorSm) / sizeof(eapGssInitiatorSm[0]));
1054     if (GSS_ERROR(major))
1055         goto cleanup;
1056
1057     if (actual_mech_type != NULL) {
1058         if (!gssEapInternalizeOid(ctx->mechanismUsed, actual_mech_type))
1059             duplicateOid(&tmpMinor, ctx->mechanismUsed, actual_mech_type);
1060     }
1061     if (ret_flags != NULL)
1062         *ret_flags = ctx->gssFlags;
1063     if (time_rec != NULL)
1064         gssEapContextTime(&tmpMinor, ctx, time_rec);
1065
1066     assert(CTX_IS_ESTABLISHED(ctx) || major == GSS_S_CONTINUE_NEEDED);
1067
1068 cleanup:
1069     if (cred != GSS_C_NO_CREDENTIAL)
1070         GSSEAP_MUTEX_UNLOCK(&cred->mutex);
1071     GSSEAP_MUTEX_UNLOCK(&ctx->mutex);
1072
1073     if (GSS_ERROR(major))
1074         gssEapReleaseContext(&tmpMinor, context_handle);
1075
1076     return major;
1077 }