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;
346 peerConfigInit(OM_uint32 *minor, gss_ctx_id_t ctx)
349 krb5_context krbContext;
350 struct eap_peer_config *eapPeerConfig = &ctx->initiatorCtx.eapPeerConfig;
351 gss_buffer_desc identity = GSS_C_EMPTY_BUFFER;
352 gss_buffer_desc realm = GSS_C_EMPTY_BUFFER;
353 gss_cred_id_t cred = ctx->cred;
355 eapPeerConfig->identity = NULL;
356 eapPeerConfig->identity_len = 0;
357 eapPeerConfig->anonymous_identity = NULL;
358 eapPeerConfig->anonymous_identity_len = 0;
359 eapPeerConfig->password = NULL;
360 eapPeerConfig->password_len = 0;
362 GSSEAP_ASSERT(cred != GSS_C_NO_CREDENTIAL);
364 GSSEAP_KRB_INIT(&krbContext);
366 eapPeerConfig->fragment_size = 1024;
371 GSSEAP_ASSERT(cred->name != GSS_C_NO_NAME);
373 if ((cred->name->flags & (NAME_FLAG_NAI | NAME_FLAG_SERVICE)) == 0) {
374 *minor = GSSEAP_BAD_INITIATOR_NAME;
375 return GSS_S_BAD_NAME;
379 major = gssEapDisplayName(minor, cred->name, &identity, NULL);
380 if (GSS_ERROR(major))
383 eapPeerConfig->identity = (unsigned char *)identity.value;
384 eapPeerConfig->identity_len = identity.length;
386 krbPrincRealmToGssBuffer(cred->name->krbPrincipal, &realm);
388 /* anonymous_identity */
389 eapPeerConfig->anonymous_identity = GSSEAP_MALLOC(realm.length + 2);
390 if (eapPeerConfig->anonymous_identity == NULL) {
392 return GSS_S_FAILURE;
395 eapPeerConfig->anonymous_identity[0] = '@';
396 memcpy(eapPeerConfig->anonymous_identity + 1, realm.value, realm.length);
397 eapPeerConfig->anonymous_identity[1 + realm.length] = '\0';
398 eapPeerConfig->anonymous_identity_len = 1 + realm.length;
401 eapPeerConfig->password = (unsigned char *)cred->password.value;
402 eapPeerConfig->password_len = cred->password.length;
405 eapPeerConfig->ca_cert = (unsigned char *)cred->caCertificate.value;
406 eapPeerConfig->subject_match = (unsigned char *)cred->subjectNameConstraint.value;
407 eapPeerConfig->altsubject_match = (unsigned char *)cred->subjectAltNameConstraint.value;
409 /* eap channel binding */
410 if (ctx->initiatorCtx.chbindData)
412 struct eap_peer_chbind_config *chbind_config =
413 (struct eap_peer_chbind_config *)
414 GSSEAP_MALLOC(sizeof(struct eap_peer_chbind_config));
415 if (chbind_config == NULL) {
417 return GSS_S_FAILURE;
420 chbind_config->req_data = wpabuf_mhead_u8(ctx->initiatorCtx.chbindData);
421 chbind_config->req_data_len = wpabuf_len(ctx->initiatorCtx.chbindData);
422 chbind_config->nsid = CHBIND_NSID_RADIUS;
423 chbind_config->response_cb = &peerProcessChbindResponse;
424 chbind_config->ctx = ctx;
425 eapPeerConfig->chbind_config = chbind_config;
426 eapPeerConfig->chbind_config_len = 1;
428 eapPeerConfig->chbind_config = NULL;
429 eapPeerConfig->chbind_config_len = 0;
432 return GSS_S_COMPLETE;
436 peerConfigFree(OM_uint32 *minor,
439 struct eap_peer_config *eapPeerConfig = &ctx->initiatorCtx.eapPeerConfig;
441 if (eapPeerConfig->identity != NULL) {
442 GSSEAP_FREE(eapPeerConfig->identity);
443 eapPeerConfig->identity = NULL;
444 eapPeerConfig->identity_len = 0;
447 if (eapPeerConfig->anonymous_identity != NULL) {
448 GSSEAP_FREE(eapPeerConfig->anonymous_identity);
449 eapPeerConfig->anonymous_identity = NULL;
450 eapPeerConfig->anonymous_identity_len = 0;
454 return GSS_S_COMPLETE;
458 * Mark an initiator context as ready for cryptographic operations
461 initReady(OM_uint32 *minor, gss_ctx_id_t ctx, OM_uint32 reqFlags)
464 const unsigned char *key;
468 /* XXX actually check for mutual auth */
469 if (reqFlags & GSS_C_MUTUAL_FLAG)
470 ctx->gssFlags |= GSS_C_MUTUAL_FLAG;
473 /* Cache encryption type derived from selected mechanism OID */
474 major = gssEapOidToEnctype(minor, ctx->mechanismUsed, &ctx->encryptionType);
475 if (GSS_ERROR(major))
478 if (!eap_key_available(ctx->initiatorCtx.eap)) {
479 *minor = GSSEAP_KEY_UNAVAILABLE;
480 return GSS_S_UNAVAILABLE;
483 key = eap_get_eapKeyData(ctx->initiatorCtx.eap, &keyLength);
485 if (keyLength < EAP_EMSK_LEN) {
486 *minor = GSSEAP_KEY_TOO_SHORT;
487 return GSS_S_UNAVAILABLE;
490 major = gssEapDeriveRfc3961Key(minor,
491 &key[EAP_EMSK_LEN / 2],
495 if (GSS_ERROR(major))
498 major = rfc3961ChecksumTypeForKey(minor, &ctx->rfc3961Key,
500 if (GSS_ERROR(major))
503 major = sequenceInit(minor,
506 ((ctx->gssFlags & GSS_C_REPLAY_FLAG) != 0),
507 ((ctx->gssFlags & GSS_C_SEQUENCE_FLAG) != 0),
509 if (GSS_ERROR(major))
513 return GSS_S_COMPLETE;
517 initBegin(OM_uint32 *minor,
521 OM_uint32 reqFlags GSSEAP_UNUSED,
523 gss_channel_bindings_t chanBindings GSSEAP_UNUSED)
526 gss_cred_id_t cred = ctx->cred;
528 GSSEAP_ASSERT(cred != GSS_C_NO_CREDENTIAL);
530 if (cred->expiryTime)
531 ctx->expiryTime = cred->expiryTime;
532 else if (timeReq == 0 || timeReq == GSS_C_INDEFINITE)
535 ctx->expiryTime = time(NULL) + timeReq;
538 * The credential mutex protects its name, however we need to
539 * explicitly lock the acceptor name (unlikely as it may be
540 * that it has attributes set on it).
542 major = gssEapDuplicateName(minor, cred->name, &ctx->initiatorName);
543 if (GSS_ERROR(major))
546 if (target != GSS_C_NO_NAME) {
547 GSSEAP_MUTEX_LOCK(&target->mutex);
549 major = gssEapDuplicateName(minor, target, &ctx->acceptorName);
550 if (GSS_ERROR(major)) {
551 GSSEAP_MUTEX_UNLOCK(&target->mutex);
555 GSSEAP_MUTEX_UNLOCK(&target->mutex);
558 major = gssEapCanonicalizeOid(minor,
560 OID_FLAG_NULL_VALID | OID_FLAG_MAP_NULL_TO_DEFAULT_MECH,
561 &ctx->mechanismUsed);
562 if (GSS_ERROR(major))
565 /* If credentials were provided, check they're usable with this mech */
566 if (!gssEapCredAvailable(cred, ctx->mechanismUsed)) {
567 *minor = GSSEAP_CRED_MECH_MISMATCH;
568 return GSS_S_BAD_MECH;
572 return GSS_S_COMPLETE;
576 eapGssSmInitError(OM_uint32 *minor,
577 gss_cred_id_t cred GSSEAP_UNUSED,
578 gss_ctx_id_t ctx GSSEAP_UNUSED,
579 gss_name_t target GSSEAP_UNUSED,
580 gss_OID mech GSSEAP_UNUSED,
581 OM_uint32 reqFlags GSSEAP_UNUSED,
582 OM_uint32 timeReq GSSEAP_UNUSED,
583 gss_channel_bindings_t chanBindings GSSEAP_UNUSED,
584 gss_buffer_t inputToken,
585 gss_buffer_t outputToken GSSEAP_UNUSED,
586 OM_uint32 *smFlags GSSEAP_UNUSED)
591 if (inputToken->length < 8) {
592 *minor = GSSEAP_TOK_TRUNC;
593 return GSS_S_DEFECTIVE_TOKEN;
596 p = (unsigned char *)inputToken->value;
598 major = load_uint32_be(&p[0]);
599 *minor = ERROR_TABLE_BASE_eapg + load_uint32_be(&p[4]);
601 if (!GSS_ERROR(major) || !IS_WIRE_ERROR(*minor)) {
602 major = GSS_S_FAILURE;
603 *minor = GSSEAP_BAD_ERROR_TOKEN;
606 GSSEAP_ASSERT(GSS_ERROR(major));
611 #ifdef GSSEAP_ENABLE_REAUTH
613 eapGssSmInitGssReauth(OM_uint32 *minor,
617 gss_OID mech GSSEAP_UNUSED,
620 gss_channel_bindings_t chanBindings,
621 gss_buffer_t inputToken,
622 gss_buffer_t outputToken,
623 OM_uint32 *smFlags GSSEAP_UNUSED)
625 OM_uint32 major, tmpMinor;
626 gss_name_t mechTarget = GSS_C_NO_NAME;
627 gss_OID actualMech = GSS_C_NO_OID;
628 OM_uint32 gssFlags, timeRec;
631 * Here we use the passed in credential handle because the resolved
632 * context credential does not currently have the reauth creds.
634 if (GSSEAP_SM_STATE(ctx) == GSSEAP_STATE_INITIAL) {
635 if (!gssEapCanReauthP(cred, target, timeReq))
636 return GSS_S_CONTINUE_NEEDED;
638 ctx->flags |= CTX_FLAG_KRB_REAUTH;
639 } else if ((ctx->flags & CTX_FLAG_KRB_REAUTH) == 0) {
640 major = GSS_S_DEFECTIVE_TOKEN;
641 *minor = GSSEAP_WRONG_ITOK;
645 GSSEAP_ASSERT(cred != GSS_C_NO_CREDENTIAL);
647 major = gssEapMechToGlueName(minor, target, &mechTarget);
648 if (GSS_ERROR(major))
651 major = gssInitSecContext(minor,
655 (gss_OID)gss_mech_krb5,
656 reqFlags | GSS_C_MUTUAL_FLAG,
664 if (GSS_ERROR(major))
667 ctx->gssFlags = gssFlags;
669 if (major == GSS_S_COMPLETE) {
670 GSSEAP_ASSERT(GSSEAP_SM_STATE(ctx) == GSSEAP_STATE_REAUTHENTICATE);
672 major = gssEapReauthComplete(minor, ctx, cred, actualMech, timeRec);
673 if (GSS_ERROR(major))
675 GSSEAP_SM_TRANSITION(ctx, GSSEAP_STATE_ESTABLISHED);
677 GSSEAP_SM_TRANSITION(ctx, GSSEAP_STATE_REAUTHENTICATE);
681 gssReleaseName(&tmpMinor, &mechTarget);
685 #endif /* GSSEAP_ENABLE_REAUTH */
689 eapGssSmInitVendorInfo(OM_uint32 *minor,
690 gss_cred_id_t cred GSSEAP_UNUSED,
691 gss_ctx_id_t ctx GSSEAP_UNUSED,
692 gss_name_t target GSSEAP_UNUSED,
693 gss_OID mech GSSEAP_UNUSED,
694 OM_uint32 reqFlags GSSEAP_UNUSED,
695 OM_uint32 timeReq GSSEAP_UNUSED,
696 gss_channel_bindings_t chanBindings GSSEAP_UNUSED,
697 gss_buffer_t inputToken GSSEAP_UNUSED,
698 gss_buffer_t outputToken,
699 OM_uint32 *smFlags GSSEAP_UNUSED)
703 major = makeStringBuffer(minor, "JANET(UK)", outputToken);
704 if (GSS_ERROR(major))
707 return GSS_S_CONTINUE_NEEDED;
712 eapGssSmInitAcceptorName(OM_uint32 *minor,
713 gss_cred_id_t cred GSSEAP_UNUSED,
715 gss_name_t target GSSEAP_UNUSED,
716 gss_OID mech GSSEAP_UNUSED,
717 OM_uint32 reqFlags GSSEAP_UNUSED,
718 OM_uint32 timeReq GSSEAP_UNUSED,
719 gss_channel_bindings_t chanBindings GSSEAP_UNUSED,
720 gss_buffer_t inputToken GSSEAP_UNUSED,
721 gss_buffer_t outputToken,
722 OM_uint32 *smFlags GSSEAP_UNUSED)
726 if (GSSEAP_SM_STATE(ctx) == GSSEAP_STATE_INITIAL &&
727 ctx->acceptorName != GSS_C_NO_NAME) {
729 /* Send desired target name to acceptor */
730 major = gssEapDisplayName(minor, ctx->acceptorName,
732 if (GSS_ERROR(major))
734 } else if (inputToken != GSS_C_NO_BUFFER &&
735 ctx->acceptorName == GSS_C_NO_NAME) {
736 /* Accept target name hint from acceptor */
737 major = gssEapImportName(minor, inputToken,
741 if (GSS_ERROR(major))
746 * Currently, other parts of the code assume that the acceptor name
747 * is available, hence this check.
749 if (ctx->acceptorName == GSS_C_NO_NAME) {
750 *minor = GSSEAP_NO_ACCEPTOR_NAME;
751 return GSS_S_FAILURE;
755 * Generate channel binding data
757 if (ctx->initiatorCtx.chbindData == NULL)
759 major = peerInitEapChannelBinding(minor, ctx);
760 if (GSS_ERROR(major))
764 return GSS_S_CONTINUE_NEEDED;
768 eapGssSmInitIdentity(OM_uint32 *minor,
769 gss_cred_id_t cred GSSEAP_UNUSED,
771 gss_name_t target GSSEAP_UNUSED,
772 gss_OID mech GSSEAP_UNUSED,
773 OM_uint32 reqFlags GSSEAP_UNUSED,
774 OM_uint32 timeReq GSSEAP_UNUSED,
775 gss_channel_bindings_t chanBindings GSSEAP_UNUSED,
776 gss_buffer_t inputToken GSSEAP_UNUSED,
777 gss_buffer_t outputToken GSSEAP_UNUSED,
780 struct eap_config eapConfig;
782 #ifdef GSSEAP_ENABLE_REAUTH
783 if (GSSEAP_SM_STATE(ctx) == GSSEAP_STATE_REAUTHENTICATE) {
786 /* server didn't support reauthentication, sent EAP request */
787 gssDeleteSecContext(&tmpMinor, &ctx->reauthCtx, GSS_C_NO_BUFFER);
788 ctx->flags &= ~(CTX_FLAG_KRB_REAUTH);
789 GSSEAP_SM_TRANSITION(ctx, GSSEAP_STATE_INITIAL);
792 *smFlags |= SM_FLAG_FORCE_SEND_TOKEN;
794 GSSEAP_ASSERT((ctx->flags & CTX_FLAG_KRB_REAUTH) == 0);
795 GSSEAP_ASSERT(inputToken == GSS_C_NO_BUFFER);
797 memset(&eapConfig, 0, sizeof(eapConfig));
799 ctx->initiatorCtx.eap = eap_peer_sm_init(ctx,
800 &gssEapPolicyCallbacks,
803 if (ctx->initiatorCtx.eap == NULL) {
804 *minor = GSSEAP_PEER_SM_INIT_FAILURE;
805 return GSS_S_FAILURE;
808 ctx->flags |= CTX_FLAG_EAP_RESTART | CTX_FLAG_EAP_PORT_ENABLED;
810 /* poke EAP state machine */
811 if (eap_peer_sm_step(ctx->initiatorCtx.eap) != 0) {
812 *minor = GSSEAP_PEER_SM_STEP_FAILURE;
813 return GSS_S_FAILURE;
816 GSSEAP_SM_TRANSITION_NEXT(ctx);
820 return GSS_S_CONTINUE_NEEDED;
824 eapGssSmInitAuthenticate(OM_uint32 *minor,
825 gss_cred_id_t cred GSSEAP_UNUSED,
827 gss_name_t target GSSEAP_UNUSED,
828 gss_OID mech GSSEAP_UNUSED,
829 OM_uint32 reqFlags GSSEAP_UNUSED,
830 OM_uint32 timeReq GSSEAP_UNUSED,
831 gss_channel_bindings_t chanBindings GSSEAP_UNUSED,
832 gss_buffer_t inputToken GSSEAP_UNUSED,
833 gss_buffer_t outputToken,
838 struct wpabuf *resp = NULL;
842 GSSEAP_ASSERT(inputToken != GSS_C_NO_BUFFER);
844 major = peerConfigInit(minor, ctx);
845 if (GSS_ERROR(major))
848 GSSEAP_ASSERT(ctx->initiatorCtx.eap != NULL);
849 GSSEAP_ASSERT(ctx->flags & CTX_FLAG_EAP_PORT_ENABLED);
851 ctx->flags |= CTX_FLAG_EAP_REQ; /* we have a Request from the acceptor */
853 wpabuf_set(&ctx->initiatorCtx.reqData,
854 inputToken->value, inputToken->length);
856 major = GSS_S_CONTINUE_NEEDED;
858 eap_peer_sm_step(ctx->initiatorCtx.eap);
859 if (ctx->flags & CTX_FLAG_EAP_RESP) {
860 ctx->flags &= ~(CTX_FLAG_EAP_RESP);
862 resp = eap_get_eapRespData(ctx->initiatorCtx.eap);
863 } else if (ctx->flags & CTX_FLAG_EAP_SUCCESS) {
864 major = initReady(minor, ctx, reqFlags);
865 if (GSS_ERROR(major))
868 ctx->flags &= ~(CTX_FLAG_EAP_SUCCESS);
869 major = GSS_S_CONTINUE_NEEDED;
870 GSSEAP_SM_TRANSITION_NEXT(ctx);
871 } else if (ctx->flags & CTX_FLAG_EAP_FAIL) {
872 major = GSS_S_DEFECTIVE_CREDENTIAL;
873 *minor = GSSEAP_PEER_AUTH_FAILURE;
875 major = GSS_S_DEFECTIVE_TOKEN;
876 *minor = GSSEAP_PEER_BAD_MESSAGE;
882 gss_buffer_desc respBuf;
884 GSSEAP_ASSERT(major == GSS_S_CONTINUE_NEEDED);
886 respBuf.length = wpabuf_len(resp);
887 respBuf.value = (void *)wpabuf_head(resp);
889 tmpMajor = duplicateBuffer(&tmpMinor, &respBuf, outputToken);
890 if (GSS_ERROR(tmpMajor)) {
895 *smFlags |= SM_FLAG_OUTPUT_TOKEN_CRITICAL;
898 wpabuf_set(&ctx->initiatorCtx.reqData, NULL, 0);
899 peerConfigFree(&tmpMinor, ctx);
905 eapGssSmInitGssFlags(OM_uint32 *minor,
906 gss_cred_id_t cred GSSEAP_UNUSED,
908 gss_name_t target GSSEAP_UNUSED,
909 gss_OID mech GSSEAP_UNUSED,
910 OM_uint32 reqFlags GSSEAP_UNUSED,
911 OM_uint32 timeReq GSSEAP_UNUSED,
912 gss_channel_bindings_t chanBindings GSSEAP_UNUSED,
913 gss_buffer_t inputToken GSSEAP_UNUSED,
914 gss_buffer_t outputToken,
915 OM_uint32 *smFlags GSSEAP_UNUSED)
917 unsigned char wireFlags[4];
918 gss_buffer_desc flagsBuf;
920 store_uint32_be(ctx->gssFlags & GSSEAP_WIRE_FLAGS_MASK, wireFlags);
922 flagsBuf.length = sizeof(wireFlags);
923 flagsBuf.value = wireFlags;
925 return duplicateBuffer(minor, &flagsBuf, outputToken);
929 eapGssSmInitGssChannelBindings(OM_uint32 *minor,
930 gss_cred_id_t cred GSSEAP_UNUSED,
932 gss_name_t target GSSEAP_UNUSED,
933 gss_OID mech GSSEAP_UNUSED,
934 OM_uint32 reqFlags GSSEAP_UNUSED,
935 OM_uint32 timeReq GSSEAP_UNUSED,
936 gss_channel_bindings_t chanBindings,
937 gss_buffer_t inputToken GSSEAP_UNUSED,
938 gss_buffer_t outputToken,
942 gss_buffer_desc buffer = GSS_C_EMPTY_BUFFER;
944 if (chanBindings != GSS_C_NO_CHANNEL_BINDINGS)
945 buffer = chanBindings->application_data;
947 major = gssEapWrap(minor, ctx, TRUE, GSS_C_QOP_DEFAULT,
948 &buffer, NULL, outputToken);
949 if (GSS_ERROR(major))
952 GSSEAP_ASSERT(outputToken->value != NULL);
955 *smFlags |= SM_FLAG_OUTPUT_TOKEN_CRITICAL;
957 return GSS_S_CONTINUE_NEEDED;
961 eapGssSmInitInitiatorMIC(OM_uint32 *minor,
962 gss_cred_id_t cred GSSEAP_UNUSED,
964 gss_name_t target GSSEAP_UNUSED,
965 gss_OID mech GSSEAP_UNUSED,
966 OM_uint32 reqFlags GSSEAP_UNUSED,
967 OM_uint32 timeReq GSSEAP_UNUSED,
968 gss_channel_bindings_t chanBindings GSSEAP_UNUSED,
969 gss_buffer_t inputToken GSSEAP_UNUSED,
970 gss_buffer_t outputToken,
975 major = gssEapMakeTokenMIC(minor, ctx, outputToken);
976 if (GSS_ERROR(major))
979 GSSEAP_SM_TRANSITION_NEXT(ctx);
982 *smFlags |= SM_FLAG_OUTPUT_TOKEN_CRITICAL;
984 return GSS_S_CONTINUE_NEEDED;
987 #ifdef GSSEAP_ENABLE_REAUTH
989 eapGssSmInitReauthCreds(OM_uint32 *minor,
992 gss_name_t target GSSEAP_UNUSED,
993 gss_OID mech GSSEAP_UNUSED,
994 OM_uint32 reqFlags GSSEAP_UNUSED,
995 OM_uint32 timeReq GSSEAP_UNUSED,
996 gss_channel_bindings_t chanBindings GSSEAP_UNUSED,
997 gss_buffer_t inputToken,
998 gss_buffer_t outputToken GSSEAP_UNUSED,
999 OM_uint32 *smFlags GSSEAP_UNUSED)
1003 if (ctx->gssFlags & GSS_C_MUTUAL_FLAG) {
1004 major = gssEapStoreReauthCreds(minor, ctx, cred, inputToken);
1005 if (GSS_ERROR(major))
1010 return GSS_S_CONTINUE_NEEDED;
1012 #endif /* GSSEAP_ENABLE_REAUTH */
1015 eapGssSmInitAcceptorMIC(OM_uint32 *minor,
1016 gss_cred_id_t cred GSSEAP_UNUSED,
1018 gss_name_t target GSSEAP_UNUSED,
1019 gss_OID mech GSSEAP_UNUSED,
1020 OM_uint32 reqFlags GSSEAP_UNUSED,
1021 OM_uint32 timeReq GSSEAP_UNUSED,
1022 gss_channel_bindings_t chanBindings GSSEAP_UNUSED,
1023 gss_buffer_t inputToken,
1024 gss_buffer_t outputToken GSSEAP_UNUSED,
1025 OM_uint32 *smFlags GSSEAP_UNUSED)
1029 major = gssEapVerifyTokenMIC(minor, ctx, inputToken);
1030 if (GSS_ERROR(major))
1033 GSSEAP_SM_TRANSITION(ctx, GSSEAP_STATE_ESTABLISHED);
1037 return GSS_S_COMPLETE;
1040 static struct gss_eap_sm eapGssInitiatorSm[] = {
1042 ITOK_TYPE_CONTEXT_ERR,
1044 GSSEAP_STATE_ALL & ~(GSSEAP_STATE_INITIAL),
1049 ITOK_TYPE_ACCEPTOR_NAME_RESP,
1050 ITOK_TYPE_ACCEPTOR_NAME_REQ,
1051 GSSEAP_STATE_INITIAL | GSSEAP_STATE_AUTHENTICATE,
1053 eapGssSmInitAcceptorName
1058 ITOK_TYPE_VENDOR_INFO,
1059 GSSEAP_STATE_INITIAL,
1061 eapGssSmInitVendorInfo
1064 #ifdef GSSEAP_ENABLE_REAUTH
1066 ITOK_TYPE_REAUTH_RESP,
1067 ITOK_TYPE_REAUTH_REQ,
1068 GSSEAP_STATE_INITIAL | GSSEAP_STATE_REAUTHENTICATE,
1070 eapGssSmInitGssReauth
1076 #ifdef GSSEAP_ENABLE_REAUTH
1077 GSSEAP_STATE_REAUTHENTICATE |
1079 GSSEAP_STATE_INITIAL,
1080 SM_ITOK_FLAG_REQUIRED,
1081 eapGssSmInitIdentity
1086 GSSEAP_STATE_AUTHENTICATE,
1087 SM_ITOK_FLAG_REQUIRED,
1088 eapGssSmInitAuthenticate
1092 ITOK_TYPE_GSS_FLAGS,
1093 GSSEAP_STATE_INITIATOR_EXTS,
1095 eapGssSmInitGssFlags
1099 ITOK_TYPE_GSS_CHANNEL_BINDINGS,
1100 GSSEAP_STATE_INITIATOR_EXTS,
1101 SM_ITOK_FLAG_REQUIRED,
1102 eapGssSmInitGssChannelBindings
1106 ITOK_TYPE_INITIATOR_MIC,
1107 GSSEAP_STATE_INITIATOR_EXTS,
1108 SM_ITOK_FLAG_REQUIRED,
1109 eapGssSmInitInitiatorMIC
1111 #ifdef GSSEAP_ENABLE_REAUTH
1113 ITOK_TYPE_REAUTH_CREDS,
1115 GSSEAP_STATE_ACCEPTOR_EXTS,
1117 eapGssSmInitReauthCreds
1120 /* other extensions go here */
1122 ITOK_TYPE_ACCEPTOR_MIC,
1124 GSSEAP_STATE_ACCEPTOR_EXTS,
1125 SM_ITOK_FLAG_REQUIRED,
1126 eapGssSmInitAcceptorMIC
1131 gssEapInitSecContext(OM_uint32 *minor,
1134 gss_name_t target_name,
1136 OM_uint32 req_flags,
1138 gss_channel_bindings_t input_chan_bindings,
1139 gss_buffer_t input_token,
1140 gss_OID *actual_mech_type,
1141 gss_buffer_t output_token,
1142 OM_uint32 *ret_flags,
1143 OM_uint32 *time_rec)
1145 OM_uint32 major, tmpMinor;
1146 int initialContextToken = (ctx->mechanismUsed == GSS_C_NO_OID);
1149 * XXX is acquiring the credential lock here necessary? The password is
1150 * mutable but the contract could specify that this is not updated whilst
1151 * a context is being initialized.
1153 if (cred != GSS_C_NO_CREDENTIAL)
1154 GSSEAP_MUTEX_LOCK(&cred->mutex);
1156 if (ctx->cred == GSS_C_NO_CREDENTIAL) {
1157 major = gssEapResolveInitiatorCred(minor, cred, target_name, &ctx->cred);
1158 if (GSS_ERROR(major))
1161 GSSEAP_ASSERT(ctx->cred != GSS_C_NO_CREDENTIAL);
1164 GSSEAP_MUTEX_LOCK(&ctx->cred->mutex);
1166 GSSEAP_ASSERT(ctx->cred->flags & CRED_FLAG_RESOLVED);
1167 GSSEAP_ASSERT(ctx->cred->flags & CRED_FLAG_INITIATE);
1169 if (initialContextToken) {
1170 major = initBegin(minor, ctx, target_name, mech_type,
1171 req_flags, time_req, input_chan_bindings);
1172 if (GSS_ERROR(major))
1176 major = gssEapSmStep(minor,
1183 input_chan_bindings,
1187 sizeof(eapGssInitiatorSm) / sizeof(eapGssInitiatorSm[0]));
1188 if (GSS_ERROR(major))
1191 if (actual_mech_type != NULL) {
1194 tmpMajor = gssEapCanonicalizeOid(&tmpMinor, ctx->mechanismUsed, 0, actual_mech_type);
1195 if (GSS_ERROR(tmpMajor)) {
1201 if (ret_flags != NULL) {
1202 if ((major == GSS_S_COMPLETE) &&
1203 (ctx->flags & CTX_FLAG_EAP_CHBIND_ACCEPT))
1204 *ret_flags = ctx->gssFlags | GSS_C_MUTUAL_FLAG;
1206 *ret_flags = ctx->gssFlags & (~GSS_C_MUTUAL_FLAG);
1208 if (major == GSS_S_COMPLETE)
1210 if (time_rec != NULL)
1211 gssEapContextTime(&tmpMinor, ctx, time_rec);
1213 GSSEAP_ASSERT(CTX_IS_ESTABLISHED(ctx) || major == GSS_S_CONTINUE_NEEDED);
1216 if (cred != GSS_C_NO_CREDENTIAL)
1217 GSSEAP_MUTEX_UNLOCK(&cred->mutex);
1218 if (ctx->cred != GSS_C_NO_CREDENTIAL)
1219 GSSEAP_MUTEX_UNLOCK(&ctx->cred->mutex);
1224 OM_uint32 GSSAPI_CALLCONV
1225 gss_init_sec_context(OM_uint32 *minor,
1227 gss_ctx_id_t *context_handle,
1228 gss_name_t target_name,
1230 OM_uint32 req_flags,
1232 gss_channel_bindings_t input_chan_bindings,
1233 gss_buffer_t input_token,
1234 gss_OID *actual_mech_type,
1235 gss_buffer_t output_token,
1236 OM_uint32 *ret_flags,
1237 OM_uint32 *time_rec)
1239 OM_uint32 major, tmpMinor;
1240 gss_ctx_id_t ctx = *context_handle;
1244 output_token->length = 0;
1245 output_token->value = NULL;
1247 if (ctx == GSS_C_NO_CONTEXT) {
1248 if (input_token != GSS_C_NO_BUFFER && input_token->length != 0) {
1249 *minor = GSSEAP_WRONG_SIZE;
1250 return GSS_S_DEFECTIVE_TOKEN;
1253 major = gssEapAllocContext(minor, &ctx);
1254 if (GSS_ERROR(major))
1257 ctx->flags |= CTX_FLAG_INITIATOR;
1259 *context_handle = ctx;
1262 GSSEAP_MUTEX_LOCK(&ctx->mutex);
1264 major = gssEapInitSecContext(minor,
1271 input_chan_bindings,
1278 GSSEAP_MUTEX_UNLOCK(&ctx->mutex);
1280 if (GSS_ERROR(major))
1281 gssEapReleaseContext(&tmpMinor, context_handle);