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 int component, components = 0;
212 unsigned int requested = 0;
213 krb5_principal princ;
214 gss_buffer_desc nameBuf;
215 OM_uint32 major = GSS_S_COMPLETE;
216 krb5_context krbContext = NULL;
218 /* must have acceptor name, but already checked in
219 * eapGssSmInitAcceptorName(), so maybe redunadant
220 * to do so here as well? */
221 if (!ctx->acceptorName) {
222 *minor = GSSEAP_NO_ACCEPTOR_NAME;
223 return GSS_S_BAD_NAME;
226 princ = ctx->acceptorName->krbPrincipal;
228 krbPrincComponentToGssBuffer(princ, 0, &nameBuf);
229 if (nameBuf.length > 0) {
230 major = gssEapRadiusAddAttr(minor, &buf, PW_GSS_ACCEPTOR_SERVICE_NAME,
231 VENDORPEC_UKERNA, &nameBuf);
232 if (GSS_ERROR(major))
233 goto init_chbind_cleanup;
234 requested |= CHBIND_SERVICE_NAME_FLAG;
237 krbPrincComponentToGssBuffer(princ, 1, &nameBuf);
238 if (nameBuf.length > 0) {
239 major = gssEapRadiusAddAttr(minor, &buf, PW_GSS_ACCEPTOR_HOST_NAME,
240 VENDORPEC_UKERNA, &nameBuf);
241 if (GSS_ERROR(major))
242 goto init_chbind_cleanup;
243 requested |= CHBIND_HOST_NAME_FLAG;
246 GSSEAP_KRB_INIT(&krbContext);
247 *minor = krbPrincUnparseServiceSpecifics(krbContext, princ, &nameBuf);
249 goto init_chbind_cleanup;
251 if (nameBuf.length > 0) {
252 major = gssEapRadiusAddAttr(minor, &buf,
253 PW_GSS_ACCEPTOR_SERVICE_SPECIFIC,
254 VENDORPEC_UKERNA, &nameBuf);
255 if (GSS_ERROR(major)) {
256 krbFreeUnparsedName(krbContext, &nameBuf);
257 goto init_chbind_cleanup;
259 requested |= CHBIND_SERVICE_SPECIFIC_FLAG;
261 krbFreeUnparsedName(krbContext, &nameBuf);
263 krbPrincRealmToGssBuffer(princ, &nameBuf);
264 if (nameBuf.length > 0) {
265 major = gssEapRadiusAddAttr(minor, &buf,
266 PW_GSS_ACCEPTOR_REALM_NAME,
267 VENDORPEC_UKERNA, &nameBuf);
268 requested |= CHBIND_REALM_NAME_FLAG;
273 *minor = GSSEAP_BAD_ACCEPTOR_NAME;
274 return GSS_S_BAD_NAME;
276 ctx->initiatorCtx.chbindData = buf;
277 ctx->initiatorCtx.chbindReqFlags = requested;
285 peerProcessChbindResponse(void *context, int code, int nsid,
286 u8 *data, size_t len)
288 radius_parser msg, vendor_specific;
289 gss_ctx_id_t ctx = (gss_ctx_id_t )context;
297 if (nsid != CHBIND_NSID_RADIUS)
299 msg = radius_parser_start(data, len);
302 while (radius_parser_parse_tlv(msg, &type, &vendor_id, &vsadata,
303 &vsadata_len) == 0) {
308 if ((type != RADIUS_ATTR_VENDOR_SPECIFIC) ||
309 (vendor_id != VENDORPEC_UKERNA))
311 vendor_specific = radius_parser_start(vsadata, vsadata_len);
312 if (!vendor_specific)
314 while (radius_parser_parse_vendor_specific(vendor_specific,
318 switch (vendor_type) {
319 case PW_GSS_ACCEPTOR_SERVICE_NAME:
320 accepted |= CHBIND_SERVICE_NAME_FLAG;
322 case PW_GSS_ACCEPTOR_HOST_NAME:
323 accepted |= CHBIND_HOST_NAME_FLAG;
325 case PW_GSS_ACCEPTOR_SERVICE_SPECIFIC:
326 accepted |= CHBIND_SERVICE_SPECIFIC_FLAG;
328 case PW_GSS_ACCEPTOR_REALM_NAME:
329 accepted |= CHBIND_REALM_NAME_FLAG;
333 radius_parser_finish(vendor_specific);
335 radius_parser_finish(msg);
336 if ((code == CHBIND_CODE_SUCCESS) &&
337 (accepted == ctx->initiatorCtx.chbindReqFlags)) {
338 ctx->flags |= CTX_FLAG_EAP_CHBIND_ACCEPT;
339 ctx->gssFlags |= GSS_C_MUTUAL_FLAG;
347 peerConfigInit(OM_uint32 *minor, gss_ctx_id_t ctx)
350 krb5_context krbContext;
351 struct eap_peer_config *eapPeerConfig = &ctx->initiatorCtx.eapPeerConfig;
352 gss_buffer_desc identity = GSS_C_EMPTY_BUFFER;
353 gss_buffer_desc realm = GSS_C_EMPTY_BUFFER;
354 gss_cred_id_t cred = ctx->cred;
356 eapPeerConfig->identity = NULL;
357 eapPeerConfig->identity_len = 0;
358 eapPeerConfig->anonymous_identity = NULL;
359 eapPeerConfig->anonymous_identity_len = 0;
360 eapPeerConfig->password = NULL;
361 eapPeerConfig->password_len = 0;
363 GSSEAP_ASSERT(cred != GSS_C_NO_CREDENTIAL);
365 GSSEAP_KRB_INIT(&krbContext);
367 eapPeerConfig->fragment_size = 1024;
372 GSSEAP_ASSERT(cred->name != GSS_C_NO_NAME);
374 if ((cred->name->flags & (NAME_FLAG_NAI | NAME_FLAG_SERVICE)) == 0) {
375 *minor = GSSEAP_BAD_INITIATOR_NAME;
376 return GSS_S_BAD_NAME;
380 major = gssEapDisplayName(minor, cred->name, &identity, NULL);
381 if (GSS_ERROR(major))
384 eapPeerConfig->identity = (unsigned char *)identity.value;
385 eapPeerConfig->identity_len = identity.length;
387 krbPrincRealmToGssBuffer(cred->name->krbPrincipal, &realm);
389 /* anonymous_identity */
390 eapPeerConfig->anonymous_identity = GSSEAP_MALLOC(realm.length + 2);
391 if (eapPeerConfig->anonymous_identity == NULL) {
393 return GSS_S_FAILURE;
396 eapPeerConfig->anonymous_identity[0] = '@';
397 memcpy(eapPeerConfig->anonymous_identity + 1, realm.value, realm.length);
398 eapPeerConfig->anonymous_identity[1 + realm.length] = '\0';
399 eapPeerConfig->anonymous_identity_len = 1 + realm.length;
402 eapPeerConfig->password = (unsigned char *)cred->password.value;
403 eapPeerConfig->password_len = cred->password.length;
406 eapPeerConfig->ca_cert = (unsigned char *)cred->caCertificate.value;
407 eapPeerConfig->subject_match = (unsigned char *)cred->subjectNameConstraint.value;
408 eapPeerConfig->altsubject_match = (unsigned char *)cred->subjectAltNameConstraint.value;
410 /* eap channel binding */
411 if (ctx->initiatorCtx.chbindData)
413 struct eap_peer_chbind_config *chbind_config =
414 (struct eap_peer_chbind_config *)
415 GSSEAP_MALLOC(sizeof(struct eap_peer_chbind_config));
416 if (chbind_config == NULL) {
418 return GSS_S_FAILURE;
421 chbind_config->req_data = wpabuf_mhead_u8(ctx->initiatorCtx.chbindData);
422 chbind_config->req_data_len = wpabuf_len(ctx->initiatorCtx.chbindData);
423 chbind_config->nsid = CHBIND_NSID_RADIUS;
424 chbind_config->response_cb = &peerProcessChbindResponse;
425 chbind_config->ctx = ctx;
426 eapPeerConfig->chbind_config = chbind_config;
427 eapPeerConfig->chbind_config_len = 1;
429 eapPeerConfig->chbind_config = NULL;
430 eapPeerConfig->chbind_config_len = 0;
433 return GSS_S_COMPLETE;
437 peerConfigFree(OM_uint32 *minor,
440 struct eap_peer_config *eapPeerConfig = &ctx->initiatorCtx.eapPeerConfig;
442 if (eapPeerConfig->identity != NULL) {
443 GSSEAP_FREE(eapPeerConfig->identity);
444 eapPeerConfig->identity = NULL;
445 eapPeerConfig->identity_len = 0;
448 if (eapPeerConfig->anonymous_identity != NULL) {
449 GSSEAP_FREE(eapPeerConfig->anonymous_identity);
450 eapPeerConfig->anonymous_identity = NULL;
451 eapPeerConfig->anonymous_identity_len = 0;
455 return GSS_S_COMPLETE;
459 * Mark an initiator context as ready for cryptographic operations
462 initReady(OM_uint32 *minor, gss_ctx_id_t ctx, OM_uint32 reqFlags)
465 const unsigned char *key;
468 /* Cache encryption type derived from selected mechanism OID */
469 major = gssEapOidToEnctype(minor, ctx->mechanismUsed, &ctx->encryptionType);
470 if (GSS_ERROR(major))
473 if (!eap_key_available(ctx->initiatorCtx.eap)) {
474 *minor = GSSEAP_KEY_UNAVAILABLE;
475 return GSS_S_UNAVAILABLE;
478 key = eap_get_eapKeyData(ctx->initiatorCtx.eap, &keyLength);
480 if (keyLength < EAP_EMSK_LEN) {
481 *minor = GSSEAP_KEY_TOO_SHORT;
482 return GSS_S_UNAVAILABLE;
485 major = gssEapDeriveRfc3961Key(minor,
486 &key[EAP_EMSK_LEN / 2],
490 if (GSS_ERROR(major))
493 major = rfc3961ChecksumTypeForKey(minor, &ctx->rfc3961Key,
495 if (GSS_ERROR(major))
498 major = sequenceInit(minor,
501 ((ctx->gssFlags & GSS_C_REPLAY_FLAG) != 0),
502 ((ctx->gssFlags & GSS_C_SEQUENCE_FLAG) != 0),
504 if (GSS_ERROR(major))
508 return GSS_S_COMPLETE;
512 initBegin(OM_uint32 *minor,
516 OM_uint32 reqFlags GSSEAP_UNUSED,
518 gss_channel_bindings_t chanBindings GSSEAP_UNUSED)
521 gss_cred_id_t cred = ctx->cred;
523 GSSEAP_ASSERT(cred != GSS_C_NO_CREDENTIAL);
525 if (cred->expiryTime)
526 ctx->expiryTime = cred->expiryTime;
527 else if (timeReq == 0 || timeReq == GSS_C_INDEFINITE)
530 ctx->expiryTime = time(NULL) + timeReq;
533 * The credential mutex protects its name, however we need to
534 * explicitly lock the acceptor name (unlikely as it may be
535 * that it has attributes set on it).
537 major = gssEapDuplicateName(minor, cred->name, &ctx->initiatorName);
538 if (GSS_ERROR(major))
541 if (target != GSS_C_NO_NAME) {
542 GSSEAP_MUTEX_LOCK(&target->mutex);
544 major = gssEapDuplicateName(minor, target, &ctx->acceptorName);
545 if (GSS_ERROR(major)) {
546 GSSEAP_MUTEX_UNLOCK(&target->mutex);
550 GSSEAP_MUTEX_UNLOCK(&target->mutex);
553 major = gssEapCanonicalizeOid(minor,
555 OID_FLAG_NULL_VALID | OID_FLAG_MAP_NULL_TO_DEFAULT_MECH,
556 &ctx->mechanismUsed);
557 if (GSS_ERROR(major))
560 /* If credentials were provided, check they're usable with this mech */
561 if (!gssEapCredAvailable(cred, ctx->mechanismUsed)) {
562 *minor = GSSEAP_CRED_MECH_MISMATCH;
563 return GSS_S_BAD_MECH;
567 return GSS_S_COMPLETE;
571 eapGssSmInitError(OM_uint32 *minor,
572 gss_cred_id_t cred GSSEAP_UNUSED,
573 gss_ctx_id_t ctx GSSEAP_UNUSED,
574 gss_name_t target GSSEAP_UNUSED,
575 gss_OID mech GSSEAP_UNUSED,
576 OM_uint32 reqFlags GSSEAP_UNUSED,
577 OM_uint32 timeReq GSSEAP_UNUSED,
578 gss_channel_bindings_t chanBindings GSSEAP_UNUSED,
579 gss_buffer_t inputToken,
580 gss_buffer_t outputToken GSSEAP_UNUSED,
581 OM_uint32 *smFlags GSSEAP_UNUSED)
586 if (inputToken->length < 8) {
587 *minor = GSSEAP_TOK_TRUNC;
588 return GSS_S_DEFECTIVE_TOKEN;
591 p = (unsigned char *)inputToken->value;
593 major = load_uint32_be(&p[0]);
594 *minor = ERROR_TABLE_BASE_eapg + load_uint32_be(&p[4]);
596 if (!GSS_ERROR(major) || !IS_WIRE_ERROR(*minor)) {
597 major = GSS_S_FAILURE;
598 *minor = GSSEAP_BAD_ERROR_TOKEN;
601 GSSEAP_ASSERT(GSS_ERROR(major));
606 #ifdef GSSEAP_ENABLE_REAUTH
608 eapGssSmInitGssReauth(OM_uint32 *minor,
612 gss_OID mech GSSEAP_UNUSED,
615 gss_channel_bindings_t chanBindings,
616 gss_buffer_t inputToken,
617 gss_buffer_t outputToken,
618 OM_uint32 *smFlags GSSEAP_UNUSED)
620 OM_uint32 major, tmpMinor;
621 gss_name_t mechTarget = GSS_C_NO_NAME;
622 gss_OID actualMech = GSS_C_NO_OID;
623 OM_uint32 gssFlags, timeRec;
626 * Here we use the passed in credential handle because the resolved
627 * context credential does not currently have the reauth creds.
629 if (GSSEAP_SM_STATE(ctx) == GSSEAP_STATE_INITIAL) {
630 if (!gssEapCanReauthP(cred, target, timeReq))
631 return GSS_S_CONTINUE_NEEDED;
633 ctx->flags |= CTX_FLAG_KRB_REAUTH;
634 } else if ((ctx->flags & CTX_FLAG_KRB_REAUTH) == 0) {
635 major = GSS_S_DEFECTIVE_TOKEN;
636 *minor = GSSEAP_WRONG_ITOK;
640 GSSEAP_ASSERT(cred != GSS_C_NO_CREDENTIAL);
642 major = gssEapMechToGlueName(minor, target, &mechTarget);
643 if (GSS_ERROR(major))
646 major = gssInitSecContext(minor,
650 (gss_OID)gss_mech_krb5,
651 reqFlags | GSS_C_MUTUAL_FLAG,
659 if (GSS_ERROR(major))
662 ctx->gssFlags = gssFlags;
664 if (major == GSS_S_COMPLETE) {
665 GSSEAP_ASSERT(GSSEAP_SM_STATE(ctx) == GSSEAP_STATE_REAUTHENTICATE);
667 major = gssEapReauthComplete(minor, ctx, cred, actualMech, timeRec);
668 if (GSS_ERROR(major))
670 GSSEAP_SM_TRANSITION(ctx, GSSEAP_STATE_ESTABLISHED);
672 GSSEAP_SM_TRANSITION(ctx, GSSEAP_STATE_REAUTHENTICATE);
676 gssReleaseName(&tmpMinor, &mechTarget);
680 #endif /* GSSEAP_ENABLE_REAUTH */
684 eapGssSmInitVendorInfo(OM_uint32 *minor,
685 gss_cred_id_t cred GSSEAP_UNUSED,
686 gss_ctx_id_t ctx GSSEAP_UNUSED,
687 gss_name_t target GSSEAP_UNUSED,
688 gss_OID mech GSSEAP_UNUSED,
689 OM_uint32 reqFlags GSSEAP_UNUSED,
690 OM_uint32 timeReq GSSEAP_UNUSED,
691 gss_channel_bindings_t chanBindings GSSEAP_UNUSED,
692 gss_buffer_t inputToken GSSEAP_UNUSED,
693 gss_buffer_t outputToken,
694 OM_uint32 *smFlags GSSEAP_UNUSED)
698 major = makeStringBuffer(minor, "JANET(UK)", outputToken);
699 if (GSS_ERROR(major))
702 return GSS_S_CONTINUE_NEEDED;
707 eapGssSmInitAcceptorName(OM_uint32 *minor,
708 gss_cred_id_t cred GSSEAP_UNUSED,
710 gss_name_t target GSSEAP_UNUSED,
711 gss_OID mech GSSEAP_UNUSED,
712 OM_uint32 reqFlags GSSEAP_UNUSED,
713 OM_uint32 timeReq GSSEAP_UNUSED,
714 gss_channel_bindings_t chanBindings GSSEAP_UNUSED,
715 gss_buffer_t inputToken GSSEAP_UNUSED,
716 gss_buffer_t outputToken,
717 OM_uint32 *smFlags GSSEAP_UNUSED)
721 if (GSSEAP_SM_STATE(ctx) == GSSEAP_STATE_INITIAL &&
722 ctx->acceptorName != GSS_C_NO_NAME) {
724 /* Send desired target name to acceptor */
725 major = gssEapDisplayName(minor, ctx->acceptorName,
727 if (GSS_ERROR(major))
729 } else if (inputToken != GSS_C_NO_BUFFER &&
730 ctx->acceptorName == GSS_C_NO_NAME) {
731 /* Accept target name hint from acceptor */
732 major = gssEapImportName(minor, inputToken,
736 if (GSS_ERROR(major))
741 * Currently, other parts of the code assume that the acceptor name
742 * is available, hence this check.
744 if (ctx->acceptorName == GSS_C_NO_NAME) {
745 *minor = GSSEAP_NO_ACCEPTOR_NAME;
746 return GSS_S_FAILURE;
750 * Generate channel binding data
752 if (ctx->initiatorCtx.chbindData == NULL)
754 major = peerInitEapChannelBinding(minor, ctx);
755 if (GSS_ERROR(major))
759 return GSS_S_CONTINUE_NEEDED;
763 eapGssSmInitIdentity(OM_uint32 *minor,
764 gss_cred_id_t cred GSSEAP_UNUSED,
766 gss_name_t target GSSEAP_UNUSED,
767 gss_OID mech GSSEAP_UNUSED,
768 OM_uint32 reqFlags GSSEAP_UNUSED,
769 OM_uint32 timeReq GSSEAP_UNUSED,
770 gss_channel_bindings_t chanBindings GSSEAP_UNUSED,
771 gss_buffer_t inputToken GSSEAP_UNUSED,
772 gss_buffer_t outputToken GSSEAP_UNUSED,
775 struct eap_config eapConfig;
777 #ifdef GSSEAP_ENABLE_REAUTH
778 if (GSSEAP_SM_STATE(ctx) == GSSEAP_STATE_REAUTHENTICATE) {
781 /* server didn't support reauthentication, sent EAP request */
782 gssDeleteSecContext(&tmpMinor, &ctx->reauthCtx, GSS_C_NO_BUFFER);
783 ctx->flags &= ~(CTX_FLAG_KRB_REAUTH);
784 GSSEAP_SM_TRANSITION(ctx, GSSEAP_STATE_INITIAL);
787 *smFlags |= SM_FLAG_FORCE_SEND_TOKEN;
789 GSSEAP_ASSERT((ctx->flags & CTX_FLAG_KRB_REAUTH) == 0);
790 GSSEAP_ASSERT(inputToken == GSS_C_NO_BUFFER);
792 memset(&eapConfig, 0, sizeof(eapConfig));
794 ctx->initiatorCtx.eap = eap_peer_sm_init(ctx,
795 &gssEapPolicyCallbacks,
798 if (ctx->initiatorCtx.eap == NULL) {
799 *minor = GSSEAP_PEER_SM_INIT_FAILURE;
800 return GSS_S_FAILURE;
803 ctx->flags |= CTX_FLAG_EAP_RESTART | CTX_FLAG_EAP_PORT_ENABLED;
805 /* poke EAP state machine */
806 if (eap_peer_sm_step(ctx->initiatorCtx.eap) != 0) {
807 *minor = GSSEAP_PEER_SM_STEP_FAILURE;
808 return GSS_S_FAILURE;
811 GSSEAP_SM_TRANSITION_NEXT(ctx);
815 return GSS_S_CONTINUE_NEEDED;
819 eapGssSmInitAuthenticate(OM_uint32 *minor,
820 gss_cred_id_t cred GSSEAP_UNUSED,
822 gss_name_t target GSSEAP_UNUSED,
823 gss_OID mech GSSEAP_UNUSED,
824 OM_uint32 reqFlags GSSEAP_UNUSED,
825 OM_uint32 timeReq GSSEAP_UNUSED,
826 gss_channel_bindings_t chanBindings GSSEAP_UNUSED,
827 gss_buffer_t inputToken GSSEAP_UNUSED,
828 gss_buffer_t outputToken,
833 struct wpabuf *resp = NULL;
837 GSSEAP_ASSERT(inputToken != GSS_C_NO_BUFFER);
839 major = peerConfigInit(minor, ctx);
840 if (GSS_ERROR(major))
843 GSSEAP_ASSERT(ctx->initiatorCtx.eap != NULL);
844 GSSEAP_ASSERT(ctx->flags & CTX_FLAG_EAP_PORT_ENABLED);
846 ctx->flags |= CTX_FLAG_EAP_REQ; /* we have a Request from the acceptor */
848 wpabuf_set(&ctx->initiatorCtx.reqData,
849 inputToken->value, inputToken->length);
851 major = GSS_S_CONTINUE_NEEDED;
853 eap_peer_sm_step(ctx->initiatorCtx.eap);
854 if (ctx->flags & CTX_FLAG_EAP_RESP) {
855 ctx->flags &= ~(CTX_FLAG_EAP_RESP);
857 resp = eap_get_eapRespData(ctx->initiatorCtx.eap);
858 } else if (ctx->flags & CTX_FLAG_EAP_SUCCESS) {
859 major = initReady(minor, ctx, reqFlags);
860 if (GSS_ERROR(major))
863 ctx->flags &= ~(CTX_FLAG_EAP_SUCCESS);
864 major = GSS_S_CONTINUE_NEEDED;
865 GSSEAP_SM_TRANSITION_NEXT(ctx);
866 } else if (ctx->flags & CTX_FLAG_EAP_FAIL) {
867 major = GSS_S_DEFECTIVE_CREDENTIAL;
868 *minor = GSSEAP_PEER_AUTH_FAILURE;
870 major = GSS_S_DEFECTIVE_TOKEN;
871 *minor = GSSEAP_PEER_BAD_MESSAGE;
877 gss_buffer_desc respBuf;
879 GSSEAP_ASSERT(major == GSS_S_CONTINUE_NEEDED);
881 respBuf.length = wpabuf_len(resp);
882 respBuf.value = (void *)wpabuf_head(resp);
884 tmpMajor = duplicateBuffer(&tmpMinor, &respBuf, outputToken);
885 if (GSS_ERROR(tmpMajor)) {
890 *smFlags |= SM_FLAG_OUTPUT_TOKEN_CRITICAL;
893 wpabuf_set(&ctx->initiatorCtx.reqData, NULL, 0);
894 peerConfigFree(&tmpMinor, ctx);
900 eapGssSmInitGssFlags(OM_uint32 *minor,
901 gss_cred_id_t cred GSSEAP_UNUSED,
903 gss_name_t target GSSEAP_UNUSED,
904 gss_OID mech GSSEAP_UNUSED,
905 OM_uint32 reqFlags GSSEAP_UNUSED,
906 OM_uint32 timeReq GSSEAP_UNUSED,
907 gss_channel_bindings_t chanBindings GSSEAP_UNUSED,
908 gss_buffer_t inputToken GSSEAP_UNUSED,
909 gss_buffer_t outputToken,
910 OM_uint32 *smFlags GSSEAP_UNUSED)
912 unsigned char wireFlags[4];
913 gss_buffer_desc flagsBuf;
915 store_uint32_be(ctx->gssFlags & GSSEAP_WIRE_FLAGS_MASK, wireFlags);
917 flagsBuf.length = sizeof(wireFlags);
918 flagsBuf.value = wireFlags;
920 return duplicateBuffer(minor, &flagsBuf, outputToken);
924 eapGssSmInitGssChannelBindings(OM_uint32 *minor,
925 gss_cred_id_t cred GSSEAP_UNUSED,
927 gss_name_t target GSSEAP_UNUSED,
928 gss_OID mech GSSEAP_UNUSED,
929 OM_uint32 reqFlags GSSEAP_UNUSED,
930 OM_uint32 timeReq GSSEAP_UNUSED,
931 gss_channel_bindings_t chanBindings,
932 gss_buffer_t inputToken GSSEAP_UNUSED,
933 gss_buffer_t outputToken,
937 gss_buffer_desc buffer = GSS_C_EMPTY_BUFFER;
939 if (chanBindings != GSS_C_NO_CHANNEL_BINDINGS)
940 buffer = chanBindings->application_data;
942 major = gssEapWrap(minor, ctx, TRUE, GSS_C_QOP_DEFAULT,
943 &buffer, NULL, outputToken);
944 if (GSS_ERROR(major))
947 GSSEAP_ASSERT(outputToken->value != NULL);
950 *smFlags |= SM_FLAG_OUTPUT_TOKEN_CRITICAL;
952 return GSS_S_CONTINUE_NEEDED;
956 eapGssSmInitInitiatorMIC(OM_uint32 *minor,
957 gss_cred_id_t cred GSSEAP_UNUSED,
959 gss_name_t target GSSEAP_UNUSED,
960 gss_OID mech GSSEAP_UNUSED,
961 OM_uint32 reqFlags GSSEAP_UNUSED,
962 OM_uint32 timeReq GSSEAP_UNUSED,
963 gss_channel_bindings_t chanBindings GSSEAP_UNUSED,
964 gss_buffer_t inputToken GSSEAP_UNUSED,
965 gss_buffer_t outputToken,
970 major = gssEapMakeTokenMIC(minor, ctx, outputToken);
971 if (GSS_ERROR(major))
974 GSSEAP_SM_TRANSITION_NEXT(ctx);
977 *smFlags |= SM_FLAG_OUTPUT_TOKEN_CRITICAL;
979 return GSS_S_CONTINUE_NEEDED;
982 #ifdef GSSEAP_ENABLE_REAUTH
984 eapGssSmInitReauthCreds(OM_uint32 *minor,
987 gss_name_t target GSSEAP_UNUSED,
988 gss_OID mech GSSEAP_UNUSED,
989 OM_uint32 reqFlags GSSEAP_UNUSED,
990 OM_uint32 timeReq GSSEAP_UNUSED,
991 gss_channel_bindings_t chanBindings GSSEAP_UNUSED,
992 gss_buffer_t inputToken,
993 gss_buffer_t outputToken GSSEAP_UNUSED,
994 OM_uint32 *smFlags GSSEAP_UNUSED)
998 if (ctx->gssFlags & GSS_C_MUTUAL_FLAG) {
999 major = gssEapStoreReauthCreds(minor, ctx, cred, inputToken);
1000 if (GSS_ERROR(major))
1005 return GSS_S_CONTINUE_NEEDED;
1007 #endif /* GSSEAP_ENABLE_REAUTH */
1010 eapGssSmInitAcceptorMIC(OM_uint32 *minor,
1011 gss_cred_id_t cred GSSEAP_UNUSED,
1013 gss_name_t target GSSEAP_UNUSED,
1014 gss_OID mech GSSEAP_UNUSED,
1015 OM_uint32 reqFlags GSSEAP_UNUSED,
1016 OM_uint32 timeReq GSSEAP_UNUSED,
1017 gss_channel_bindings_t chanBindings GSSEAP_UNUSED,
1018 gss_buffer_t inputToken,
1019 gss_buffer_t outputToken GSSEAP_UNUSED,
1020 OM_uint32 *smFlags GSSEAP_UNUSED)
1024 major = gssEapVerifyTokenMIC(minor, ctx, inputToken);
1025 if (GSS_ERROR(major))
1028 GSSEAP_SM_TRANSITION(ctx, GSSEAP_STATE_ESTABLISHED);
1032 return GSS_S_COMPLETE;
1035 static struct gss_eap_sm eapGssInitiatorSm[] = {
1037 ITOK_TYPE_CONTEXT_ERR,
1039 GSSEAP_STATE_ALL & ~(GSSEAP_STATE_INITIAL),
1044 ITOK_TYPE_ACCEPTOR_NAME_RESP,
1045 ITOK_TYPE_ACCEPTOR_NAME_REQ,
1046 GSSEAP_STATE_INITIAL | GSSEAP_STATE_AUTHENTICATE,
1048 eapGssSmInitAcceptorName
1053 ITOK_TYPE_VENDOR_INFO,
1054 GSSEAP_STATE_INITIAL,
1056 eapGssSmInitVendorInfo
1059 #ifdef GSSEAP_ENABLE_REAUTH
1061 ITOK_TYPE_REAUTH_RESP,
1062 ITOK_TYPE_REAUTH_REQ,
1063 GSSEAP_STATE_INITIAL | GSSEAP_STATE_REAUTHENTICATE,
1065 eapGssSmInitGssReauth
1071 #ifdef GSSEAP_ENABLE_REAUTH
1072 GSSEAP_STATE_REAUTHENTICATE |
1074 GSSEAP_STATE_INITIAL,
1075 SM_ITOK_FLAG_REQUIRED,
1076 eapGssSmInitIdentity
1081 GSSEAP_STATE_AUTHENTICATE,
1082 SM_ITOK_FLAG_REQUIRED,
1083 eapGssSmInitAuthenticate
1087 ITOK_TYPE_GSS_FLAGS,
1088 GSSEAP_STATE_INITIATOR_EXTS,
1090 eapGssSmInitGssFlags
1094 ITOK_TYPE_GSS_CHANNEL_BINDINGS,
1095 GSSEAP_STATE_INITIATOR_EXTS,
1096 SM_ITOK_FLAG_REQUIRED,
1097 eapGssSmInitGssChannelBindings
1101 ITOK_TYPE_INITIATOR_MIC,
1102 GSSEAP_STATE_INITIATOR_EXTS,
1103 SM_ITOK_FLAG_REQUIRED,
1104 eapGssSmInitInitiatorMIC
1106 #ifdef GSSEAP_ENABLE_REAUTH
1108 ITOK_TYPE_REAUTH_CREDS,
1110 GSSEAP_STATE_ACCEPTOR_EXTS,
1112 eapGssSmInitReauthCreds
1115 /* other extensions go here */
1117 ITOK_TYPE_ACCEPTOR_MIC,
1119 GSSEAP_STATE_ACCEPTOR_EXTS,
1120 SM_ITOK_FLAG_REQUIRED,
1121 eapGssSmInitAcceptorMIC
1126 gssEapInitSecContext(OM_uint32 *minor,
1129 gss_name_t target_name,
1131 OM_uint32 req_flags,
1133 gss_channel_bindings_t input_chan_bindings,
1134 gss_buffer_t input_token,
1135 gss_OID *actual_mech_type,
1136 gss_buffer_t output_token,
1137 OM_uint32 *ret_flags,
1138 OM_uint32 *time_rec)
1140 OM_uint32 major, tmpMinor;
1141 int initialContextToken = (ctx->mechanismUsed == GSS_C_NO_OID);
1144 * XXX is acquiring the credential lock here necessary? The password is
1145 * mutable but the contract could specify that this is not updated whilst
1146 * a context is being initialized.
1148 if (cred != GSS_C_NO_CREDENTIAL)
1149 GSSEAP_MUTEX_LOCK(&cred->mutex);
1151 if (ctx->cred == GSS_C_NO_CREDENTIAL) {
1152 major = gssEapResolveInitiatorCred(minor, cred, target_name, &ctx->cred);
1153 if (GSS_ERROR(major))
1156 GSSEAP_ASSERT(ctx->cred != GSS_C_NO_CREDENTIAL);
1159 GSSEAP_MUTEX_LOCK(&ctx->cred->mutex);
1161 GSSEAP_ASSERT(ctx->cred->flags & CRED_FLAG_RESOLVED);
1162 GSSEAP_ASSERT(ctx->cred->flags & CRED_FLAG_INITIATE);
1164 if (initialContextToken) {
1165 major = initBegin(minor, ctx, target_name, mech_type,
1166 req_flags, time_req, input_chan_bindings);
1167 if (GSS_ERROR(major))
1171 major = gssEapSmStep(minor,
1178 input_chan_bindings,
1182 sizeof(eapGssInitiatorSm) / sizeof(eapGssInitiatorSm[0]));
1183 if (GSS_ERROR(major))
1186 if (actual_mech_type != NULL) {
1189 tmpMajor = gssEapCanonicalizeOid(&tmpMinor, ctx->mechanismUsed, 0, actual_mech_type);
1190 if (GSS_ERROR(tmpMajor)) {
1197 if (ret_flags != NULL)
1198 *ret_flags = ctx->gssFlags;
1200 if (major == GSS_S_COMPLETE)
1202 if (time_rec != NULL)
1203 gssEapContextTime(&tmpMinor, ctx, time_rec);
1205 GSSEAP_ASSERT(CTX_IS_ESTABLISHED(ctx) || major == GSS_S_CONTINUE_NEEDED);
1208 if (cred != GSS_C_NO_CREDENTIAL)
1209 GSSEAP_MUTEX_UNLOCK(&cred->mutex);
1210 if (ctx->cred != GSS_C_NO_CREDENTIAL)
1211 GSSEAP_MUTEX_UNLOCK(&ctx->cred->mutex);
1216 OM_uint32 GSSAPI_CALLCONV
1217 gss_init_sec_context(OM_uint32 *minor,
1219 gss_ctx_id_t *context_handle,
1220 gss_name_t target_name,
1222 OM_uint32 req_flags,
1224 gss_channel_bindings_t input_chan_bindings,
1225 gss_buffer_t input_token,
1226 gss_OID *actual_mech_type,
1227 gss_buffer_t output_token,
1228 OM_uint32 *ret_flags,
1229 OM_uint32 *time_rec)
1231 OM_uint32 major, tmpMinor;
1232 gss_ctx_id_t ctx = *context_handle;
1236 output_token->length = 0;
1237 output_token->value = NULL;
1239 if (ctx == GSS_C_NO_CONTEXT) {
1240 if (input_token != GSS_C_NO_BUFFER && input_token->length != 0) {
1241 *minor = GSSEAP_WRONG_SIZE;
1242 return GSS_S_DEFECTIVE_TOKEN;
1245 major = gssEapAllocContext(minor, &ctx);
1246 if (GSS_ERROR(major))
1249 ctx->flags |= CTX_FLAG_INITIATOR;
1251 *context_handle = ctx;
1254 GSSEAP_MUTEX_LOCK(&ctx->mutex);
1256 major = gssEapInitSecContext(minor,
1263 input_chan_bindings,
1270 GSSEAP_MUTEX_UNLOCK(&ctx->mutex);
1272 if (GSS_ERROR(major))
1273 gssEapReleaseContext(&tmpMinor, context_handle);