X-Git-Url: http://www.project-moonshot.org/gitweb/?a=blobdiff_plain;f=mech_eap%2Finit_sec_context.c;h=a9d8891aa174a2b7bc5f439a58df6a068ff131ea;hb=4f319dde67a76fe0aaf33f6d2788968012584ada;hp=a1236262a787bbb73d19fe02b4df953679f8f61b;hpb=16959b8332dbbc6a3bcaac1371eaf028101d1dae;p=mech_eap.git diff --git a/mech_eap/init_sec_context.c b/mech_eap/init_sec_context.c index a123626..a9d8891 100644 --- a/mech_eap/init_sec_context.c +++ b/mech_eap/init_sec_context.c @@ -36,6 +36,14 @@ */ #include "gssapiP_eap.h" +#include "radius/radius.h" +#include "util_radius.h" +#include "utils/radius_utils.h" + +/* methods allowed for phase1 authentication*/ +static const struct eap_method_type allowed_eap_method_types[] = { + {EAP_VENDOR_IETF, EAP_TYPE_TTLS}, + {EAP_VENDOR_IETF, EAP_TYPE_NONE}}; static OM_uint32 policyVariableToFlag(enum eapol_bool_var variable) @@ -70,6 +78,9 @@ policyVariableToFlag(enum eapol_bool_var variable) case EAPOL_altReject: flag = CTX_FLAG_EAP_ALT_REJECT; break; + case EAPOL_eapTriggerStart: + flag = CTX_FLAG_EAP_TRIGGER_START; + break; } return flag; @@ -167,10 +178,22 @@ peerSetConfigBlob(void *ctx GSSEAP_UNUSED, } static const struct wpa_config_blob * -peerGetConfigBlob(void *ctx GSSEAP_UNUSED, - const char *name GSSEAP_UNUSED) +peerGetConfigBlob(void *ctx, + const char *name) { - return NULL; + gss_ctx_id_t gssCtx = (gss_ctx_id_t)ctx; + size_t index; + + if (strcmp(name, "client-cert") == 0) + index = CONFIG_BLOB_CLIENT_CERT; + else if (strcmp(name, "private-key") == 0) + index = CONFIG_BLOB_PRIVATE_KEY; + else if (strcmp(name, "ca-cert") == 0) + index = CONFIG_BLOB_CA_CERT; + else + return NULL; + + return &gssCtx->initiatorCtx.configBlobs[index]; } static void @@ -190,9 +213,148 @@ static struct eapol_callbacks gssEapPolicyCallbacks = { peerNotifyPending, }; -#ifdef GSSEAP_DEBUG -extern int wpa_debug_level; -#endif + +#define CHBIND_SERVICE_NAME_FLAG 0x01 +#define CHBIND_HOST_NAME_FLAG 0x02 +#define CHBIND_SERVICE_SPECIFIC_FLAG 0x04 +#define CHBIND_REALM_NAME_FLAG 0x08 + +static OM_uint32 +peerInitEapChannelBinding(OM_uint32 *minor, gss_ctx_id_t ctx) +{ + struct wpabuf *buf = NULL; + unsigned int chbindReqFlags = 0; + krb5_principal princ = NULL; + gss_buffer_desc nameBuf = GSS_C_EMPTY_BUFFER; + OM_uint32 major = GSS_S_COMPLETE; + krb5_context krbContext = NULL; + + /* XXX is this check redundant? */ + if (ctx->acceptorName == GSS_C_NO_NAME) { + major = GSS_S_BAD_NAME; + *minor = GSSEAP_NO_ACCEPTOR_NAME; + goto cleanup; + } + + princ = ctx->acceptorName->krbPrincipal; + + krbPrincComponentToGssBuffer(princ, 0, &nameBuf); + if (nameBuf.length > 0) { + major = gssEapRadiusAddAttr(minor, &buf, PW_GSS_ACCEPTOR_SERVICE_NAME, + 0, &nameBuf); + if (GSS_ERROR(major)) + goto cleanup; + + chbindReqFlags |= CHBIND_SERVICE_NAME_FLAG; + } + + krbPrincComponentToGssBuffer(princ, 1, &nameBuf); + if (nameBuf.length > 0) { + major = gssEapRadiusAddAttr(minor, &buf, PW_GSS_ACCEPTOR_HOST_NAME, + 0, &nameBuf); + if (GSS_ERROR(major)) + goto cleanup; + + chbindReqFlags |= CHBIND_HOST_NAME_FLAG; + } + + GSSEAP_KRB_INIT(&krbContext); + + *minor = krbPrincUnparseServiceSpecifics(krbContext, princ, &nameBuf); + if (*minor != 0) + goto cleanup; + + if (nameBuf.length > 0) { + major = gssEapRadiusAddAttr(minor, &buf, + PW_GSS_ACCEPTOR_SERVICE_SPECIFICS, + 0, &nameBuf); + if (GSS_ERROR(major)) + goto cleanup; + + chbindReqFlags |= CHBIND_SERVICE_SPECIFIC_FLAG; + } + + krbFreeUnparsedName(krbContext, &nameBuf); + krbPrincRealmToGssBuffer(princ, &nameBuf); + + if (nameBuf.length > 0) { + major = gssEapRadiusAddAttr(minor, &buf, + PW_GSS_ACCEPTOR_REALM_NAME, + 0, &nameBuf); + if (GSS_ERROR(major)) + goto cleanup; + + chbindReqFlags |= CHBIND_REALM_NAME_FLAG; + } + + if (chbindReqFlags == 0) { + major = GSS_S_BAD_NAME; + *minor = GSSEAP_BAD_ACCEPTOR_NAME; + goto cleanup; + } + + ctx->initiatorCtx.chbindData = buf; + ctx->initiatorCtx.chbindReqFlags = chbindReqFlags; + + buf = NULL; + + major = GSS_S_COMPLETE; + *minor = 0; + +cleanup: + /*namebuf is freed when used and may be left with a unowned pointer*/ + wpabuf_free(buf); + + return major; +} + +static void +peerProcessChbindResponse(void *context, int code, int nsid, + u8 *data, size_t len) +{ + radius_parser msg; + gss_ctx_id_t ctx = (gss_ctx_id_t )context; + void *vsadata; + u8 type; + u32 vendor_id; + u32 chbindRetFlags = 0; + size_t vsadata_len; + + if (nsid != CHBIND_NSID_RADIUS) + return; + + if (data == NULL) + return; + msg = radius_parser_start(data, len); + if (msg == NULL) + return; + + while (radius_parser_parse_tlv(msg, &type, &vendor_id, &vsadata, + &vsadata_len) == 0) { + switch (type) { + case PW_GSS_ACCEPTOR_SERVICE_NAME: + chbindRetFlags |= CHBIND_SERVICE_NAME_FLAG; + break; + case PW_GSS_ACCEPTOR_HOST_NAME: + chbindRetFlags |= CHBIND_HOST_NAME_FLAG; + break; + case PW_GSS_ACCEPTOR_SERVICE_SPECIFICS: + chbindRetFlags |= CHBIND_SERVICE_SPECIFIC_FLAG; + break; + case PW_GSS_ACCEPTOR_REALM_NAME: + chbindRetFlags |= CHBIND_REALM_NAME_FLAG; + break; + } + } + + radius_parser_finish(msg); + + if (code == CHBIND_CODE_SUCCESS && + ((chbindRetFlags & ctx->initiatorCtx.chbindReqFlags) == ctx->initiatorCtx.chbindReqFlags)) { + ctx->flags |= CTX_FLAG_EAP_CHBIND_ACCEPT; + ctx->gssFlags |= GSS_C_MUTUAL_FLAG; + } /* else log failures? */ +} static OM_uint32 peerConfigInit(OM_uint32 *minor, gss_ctx_id_t ctx) @@ -200,6 +362,7 @@ peerConfigInit(OM_uint32 *minor, gss_ctx_id_t ctx) OM_uint32 major; krb5_context krbContext; struct eap_peer_config *eapPeerConfig = &ctx->initiatorCtx.eapPeerConfig; + struct wpa_config_blob *configBlobs = ctx->initiatorCtx.configBlobs; gss_buffer_desc identity = GSS_C_EMPTY_BUFFER; gss_buffer_desc realm = GSS_C_EMPTY_BUFFER; gss_cred_id_t cred = ctx->cred; @@ -210,16 +373,14 @@ peerConfigInit(OM_uint32 *minor, gss_ctx_id_t ctx) eapPeerConfig->anonymous_identity_len = 0; eapPeerConfig->password = NULL; eapPeerConfig->password_len = 0; + eapPeerConfig->eap_methods = (struct eap_method_type *) allowed_eap_method_types; GSSEAP_ASSERT(cred != GSS_C_NO_CREDENTIAL); GSSEAP_KRB_INIT(&krbContext); eapPeerConfig->fragment_size = 1024; -#ifdef GSSEAP_DEBUG - wpa_debug_level = 0; -#endif - + GSSEAP_ASSERT(cred->name != GSS_C_NO_NAME); if ((cred->name->flags & (NAME_FLAG_NAI | NAME_FLAG_SERVICE)) == 0) { @@ -250,13 +411,58 @@ peerConfigInit(OM_uint32 *minor, gss_ctx_id_t ctx) eapPeerConfig->anonymous_identity_len = 1 + realm.length; /* password */ - eapPeerConfig->password = (unsigned char *)cred->password.value; - eapPeerConfig->password_len = cred->password.length; + if ((cred->flags & CRED_FLAG_CERTIFICATE) == 0) { + eapPeerConfig->password = (unsigned char *)cred->password.value; + eapPeerConfig->password_len = cred->password.length; + } /* certs */ eapPeerConfig->ca_cert = (unsigned char *)cred->caCertificate.value; eapPeerConfig->subject_match = (unsigned char *)cred->subjectNameConstraint.value; eapPeerConfig->altsubject_match = (unsigned char *)cred->subjectAltNameConstraint.value; + configBlobs[CONFIG_BLOB_CA_CERT].data = cred->caCertificateBlob.value; + configBlobs[CONFIG_BLOB_CA_CERT].len = cred->caCertificateBlob.length; + + /* eap channel binding */ + if (ctx->initiatorCtx.chbindData != NULL) { + struct eap_peer_chbind_config *chbind_config = + (struct eap_peer_chbind_config *)GSSEAP_MALLOC(sizeof(struct eap_peer_chbind_config)); + if (chbind_config == NULL) { + *minor = ENOMEM; + return GSS_S_FAILURE; + } + + chbind_config->req_data = wpabuf_mhead_u8(ctx->initiatorCtx.chbindData); + chbind_config->req_data_len = wpabuf_len(ctx->initiatorCtx.chbindData); + chbind_config->nsid = CHBIND_NSID_RADIUS; + chbind_config->response_cb = &peerProcessChbindResponse; + chbind_config->ctx = ctx; + eapPeerConfig->chbind_config = chbind_config; + eapPeerConfig->chbind_config_len = 1; + } else { + eapPeerConfig->chbind_config = NULL; + eapPeerConfig->chbind_config_len = 0; + } + if (cred->flags & CRED_FLAG_CERTIFICATE) { + /* + * CRED_FLAG_CONFIG_BLOB is an internal flag which will be used in the + * future to directly pass certificate and private key data to the + * EAP implementation, rather than an indirected string pointer. + */ + if (cred->flags & CRED_FLAG_CONFIG_BLOB) { + eapPeerConfig->client_cert = (unsigned char *)"blob://client-cert"; + configBlobs[CONFIG_BLOB_CLIENT_CERT].data = cred->clientCertificate.value; + configBlobs[CONFIG_BLOB_CLIENT_CERT].len = cred->clientCertificate.length; + + eapPeerConfig->client_cert = (unsigned char *)"blob://private-key"; + configBlobs[CONFIG_BLOB_PRIVATE_KEY].data = cred->clientCertificate.value; + configBlobs[CONFIG_BLOB_PRIVATE_KEY].len = cred->privateKey.length; + } else { + eapPeerConfig->client_cert = (unsigned char *)cred->clientCertificate.value; + eapPeerConfig->private_key = (unsigned char *)cred->privateKey.value; + } + eapPeerConfig->private_key_passwd = (char *)cred->password.value; + } *minor = 0; return GSS_S_COMPLETE; @@ -288,18 +494,12 @@ peerConfigFree(OM_uint32 *minor, * Mark an initiator context as ready for cryptographic operations */ static OM_uint32 -initReady(OM_uint32 *minor, gss_ctx_id_t ctx, OM_uint32 reqFlags) +initReady(OM_uint32 *minor, gss_ctx_id_t ctx) { OM_uint32 major; const unsigned char *key; size_t keyLength; -#if 1 - /* XXX actually check for mutual auth */ - if (reqFlags & GSS_C_MUTUAL_FLAG) - ctx->gssFlags |= GSS_C_MUTUAL_FLAG; -#endif - /* Cache encryption type derived from selected mechanism OID */ major = gssEapOidToEnctype(minor, ctx->mechanismUsed, &ctx->encryptionType); if (GSS_ERROR(major)) @@ -426,7 +626,10 @@ eapGssSmInitError(OM_uint32 *minor, p = (unsigned char *)inputToken->value; major = load_uint32_be(&p[0]); - *minor = ERROR_TABLE_BASE_eapg + load_uint32_be(&p[4]); + *minor = load_uint32_be(&p[4]); + if ((*minor >0) && (*minor < 128)) + * minor += ERROR_TABLE_BASE_eapg; + else *minor = 0; if (!GSS_ERROR(major) || !IS_WIRE_ERROR(*minor)) { major = GSS_S_FAILURE; @@ -561,17 +764,45 @@ eapGssSmInitAcceptorName(OM_uint32 *minor, outputToken, NULL); if (GSS_ERROR(major)) return major; - } else if (inputToken != GSS_C_NO_BUFFER && - ctx->acceptorName == GSS_C_NO_NAME) { - /* Accept target name hint from acceptor */ + } else if (inputToken != GSS_C_NO_BUFFER) { + OM_uint32 tmpMinor; + gss_name_t nameHint; + int equal; + + /* Accept target name hint from acceptor or verify acceptor */ major = gssEapImportName(minor, inputToken, GSS_C_NT_USER_NAME, ctx->mechanismUsed, - &ctx->acceptorName); + &nameHint); if (GSS_ERROR(major)) return major; + + if (ctx->acceptorName != GSS_C_NO_NAME) { + /* verify name hint matched asserted acceptor name */ + major = gssEapCompareName(minor, + nameHint, + ctx->acceptorName, + COMPARE_NAME_FLAG_IGNORE_EMPTY_REALMS, + &equal); + if (GSS_ERROR(major)) { + gssEapReleaseName(&tmpMinor, &nameHint); + return major; + } + + gssEapReleaseName(&tmpMinor, &nameHint); + + if (!equal) { + *minor = GSSEAP_WRONG_ACCEPTOR_NAME; + return GSS_S_DEFECTIVE_TOKEN; + } + } else { /* acceptor name is no_name */ + /* accept acceptor name hint */ + ctx->acceptorName = nameHint; + nameHint = GSS_C_NO_NAME; + } } + /* * Currently, other parts of the code assume that the acceptor name * is available, hence this check. @@ -581,6 +812,15 @@ eapGssSmInitAcceptorName(OM_uint32 *minor, return GSS_S_FAILURE; } + /* + * Generate channel binding data + */ + if (ctx->initiatorCtx.chbindData == NULL) { + major = peerInitEapChannelBinding(minor, ctx); + if (GSS_ERROR(major)) + return major; + } + return GSS_S_CONTINUE_NEEDED; } @@ -681,7 +921,7 @@ eapGssSmInitAuthenticate(OM_uint32 *minor, resp = eap_get_eapRespData(ctx->initiatorCtx.eap); } else if (ctx->flags & CTX_FLAG_EAP_SUCCESS) { - major = initReady(minor, ctx, reqFlags); + major = initReady(minor, ctx); if (GSS_ERROR(major)) goto cleanup; @@ -737,6 +977,11 @@ eapGssSmInitGssFlags(OM_uint32 *minor, unsigned char wireFlags[4]; gss_buffer_desc flagsBuf; + /* + * As a temporary measure, force mutual authentication until channel binding is + * more widely deployed. + */ + ctx->gssFlags |= GSS_C_MUTUAL_FLAG; store_uint32_be(ctx->gssFlags & GSSEAP_WIRE_FLAGS_MASK, wireFlags); flagsBuf.length = sizeof(wireFlags); @@ -892,7 +1137,8 @@ static struct gss_eap_sm eapGssInitiatorSm[] = { { ITOK_TYPE_ACCEPTOR_NAME_RESP, ITOK_TYPE_ACCEPTOR_NAME_REQ, - GSSEAP_STATE_INITIAL | GSSEAP_STATE_AUTHENTICATE, + GSSEAP_STATE_INITIAL | GSSEAP_STATE_AUTHENTICATE | + GSSEAP_STATE_ACCEPTOR_EXTS, 0, eapGssSmInitAcceptorName }, @@ -1042,8 +1288,10 @@ gssEapInitSecContext(OM_uint32 *minor, goto cleanup; } } + if (ret_flags != NULL) *ret_flags = ctx->gssFlags; + if (time_rec != NULL) gssEapContextTime(&tmpMinor, ctx, time_rec); @@ -1117,5 +1365,6 @@ gss_init_sec_context(OM_uint32 *minor, if (GSS_ERROR(major)) gssEapReleaseContext(&tmpMinor, context_handle); + gssEapTraceStatus( "gss_init_sec_context", major, *minor); return major; }