2 * Copyright (c) 2011, JANET(UK)
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
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.
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.
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
34 * Establish a security context on the initiator (client). These functions
38 #include "gssapiP_eap.h"
39 #include "radius/radius.h"
40 #include "util_radius.h"
41 #include "utils/radius_utils.h"
44 policyVariableToFlag(enum eapol_bool_var variable)
49 case EAPOL_eapSuccess:
50 flag = CTX_FLAG_EAP_SUCCESS;
52 case EAPOL_eapRestart:
53 flag = CTX_FLAG_EAP_RESTART;
56 flag = CTX_FLAG_EAP_FAIL;
59 flag = CTX_FLAG_EAP_RESP;
62 flag = CTX_FLAG_EAP_NO_RESP;
65 flag = CTX_FLAG_EAP_REQ;
67 case EAPOL_portEnabled:
68 flag = CTX_FLAG_EAP_PORT_ENABLED;
71 flag = CTX_FLAG_EAP_ALT_ACCEPT;
74 flag = CTX_FLAG_EAP_ALT_REJECT;
81 static struct eap_peer_config *
82 peerGetConfig(void *ctx)
84 gss_ctx_id_t gssCtx = (gss_ctx_id_t)ctx;
86 return &gssCtx->initiatorCtx.eapPeerConfig;
90 peerGetBool(void *data, enum eapol_bool_var variable)
92 gss_ctx_id_t ctx = data;
95 if (ctx == GSS_C_NO_CONTEXT)
98 flag = policyVariableToFlag(variable);
100 return ((ctx->flags & flag) != 0);
104 peerSetBool(void *data, enum eapol_bool_var variable,
107 gss_ctx_id_t ctx = data;
110 if (ctx == GSS_C_NO_CONTEXT)
113 flag = policyVariableToFlag(variable);
118 ctx->flags &= ~(flag);
122 peerGetInt(void *data, enum eapol_int_var variable)
124 gss_ctx_id_t ctx = data;
126 if (ctx == GSS_C_NO_CONTEXT)
129 GSSEAP_ASSERT(CTX_IS_INITIATOR(ctx));
132 case EAPOL_idleWhile:
133 return ctx->initiatorCtx.idleWhile;
141 peerSetInt(void *data, enum eapol_int_var variable,
144 gss_ctx_id_t ctx = data;
146 if (ctx == GSS_C_NO_CONTEXT)
149 GSSEAP_ASSERT(CTX_IS_INITIATOR(ctx));
152 case EAPOL_idleWhile:
153 ctx->initiatorCtx.idleWhile = value;
158 static struct wpabuf *
159 peerGetEapReqData(void *ctx)
161 gss_ctx_id_t gssCtx = (gss_ctx_id_t)ctx;
163 return &gssCtx->initiatorCtx.reqData;
167 peerSetConfigBlob(void *ctx GSSEAP_UNUSED,
168 struct wpa_config_blob *blob GSSEAP_UNUSED)
172 static const struct wpa_config_blob *
173 peerGetConfigBlob(void *ctx GSSEAP_UNUSED,
174 const char *name GSSEAP_UNUSED)
180 peerNotifyPending(void *ctx GSSEAP_UNUSED)
184 static struct eapol_callbacks gssEapPolicyCallbacks = {
197 extern int wpa_debug_level;
200 #define CHBIND_SERVICE_NAME_FLAG 0x01
201 #define CHBIND_HOST_NAME_FLAG 0x02
202 #define CHBIND_SERVICE_SPECIFIC_FLAG 0x04
203 #define CHBIND_REALM_NAME_FLAG 0x08
205 extern void TestFunc();
208 peerInitEapChannelBinding(OM_uint32 *minor, gss_ctx_id_t ctx)
210 struct wpabuf *buf = NULL;
211 unsigned int requested = 0;
212 krb5_principal princ;
213 gss_buffer_desc nameBuf;
214 OM_uint32 major = GSS_S_COMPLETE;
215 krb5_context krbContext = NULL;
217 /* must have acceptor name, but already checked in
218 * eapGssSmInitAcceptorName(), so maybe redunadant
219 * to do so here as well? */
220 if (!ctx->acceptorName) {
221 *minor = GSSEAP_NO_ACCEPTOR_NAME;
222 return GSS_S_BAD_NAME;
225 princ = ctx->acceptorName->krbPrincipal;
227 krbPrincComponentToGssBuffer(princ, 0, &nameBuf);
228 if (nameBuf.length > 0) {
229 major = gssEapRadiusAddAttr(minor, &buf, PW_GSS_ACCEPTOR_SERVICE_NAME,
231 if (GSS_ERROR(major))
232 goto init_chbind_cleanup;
233 requested |= CHBIND_SERVICE_NAME_FLAG;
236 krbPrincComponentToGssBuffer(princ, 1, &nameBuf);
237 if (nameBuf.length > 0) {
238 major = gssEapRadiusAddAttr(minor, &buf, PW_GSS_ACCEPTOR_HOST_NAME,
240 if (GSS_ERROR(major))
241 goto init_chbind_cleanup;
242 requested |= CHBIND_HOST_NAME_FLAG;
245 GSSEAP_KRB_INIT(&krbContext);
246 *minor = krbPrincUnparseServiceSpecifics(krbContext, princ, &nameBuf);
248 goto init_chbind_cleanup;
250 if (nameBuf.length > 0) {
251 major = gssEapRadiusAddAttr(minor, &buf,
252 PW_GSS_ACCEPTOR_SERVICE_SPECIFICS,
254 if (GSS_ERROR(major)) {
255 krbFreeUnparsedName(krbContext, &nameBuf);
256 goto init_chbind_cleanup;
258 requested |= CHBIND_SERVICE_SPECIFIC_FLAG;
260 krbFreeUnparsedName(krbContext, &nameBuf);
262 krbPrincRealmToGssBuffer(princ, &nameBuf);
263 if (nameBuf.length > 0) {
264 major = gssEapRadiusAddAttr(minor, &buf,
265 PW_GSS_ACCEPTOR_REALM_NAME,
267 requested |= CHBIND_REALM_NAME_FLAG;
272 *minor = GSSEAP_BAD_ACCEPTOR_NAME;
273 return GSS_S_BAD_NAME;
275 ctx->initiatorCtx.chbindData = buf;
276 ctx->initiatorCtx.chbindReqFlags = requested;
284 peerProcessChbindResponse(void *context, int code, int nsid,
285 u8 *data, size_t len)
288 gss_ctx_id_t ctx = (gss_ctx_id_t )context;
295 if (nsid != CHBIND_NSID_RADIUS)
297 msg = radius_parser_start(data, len);
300 while (radius_parser_parse_tlv(msg, &type, &vendor_id, &vsadata,
301 &vsadata_len) == 0) {
304 case PW_GSS_ACCEPTOR_SERVICE_NAME:
305 accepted |= CHBIND_SERVICE_NAME_FLAG;
307 case PW_GSS_ACCEPTOR_HOST_NAME:
308 accepted |= CHBIND_HOST_NAME_FLAG;
310 case PW_GSS_ACCEPTOR_SERVICE_SPECIFICS:
311 accepted |= CHBIND_SERVICE_SPECIFIC_FLAG;
313 case PW_GSS_ACCEPTOR_REALM_NAME:
314 accepted |= CHBIND_REALM_NAME_FLAG;
318 radius_parser_finish(msg);
319 if ((code == CHBIND_CODE_SUCCESS) &&
320 ((accepted & ctx->initiatorCtx.chbindReqFlags) == ctx->initiatorCtx.chbindReqFlags)) {
321 ctx->flags |= CTX_FLAG_EAP_CHBIND_ACCEPT;
322 ctx->gssFlags |= GSS_C_MUTUAL_FLAG;
330 peerConfigInit(OM_uint32 *minor, gss_ctx_id_t ctx)
333 krb5_context krbContext;
334 struct eap_peer_config *eapPeerConfig = &ctx->initiatorCtx.eapPeerConfig;
335 gss_buffer_desc identity = GSS_C_EMPTY_BUFFER;
336 gss_buffer_desc realm = GSS_C_EMPTY_BUFFER;
337 gss_cred_id_t cred = ctx->cred;
339 eapPeerConfig->identity = NULL;
340 eapPeerConfig->identity_len = 0;
341 eapPeerConfig->anonymous_identity = NULL;
342 eapPeerConfig->anonymous_identity_len = 0;
343 eapPeerConfig->password = NULL;
344 eapPeerConfig->password_len = 0;
346 GSSEAP_ASSERT(cred != GSS_C_NO_CREDENTIAL);
348 GSSEAP_KRB_INIT(&krbContext);
350 eapPeerConfig->fragment_size = 1024;
355 GSSEAP_ASSERT(cred->name != GSS_C_NO_NAME);
357 if ((cred->name->flags & (NAME_FLAG_NAI | NAME_FLAG_SERVICE)) == 0) {
358 *minor = GSSEAP_BAD_INITIATOR_NAME;
359 return GSS_S_BAD_NAME;
363 major = gssEapDisplayName(minor, cred->name, &identity, NULL);
364 if (GSS_ERROR(major))
367 eapPeerConfig->identity = (unsigned char *)identity.value;
368 eapPeerConfig->identity_len = identity.length;
370 krbPrincRealmToGssBuffer(cred->name->krbPrincipal, &realm);
372 /* anonymous_identity */
373 eapPeerConfig->anonymous_identity = GSSEAP_MALLOC(realm.length + 2);
374 if (eapPeerConfig->anonymous_identity == NULL) {
376 return GSS_S_FAILURE;
379 eapPeerConfig->anonymous_identity[0] = '@';
380 memcpy(eapPeerConfig->anonymous_identity + 1, realm.value, realm.length);
381 eapPeerConfig->anonymous_identity[1 + realm.length] = '\0';
382 eapPeerConfig->anonymous_identity_len = 1 + realm.length;
385 eapPeerConfig->password = (unsigned char *)cred->password.value;
386 eapPeerConfig->password_len = cred->password.length;
389 eapPeerConfig->ca_cert = (unsigned char *)cred->caCertificate.value;
390 eapPeerConfig->subject_match = (unsigned char *)cred->subjectNameConstraint.value;
391 eapPeerConfig->altsubject_match = (unsigned char *)cred->subjectAltNameConstraint.value;
393 /* eap channel binding */
394 if (ctx->initiatorCtx.chbindData)
396 struct eap_peer_chbind_config *chbind_config =
397 (struct eap_peer_chbind_config *)
398 GSSEAP_MALLOC(sizeof(struct eap_peer_chbind_config));
399 if (chbind_config == NULL) {
401 return GSS_S_FAILURE;
404 chbind_config->req_data = wpabuf_mhead_u8(ctx->initiatorCtx.chbindData);
405 chbind_config->req_data_len = wpabuf_len(ctx->initiatorCtx.chbindData);
406 chbind_config->nsid = CHBIND_NSID_RADIUS;
407 chbind_config->response_cb = &peerProcessChbindResponse;
408 chbind_config->ctx = ctx;
409 eapPeerConfig->chbind_config = chbind_config;
410 eapPeerConfig->chbind_config_len = 1;
412 eapPeerConfig->chbind_config = NULL;
413 eapPeerConfig->chbind_config_len = 0;
416 return GSS_S_COMPLETE;
420 peerConfigFree(OM_uint32 *minor,
423 struct eap_peer_config *eapPeerConfig = &ctx->initiatorCtx.eapPeerConfig;
425 if (eapPeerConfig->identity != NULL) {
426 GSSEAP_FREE(eapPeerConfig->identity);
427 eapPeerConfig->identity = NULL;
428 eapPeerConfig->identity_len = 0;
431 if (eapPeerConfig->anonymous_identity != NULL) {
432 GSSEAP_FREE(eapPeerConfig->anonymous_identity);
433 eapPeerConfig->anonymous_identity = NULL;
434 eapPeerConfig->anonymous_identity_len = 0;
438 return GSS_S_COMPLETE;
442 * Mark an initiator context as ready for cryptographic operations
445 initReady(OM_uint32 *minor, gss_ctx_id_t ctx)
448 const unsigned char *key;
451 /* Cache encryption type derived from selected mechanism OID */
452 major = gssEapOidToEnctype(minor, ctx->mechanismUsed, &ctx->encryptionType);
453 if (GSS_ERROR(major))
456 if (!eap_key_available(ctx->initiatorCtx.eap)) {
457 *minor = GSSEAP_KEY_UNAVAILABLE;
458 return GSS_S_UNAVAILABLE;
461 key = eap_get_eapKeyData(ctx->initiatorCtx.eap, &keyLength);
463 if (keyLength < EAP_EMSK_LEN) {
464 *minor = GSSEAP_KEY_TOO_SHORT;
465 return GSS_S_UNAVAILABLE;
468 major = gssEapDeriveRfc3961Key(minor,
469 &key[EAP_EMSK_LEN / 2],
473 if (GSS_ERROR(major))
476 major = rfc3961ChecksumTypeForKey(minor, &ctx->rfc3961Key,
478 if (GSS_ERROR(major))
481 major = sequenceInit(minor,
484 ((ctx->gssFlags & GSS_C_REPLAY_FLAG) != 0),
485 ((ctx->gssFlags & GSS_C_SEQUENCE_FLAG) != 0),
487 if (GSS_ERROR(major))
491 return GSS_S_COMPLETE;
495 initBegin(OM_uint32 *minor,
499 OM_uint32 reqFlags GSSEAP_UNUSED,
501 gss_channel_bindings_t chanBindings GSSEAP_UNUSED)
504 gss_cred_id_t cred = ctx->cred;
506 GSSEAP_ASSERT(cred != GSS_C_NO_CREDENTIAL);
508 if (cred->expiryTime)
509 ctx->expiryTime = cred->expiryTime;
510 else if (timeReq == 0 || timeReq == GSS_C_INDEFINITE)
513 ctx->expiryTime = time(NULL) + timeReq;
516 * The credential mutex protects its name, however we need to
517 * explicitly lock the acceptor name (unlikely as it may be
518 * that it has attributes set on it).
520 major = gssEapDuplicateName(minor, cred->name, &ctx->initiatorName);
521 if (GSS_ERROR(major))
524 if (target != GSS_C_NO_NAME) {
525 GSSEAP_MUTEX_LOCK(&target->mutex);
527 major = gssEapDuplicateName(minor, target, &ctx->acceptorName);
528 if (GSS_ERROR(major)) {
529 GSSEAP_MUTEX_UNLOCK(&target->mutex);
533 GSSEAP_MUTEX_UNLOCK(&target->mutex);
536 major = gssEapCanonicalizeOid(minor,
538 OID_FLAG_NULL_VALID | OID_FLAG_MAP_NULL_TO_DEFAULT_MECH,
539 &ctx->mechanismUsed);
540 if (GSS_ERROR(major))
543 /* If credentials were provided, check they're usable with this mech */
544 if (!gssEapCredAvailable(cred, ctx->mechanismUsed)) {
545 *minor = GSSEAP_CRED_MECH_MISMATCH;
546 return GSS_S_BAD_MECH;
550 return GSS_S_COMPLETE;
554 eapGssSmInitError(OM_uint32 *minor,
555 gss_cred_id_t cred GSSEAP_UNUSED,
556 gss_ctx_id_t ctx GSSEAP_UNUSED,
557 gss_name_t target GSSEAP_UNUSED,
558 gss_OID mech GSSEAP_UNUSED,
559 OM_uint32 reqFlags GSSEAP_UNUSED,
560 OM_uint32 timeReq GSSEAP_UNUSED,
561 gss_channel_bindings_t chanBindings GSSEAP_UNUSED,
562 gss_buffer_t inputToken,
563 gss_buffer_t outputToken GSSEAP_UNUSED,
564 OM_uint32 *smFlags GSSEAP_UNUSED)
569 if (inputToken->length < 8) {
570 *minor = GSSEAP_TOK_TRUNC;
571 return GSS_S_DEFECTIVE_TOKEN;
574 p = (unsigned char *)inputToken->value;
576 major = load_uint32_be(&p[0]);
577 *minor = ERROR_TABLE_BASE_eapg + load_uint32_be(&p[4]);
579 if (!GSS_ERROR(major) || !IS_WIRE_ERROR(*minor)) {
580 major = GSS_S_FAILURE;
581 *minor = GSSEAP_BAD_ERROR_TOKEN;
584 GSSEAP_ASSERT(GSS_ERROR(major));
589 #ifdef GSSEAP_ENABLE_REAUTH
591 eapGssSmInitGssReauth(OM_uint32 *minor,
595 gss_OID mech GSSEAP_UNUSED,
598 gss_channel_bindings_t chanBindings,
599 gss_buffer_t inputToken,
600 gss_buffer_t outputToken,
601 OM_uint32 *smFlags GSSEAP_UNUSED)
603 OM_uint32 major, tmpMinor;
604 gss_name_t mechTarget = GSS_C_NO_NAME;
605 gss_OID actualMech = GSS_C_NO_OID;
606 OM_uint32 gssFlags, timeRec;
609 * Here we use the passed in credential handle because the resolved
610 * context credential does not currently have the reauth creds.
612 if (GSSEAP_SM_STATE(ctx) == GSSEAP_STATE_INITIAL) {
613 if (!gssEapCanReauthP(cred, target, timeReq))
614 return GSS_S_CONTINUE_NEEDED;
616 ctx->flags |= CTX_FLAG_KRB_REAUTH;
617 } else if ((ctx->flags & CTX_FLAG_KRB_REAUTH) == 0) {
618 major = GSS_S_DEFECTIVE_TOKEN;
619 *minor = GSSEAP_WRONG_ITOK;
623 GSSEAP_ASSERT(cred != GSS_C_NO_CREDENTIAL);
625 major = gssEapMechToGlueName(minor, target, &mechTarget);
626 if (GSS_ERROR(major))
629 major = gssInitSecContext(minor,
633 (gss_OID)gss_mech_krb5,
634 reqFlags | GSS_C_MUTUAL_FLAG,
642 if (GSS_ERROR(major))
645 ctx->gssFlags = gssFlags;
647 if (major == GSS_S_COMPLETE) {
648 GSSEAP_ASSERT(GSSEAP_SM_STATE(ctx) == GSSEAP_STATE_REAUTHENTICATE);
650 major = gssEapReauthComplete(minor, ctx, cred, actualMech, timeRec);
651 if (GSS_ERROR(major))
653 GSSEAP_SM_TRANSITION(ctx, GSSEAP_STATE_ESTABLISHED);
655 GSSEAP_SM_TRANSITION(ctx, GSSEAP_STATE_REAUTHENTICATE);
659 gssReleaseName(&tmpMinor, &mechTarget);
663 #endif /* GSSEAP_ENABLE_REAUTH */
667 eapGssSmInitVendorInfo(OM_uint32 *minor,
668 gss_cred_id_t cred GSSEAP_UNUSED,
669 gss_ctx_id_t ctx GSSEAP_UNUSED,
670 gss_name_t target GSSEAP_UNUSED,
671 gss_OID mech GSSEAP_UNUSED,
672 OM_uint32 reqFlags GSSEAP_UNUSED,
673 OM_uint32 timeReq GSSEAP_UNUSED,
674 gss_channel_bindings_t chanBindings GSSEAP_UNUSED,
675 gss_buffer_t inputToken GSSEAP_UNUSED,
676 gss_buffer_t outputToken,
677 OM_uint32 *smFlags GSSEAP_UNUSED)
681 major = makeStringBuffer(minor, "JANET(UK)", outputToken);
682 if (GSS_ERROR(major))
685 return GSS_S_CONTINUE_NEEDED;
690 eapGssSmInitAcceptorName(OM_uint32 *minor,
691 gss_cred_id_t cred GSSEAP_UNUSED,
693 gss_name_t target GSSEAP_UNUSED,
694 gss_OID mech GSSEAP_UNUSED,
695 OM_uint32 reqFlags GSSEAP_UNUSED,
696 OM_uint32 timeReq GSSEAP_UNUSED,
697 gss_channel_bindings_t chanBindings GSSEAP_UNUSED,
698 gss_buffer_t inputToken GSSEAP_UNUSED,
699 gss_buffer_t outputToken,
700 OM_uint32 *smFlags GSSEAP_UNUSED)
704 if (GSSEAP_SM_STATE(ctx) == GSSEAP_STATE_INITIAL &&
705 ctx->acceptorName != GSS_C_NO_NAME) {
707 /* Send desired target name to acceptor */
708 major = gssEapDisplayName(minor, ctx->acceptorName,
710 if (GSS_ERROR(major))
712 } else if (inputToken != GSS_C_NO_BUFFER &&
713 ctx->acceptorName == GSS_C_NO_NAME) {
714 /* Accept target name hint from acceptor */
715 major = gssEapImportName(minor, inputToken,
719 if (GSS_ERROR(major))
724 * Currently, other parts of the code assume that the acceptor name
725 * is available, hence this check.
727 if (ctx->acceptorName == GSS_C_NO_NAME) {
728 *minor = GSSEAP_NO_ACCEPTOR_NAME;
729 return GSS_S_FAILURE;
733 * Generate channel binding data
735 if (ctx->initiatorCtx.chbindData == NULL)
737 major = peerInitEapChannelBinding(minor, ctx);
738 if (GSS_ERROR(major))
742 return GSS_S_CONTINUE_NEEDED;
746 eapGssSmInitIdentity(OM_uint32 *minor,
747 gss_cred_id_t cred GSSEAP_UNUSED,
749 gss_name_t target GSSEAP_UNUSED,
750 gss_OID mech GSSEAP_UNUSED,
751 OM_uint32 reqFlags GSSEAP_UNUSED,
752 OM_uint32 timeReq GSSEAP_UNUSED,
753 gss_channel_bindings_t chanBindings GSSEAP_UNUSED,
754 gss_buffer_t inputToken GSSEAP_UNUSED,
755 gss_buffer_t outputToken GSSEAP_UNUSED,
758 struct eap_config eapConfig;
760 #ifdef GSSEAP_ENABLE_REAUTH
761 if (GSSEAP_SM_STATE(ctx) == GSSEAP_STATE_REAUTHENTICATE) {
764 /* server didn't support reauthentication, sent EAP request */
765 gssDeleteSecContext(&tmpMinor, &ctx->reauthCtx, GSS_C_NO_BUFFER);
766 ctx->flags &= ~(CTX_FLAG_KRB_REAUTH);
767 GSSEAP_SM_TRANSITION(ctx, GSSEAP_STATE_INITIAL);
770 *smFlags |= SM_FLAG_FORCE_SEND_TOKEN;
772 GSSEAP_ASSERT((ctx->flags & CTX_FLAG_KRB_REAUTH) == 0);
773 GSSEAP_ASSERT(inputToken == GSS_C_NO_BUFFER);
775 memset(&eapConfig, 0, sizeof(eapConfig));
777 ctx->initiatorCtx.eap = eap_peer_sm_init(ctx,
778 &gssEapPolicyCallbacks,
781 if (ctx->initiatorCtx.eap == NULL) {
782 *minor = GSSEAP_PEER_SM_INIT_FAILURE;
783 return GSS_S_FAILURE;
786 ctx->flags |= CTX_FLAG_EAP_RESTART | CTX_FLAG_EAP_PORT_ENABLED;
788 /* poke EAP state machine */
789 if (eap_peer_sm_step(ctx->initiatorCtx.eap) != 0) {
790 *minor = GSSEAP_PEER_SM_STEP_FAILURE;
791 return GSS_S_FAILURE;
794 GSSEAP_SM_TRANSITION_NEXT(ctx);
798 return GSS_S_CONTINUE_NEEDED;
802 eapGssSmInitAuthenticate(OM_uint32 *minor,
803 gss_cred_id_t cred GSSEAP_UNUSED,
805 gss_name_t target GSSEAP_UNUSED,
806 gss_OID mech GSSEAP_UNUSED,
807 OM_uint32 reqFlags GSSEAP_UNUSED,
808 OM_uint32 timeReq GSSEAP_UNUSED,
809 gss_channel_bindings_t chanBindings GSSEAP_UNUSED,
810 gss_buffer_t inputToken GSSEAP_UNUSED,
811 gss_buffer_t outputToken,
816 struct wpabuf *resp = NULL;
820 GSSEAP_ASSERT(inputToken != GSS_C_NO_BUFFER);
822 major = peerConfigInit(minor, ctx);
823 if (GSS_ERROR(major))
826 GSSEAP_ASSERT(ctx->initiatorCtx.eap != NULL);
827 GSSEAP_ASSERT(ctx->flags & CTX_FLAG_EAP_PORT_ENABLED);
829 ctx->flags |= CTX_FLAG_EAP_REQ; /* we have a Request from the acceptor */
831 wpabuf_set(&ctx->initiatorCtx.reqData,
832 inputToken->value, inputToken->length);
834 major = GSS_S_CONTINUE_NEEDED;
836 eap_peer_sm_step(ctx->initiatorCtx.eap);
837 if (ctx->flags & CTX_FLAG_EAP_RESP) {
838 ctx->flags &= ~(CTX_FLAG_EAP_RESP);
840 resp = eap_get_eapRespData(ctx->initiatorCtx.eap);
841 } else if (ctx->flags & CTX_FLAG_EAP_SUCCESS) {
842 major = initReady(minor, ctx);
843 if (GSS_ERROR(major))
846 ctx->flags &= ~(CTX_FLAG_EAP_SUCCESS);
847 major = GSS_S_CONTINUE_NEEDED;
848 GSSEAP_SM_TRANSITION_NEXT(ctx);
849 } else if (ctx->flags & CTX_FLAG_EAP_FAIL) {
850 major = GSS_S_DEFECTIVE_CREDENTIAL;
851 *minor = GSSEAP_PEER_AUTH_FAILURE;
853 major = GSS_S_DEFECTIVE_TOKEN;
854 *minor = GSSEAP_PEER_BAD_MESSAGE;
860 gss_buffer_desc respBuf;
862 GSSEAP_ASSERT(major == GSS_S_CONTINUE_NEEDED);
864 respBuf.length = wpabuf_len(resp);
865 respBuf.value = (void *)wpabuf_head(resp);
867 tmpMajor = duplicateBuffer(&tmpMinor, &respBuf, outputToken);
868 if (GSS_ERROR(tmpMajor)) {
873 *smFlags |= SM_FLAG_OUTPUT_TOKEN_CRITICAL;
876 wpabuf_set(&ctx->initiatorCtx.reqData, NULL, 0);
877 peerConfigFree(&tmpMinor, ctx);
883 eapGssSmInitGssFlags(OM_uint32 *minor,
884 gss_cred_id_t cred GSSEAP_UNUSED,
886 gss_name_t target GSSEAP_UNUSED,
887 gss_OID mech GSSEAP_UNUSED,
888 OM_uint32 reqFlags GSSEAP_UNUSED,
889 OM_uint32 timeReq GSSEAP_UNUSED,
890 gss_channel_bindings_t chanBindings GSSEAP_UNUSED,
891 gss_buffer_t inputToken GSSEAP_UNUSED,
892 gss_buffer_t outputToken,
893 OM_uint32 *smFlags GSSEAP_UNUSED)
895 unsigned char wireFlags[4];
896 gss_buffer_desc flagsBuf;
898 store_uint32_be(ctx->gssFlags & GSSEAP_WIRE_FLAGS_MASK, wireFlags);
900 flagsBuf.length = sizeof(wireFlags);
901 flagsBuf.value = wireFlags;
903 return duplicateBuffer(minor, &flagsBuf, outputToken);
907 eapGssSmInitGssChannelBindings(OM_uint32 *minor,
908 gss_cred_id_t cred GSSEAP_UNUSED,
910 gss_name_t target GSSEAP_UNUSED,
911 gss_OID mech GSSEAP_UNUSED,
912 OM_uint32 reqFlags GSSEAP_UNUSED,
913 OM_uint32 timeReq GSSEAP_UNUSED,
914 gss_channel_bindings_t chanBindings,
915 gss_buffer_t inputToken GSSEAP_UNUSED,
916 gss_buffer_t outputToken,
920 gss_buffer_desc buffer = GSS_C_EMPTY_BUFFER;
922 if (chanBindings != GSS_C_NO_CHANNEL_BINDINGS)
923 buffer = chanBindings->application_data;
925 major = gssEapWrap(minor, ctx, TRUE, GSS_C_QOP_DEFAULT,
926 &buffer, NULL, outputToken);
927 if (GSS_ERROR(major))
930 GSSEAP_ASSERT(outputToken->value != NULL);
933 *smFlags |= SM_FLAG_OUTPUT_TOKEN_CRITICAL;
935 return GSS_S_CONTINUE_NEEDED;
939 eapGssSmInitInitiatorMIC(OM_uint32 *minor,
940 gss_cred_id_t cred GSSEAP_UNUSED,
942 gss_name_t target GSSEAP_UNUSED,
943 gss_OID mech GSSEAP_UNUSED,
944 OM_uint32 reqFlags GSSEAP_UNUSED,
945 OM_uint32 timeReq GSSEAP_UNUSED,
946 gss_channel_bindings_t chanBindings GSSEAP_UNUSED,
947 gss_buffer_t inputToken GSSEAP_UNUSED,
948 gss_buffer_t outputToken,
953 major = gssEapMakeTokenMIC(minor, ctx, outputToken);
954 if (GSS_ERROR(major))
957 GSSEAP_SM_TRANSITION_NEXT(ctx);
960 *smFlags |= SM_FLAG_OUTPUT_TOKEN_CRITICAL;
962 return GSS_S_CONTINUE_NEEDED;
965 #ifdef GSSEAP_ENABLE_REAUTH
967 eapGssSmInitReauthCreds(OM_uint32 *minor,
970 gss_name_t target GSSEAP_UNUSED,
971 gss_OID mech GSSEAP_UNUSED,
972 OM_uint32 reqFlags GSSEAP_UNUSED,
973 OM_uint32 timeReq GSSEAP_UNUSED,
974 gss_channel_bindings_t chanBindings GSSEAP_UNUSED,
975 gss_buffer_t inputToken,
976 gss_buffer_t outputToken GSSEAP_UNUSED,
977 OM_uint32 *smFlags GSSEAP_UNUSED)
981 if (ctx->gssFlags & GSS_C_MUTUAL_FLAG) {
982 major = gssEapStoreReauthCreds(minor, ctx, cred, inputToken);
983 if (GSS_ERROR(major))
988 return GSS_S_CONTINUE_NEEDED;
990 #endif /* GSSEAP_ENABLE_REAUTH */
993 eapGssSmInitAcceptorMIC(OM_uint32 *minor,
994 gss_cred_id_t cred GSSEAP_UNUSED,
996 gss_name_t target GSSEAP_UNUSED,
997 gss_OID mech GSSEAP_UNUSED,
998 OM_uint32 reqFlags GSSEAP_UNUSED,
999 OM_uint32 timeReq GSSEAP_UNUSED,
1000 gss_channel_bindings_t chanBindings GSSEAP_UNUSED,
1001 gss_buffer_t inputToken,
1002 gss_buffer_t outputToken GSSEAP_UNUSED,
1003 OM_uint32 *smFlags GSSEAP_UNUSED)
1007 major = gssEapVerifyTokenMIC(minor, ctx, inputToken);
1008 if (GSS_ERROR(major))
1011 GSSEAP_SM_TRANSITION(ctx, GSSEAP_STATE_ESTABLISHED);
1015 return GSS_S_COMPLETE;
1018 static struct gss_eap_sm eapGssInitiatorSm[] = {
1020 ITOK_TYPE_CONTEXT_ERR,
1022 GSSEAP_STATE_ALL & ~(GSSEAP_STATE_INITIAL),
1027 ITOK_TYPE_ACCEPTOR_NAME_RESP,
1028 ITOK_TYPE_ACCEPTOR_NAME_REQ,
1029 GSSEAP_STATE_INITIAL | GSSEAP_STATE_AUTHENTICATE,
1031 eapGssSmInitAcceptorName
1036 ITOK_TYPE_VENDOR_INFO,
1037 GSSEAP_STATE_INITIAL,
1039 eapGssSmInitVendorInfo
1042 #ifdef GSSEAP_ENABLE_REAUTH
1044 ITOK_TYPE_REAUTH_RESP,
1045 ITOK_TYPE_REAUTH_REQ,
1046 GSSEAP_STATE_INITIAL | GSSEAP_STATE_REAUTHENTICATE,
1048 eapGssSmInitGssReauth
1054 #ifdef GSSEAP_ENABLE_REAUTH
1055 GSSEAP_STATE_REAUTHENTICATE |
1057 GSSEAP_STATE_INITIAL,
1058 SM_ITOK_FLAG_REQUIRED,
1059 eapGssSmInitIdentity
1064 GSSEAP_STATE_AUTHENTICATE,
1065 SM_ITOK_FLAG_REQUIRED,
1066 eapGssSmInitAuthenticate
1070 ITOK_TYPE_GSS_FLAGS,
1071 GSSEAP_STATE_INITIATOR_EXTS,
1073 eapGssSmInitGssFlags
1077 ITOK_TYPE_GSS_CHANNEL_BINDINGS,
1078 GSSEAP_STATE_INITIATOR_EXTS,
1079 SM_ITOK_FLAG_REQUIRED,
1080 eapGssSmInitGssChannelBindings
1084 ITOK_TYPE_INITIATOR_MIC,
1085 GSSEAP_STATE_INITIATOR_EXTS,
1086 SM_ITOK_FLAG_REQUIRED,
1087 eapGssSmInitInitiatorMIC
1089 #ifdef GSSEAP_ENABLE_REAUTH
1091 ITOK_TYPE_REAUTH_CREDS,
1093 GSSEAP_STATE_ACCEPTOR_EXTS,
1095 eapGssSmInitReauthCreds
1098 /* other extensions go here */
1100 ITOK_TYPE_ACCEPTOR_MIC,
1102 GSSEAP_STATE_ACCEPTOR_EXTS,
1103 SM_ITOK_FLAG_REQUIRED,
1104 eapGssSmInitAcceptorMIC
1109 gssEapInitSecContext(OM_uint32 *minor,
1112 gss_name_t target_name,
1114 OM_uint32 req_flags,
1116 gss_channel_bindings_t input_chan_bindings,
1117 gss_buffer_t input_token,
1118 gss_OID *actual_mech_type,
1119 gss_buffer_t output_token,
1120 OM_uint32 *ret_flags,
1121 OM_uint32 *time_rec)
1123 OM_uint32 major, tmpMinor;
1124 int initialContextToken = (ctx->mechanismUsed == GSS_C_NO_OID);
1127 * XXX is acquiring the credential lock here necessary? The password is
1128 * mutable but the contract could specify that this is not updated whilst
1129 * a context is being initialized.
1131 if (cred != GSS_C_NO_CREDENTIAL)
1132 GSSEAP_MUTEX_LOCK(&cred->mutex);
1134 if (ctx->cred == GSS_C_NO_CREDENTIAL) {
1135 major = gssEapResolveInitiatorCred(minor, cred, target_name, &ctx->cred);
1136 if (GSS_ERROR(major))
1139 GSSEAP_ASSERT(ctx->cred != GSS_C_NO_CREDENTIAL);
1142 GSSEAP_MUTEX_LOCK(&ctx->cred->mutex);
1144 GSSEAP_ASSERT(ctx->cred->flags & CRED_FLAG_RESOLVED);
1145 GSSEAP_ASSERT(ctx->cred->flags & CRED_FLAG_INITIATE);
1147 if (initialContextToken) {
1148 major = initBegin(minor, ctx, target_name, mech_type,
1149 req_flags, time_req, input_chan_bindings);
1150 if (GSS_ERROR(major))
1154 major = gssEapSmStep(minor,
1161 input_chan_bindings,
1165 sizeof(eapGssInitiatorSm) / sizeof(eapGssInitiatorSm[0]));
1166 if (GSS_ERROR(major))
1169 if (actual_mech_type != NULL) {
1172 tmpMajor = gssEapCanonicalizeOid(&tmpMinor, ctx->mechanismUsed, 0, actual_mech_type);
1173 if (GSS_ERROR(tmpMajor)) {
1180 if (ret_flags != NULL)
1181 *ret_flags = ctx->gssFlags;
1183 if (major == GSS_S_COMPLETE)
1185 if (time_rec != NULL)
1186 gssEapContextTime(&tmpMinor, ctx, time_rec);
1188 GSSEAP_ASSERT(CTX_IS_ESTABLISHED(ctx) || major == GSS_S_CONTINUE_NEEDED);
1191 if (cred != GSS_C_NO_CREDENTIAL)
1192 GSSEAP_MUTEX_UNLOCK(&cred->mutex);
1193 if (ctx->cred != GSS_C_NO_CREDENTIAL)
1194 GSSEAP_MUTEX_UNLOCK(&ctx->cred->mutex);
1199 OM_uint32 GSSAPI_CALLCONV
1200 gss_init_sec_context(OM_uint32 *minor,
1202 gss_ctx_id_t *context_handle,
1203 gss_name_t target_name,
1205 OM_uint32 req_flags,
1207 gss_channel_bindings_t input_chan_bindings,
1208 gss_buffer_t input_token,
1209 gss_OID *actual_mech_type,
1210 gss_buffer_t output_token,
1211 OM_uint32 *ret_flags,
1212 OM_uint32 *time_rec)
1214 OM_uint32 major, tmpMinor;
1215 gss_ctx_id_t ctx = *context_handle;
1219 output_token->length = 0;
1220 output_token->value = NULL;
1222 if (ctx == GSS_C_NO_CONTEXT) {
1223 if (input_token != GSS_C_NO_BUFFER && input_token->length != 0) {
1224 *minor = GSSEAP_WRONG_SIZE;
1225 return GSS_S_DEFECTIVE_TOKEN;
1228 major = gssEapAllocContext(minor, &ctx);
1229 if (GSS_ERROR(major))
1232 ctx->flags |= CTX_FLAG_INITIATOR;
1234 *context_handle = ctx;
1237 GSSEAP_MUTEX_LOCK(&ctx->mutex);
1239 major = gssEapInitSecContext(minor,
1246 input_chan_bindings,
1253 GSSEAP_MUTEX_UNLOCK(&ctx->mutex);
1255 if (GSS_ERROR(major))
1256 gssEapReleaseContext(&tmpMinor, context_handle);