X-Git-Url: http://www.project-moonshot.org/gitweb/?a=blobdiff_plain;f=init_sec_context.c;h=119eb4f30b6a261a9be770d4335637d39ed5b747;hb=d4fe7ce93304facaf069792c347b01b25ab765ae;hp=cb20f208c6cd1d94ebcd7415a278ffe430b8c325;hpb=c11d67f46f001b7f0a664e3c995a8aa7a4eb960c;p=mech_eap.orig diff --git a/init_sec_context.c b/init_sec_context.c index cb20f20..119eb4f 100644 --- a/init_sec_context.c +++ b/init_sec_context.c @@ -70,6 +70,14 @@ policyVariableToFlag(enum eapol_bool_var variable) return flag; } +static struct eap_peer_config * +peerGetConfig(void *ctx) +{ + gss_ctx_id_t gssCtx = (gss_ctx_id_t)ctx; + + return &gssCtx->initiatorCtx.eapPeerConfig; +} + static Boolean peerGetBool(void *data, enum eapol_bool_var variable) { @@ -102,7 +110,7 @@ peerSetBool(void *data, enum eapol_bool_var variable, ctx->flags &= ~(flag); } -static int +static unsigned int peerGetInt(void *data, enum eapol_int_var variable) { gss_ctx_id_t ctx = data; @@ -139,10 +147,310 @@ peerSetInt(void *data, enum eapol_int_var variable, } } +static struct wpabuf * +peerGetEapReqData(void *ctx) +{ + gss_ctx_id_t gssCtx = (gss_ctx_id_t)ctx; + + return &gssCtx->initiatorCtx.reqData; +} + +static void +peerSetConfigBlob(void *ctx, struct wpa_config_blob *blob) +{ +} + +static const struct wpa_config_blob * +peerGetConfigBlob(void *ctx, const char *name) +{ + return NULL; +} + +static void +peerNotifyPending(void *ctx) +{ +} + +static struct eapol_callbacks gssEapPolicyCallbacks = { + peerGetConfig, + peerGetBool, + peerSetBool, + peerGetInt, + peerSetInt, + peerGetEapReqData, + peerSetConfigBlob, + peerGetConfigBlob, + peerNotifyPending, +}; + +static OM_uint32 +peerConfigInit(OM_uint32 *minor, + gss_cred_id_t cred, + gss_ctx_id_t ctx, + int loadConfig) +{ + krb5_context krbContext; + struct eap_peer_config *eapPeerConfig = &ctx->initiatorCtx.eapPeerConfig; + krb5_error_code code; + char *identity; + + GSSEAP_KRB_INIT(&krbContext); + + if (loadConfig) { + } + + code = krb5_unparse_name(krbContext, cred->name->krbPrincipal, &identity); + if (code != 0) { + *minor = code; + return GSS_S_FAILURE; + } + + eapPeerConfig->identity = (unsigned char *)identity; + eapPeerConfig->identity_len = strlen(identity); + eapPeerConfig->password = (unsigned char *)cred->password.value; + eapPeerConfig->password_len = cred->password.length; + + return GSS_S_COMPLETE; +} + +static OM_uint32 +peerConfigFree(OM_uint32 *minor, + gss_ctx_id_t ctx) +{ + krb5_context krbContext; + struct eap_peer_config *eapPeerConfig = &ctx->initiatorCtx.eapPeerConfig; + + GSSEAP_KRB_INIT(&krbContext); + + krb5_free_unparsed_name(krbContext, (char *)eapPeerConfig->identity); + + return GSS_S_COMPLETE; +} + +static OM_uint32 +peerDeriveKey(OM_uint32 *minor, + gss_ctx_id_t ctx) +{ + OM_uint32 major; + const unsigned char *key; + size_t keyLength; + krb5_context krbContext; + + GSSEAP_KRB_INIT(&krbContext); + + /* Cache encryption type derived from selected mechanism OID */ + major = gssEapOidToEnctype(minor, ctx->mechanismUsed, &ctx->encryptionType); + if (GSS_ERROR(major)) + return major; + + if (ctx->encryptionType != ENCTYPE_NULL && + eap_key_available(ctx->initiatorCtx.eap)) { + key = eap_get_eapKeyData(ctx->initiatorCtx.eap, &keyLength); + + major = gssEapDeriveRFC3961Key(minor, key, keyLength, + ctx->encryptionType, &ctx->rfc3961Key); + if (GSS_ERROR(major)) + return major; + } else { + /* + * draft-howlett-eap-gss says that integrity/confidentialty should + * always be advertised as available, but if we have no keying + * material it seems confusing to the caller to advertise this. + */ + ctx->gssFlags &= ~(GSS_C_INTEG_FLAG | GSS_C_CONF_FLAG); + } + + return GSS_S_COMPLETE; +} + +static OM_uint32 +eapGssSmInitAuthenticate(OM_uint32 *minor, + gss_cred_id_t cred, + gss_ctx_id_t ctx, + gss_name_t target, + gss_OID mech, + OM_uint32 reqFlags, + OM_uint32 timeReq, + gss_channel_bindings_t chanBindings, + gss_buffer_t inputToken, + gss_buffer_t outputToken) +{ + OM_uint32 major, tmpMinor; + time_t now; + int initialContextToken = 0, code; + + initialContextToken = (inputToken == GSS_C_NO_BUFFER || + inputToken->length == 0); + + major = peerConfigInit(minor, cred, ctx, initialContextToken); + if (GSS_ERROR(major)) + goto cleanup; + + if (initialContextToken) { + ctx->flags |= CTX_FLAG_EAP_PORT_ENABLED; + + ctx->initiatorCtx.eap = eap_peer_sm_init(ctx, + &gssEapPolicyCallbacks, + ctx, + &ctx->initiatorCtx.eapConfig); + + time(&now); + if (timeReq == 0 || timeReq == GSS_C_INDEFINITE) + ctx->expiryTime = 0; + else + ctx->expiryTime = now + timeReq; + + major = gss_duplicate_name(minor, cred->name, &ctx->initiatorName); + if (GSS_ERROR(major)) + goto cleanup; + + major = gss_duplicate_name(minor, target, &ctx->acceptorName); + if (GSS_ERROR(major)) + goto cleanup; + + if (mech == GSS_C_NULL_OID || oidEqual(mech, GSS_EAP_MECHANISM)) { + major = gssEapDefaultMech(minor, &ctx->mechanismUsed); + } else if (gssEapIsConcreteMechanismOid(mech)) { + if (!gssEapInternalizeOid(mech, &ctx->mechanismUsed)) + major = duplicateOid(minor, mech, &ctx->mechanismUsed); + } else { + major = GSS_S_BAD_MECH; + } + if (GSS_ERROR(major)) + goto cleanup; + } + + wpabuf_set(&ctx->initiatorCtx.reqData, + inputToken->value, inputToken->length); + + code = eap_peer_sm_step(ctx->initiatorCtx.eap); + + if (ctx->flags & CTX_FLAG_EAP_RESP) { + struct wpabuf *resp; + gss_buffer_desc buf; + + ctx->flags &= ~(CTX_FLAG_EAP_RESP); + + resp = eap_get_eapRespData(ctx->initiatorCtx.eap); + + if (resp != NULL) { + buf.length = wpabuf_len(resp); + buf.value = (void *)wpabuf_head(resp); + + major = duplicateBuffer(minor, &buf, outputToken); + if (GSS_ERROR(major)) + goto cleanup; + + major = GSS_S_CONTINUE_NEEDED; + } + } + + if (ctx->flags & CTX_FLAG_EAP_SUCCESS) { + major = peerDeriveKey(minor, ctx); + if (GSS_ERROR(major)) + goto cleanup; + + ctx->flags &= ~(CTX_FLAG_EAP_SUCCESS); + ctx->state = EAP_STATE_ESTABLISHED; + major = GSS_S_COMPLETE; + } else if ((ctx->flags & CTX_FLAG_EAP_FAIL) || code == 0) { + major = GSS_S_FAILURE; + } + +cleanup: + wpabuf_set(&ctx->initiatorCtx.reqData, NULL, 0); + peerConfigFree(&tmpMinor, ctx); + + return major; +} + +static OM_uint32 +eapGssSmInitKeyTransport(OM_uint32 *minor, + gss_cred_id_t cred, + gss_ctx_id_t ctx, + gss_name_t target, + gss_OID mech, + OM_uint32 reqFlags, + OM_uint32 timeReq, + gss_channel_bindings_t chanBindings, + gss_buffer_t inputToken, + gss_buffer_t outputToken) +{ + GSSEAP_NOT_IMPLEMENTED; +} + +static OM_uint32 +eapGssSmInitSecureAssoc(OM_uint32 *minor, + gss_cred_id_t cred, + gss_ctx_id_t ctx, + gss_name_t target, + gss_OID mech, + OM_uint32 reqFlags, + OM_uint32 timeReq, + gss_channel_bindings_t chanBindings, + gss_buffer_t inputToken, + gss_buffer_t outputToken) +{ + GSSEAP_NOT_IMPLEMENTED; +} + +static OM_uint32 +eapGssSmInitGssChannelBindings(OM_uint32 *minor, + gss_cred_id_t cred, + gss_ctx_id_t ctx, + gss_name_t target, + gss_OID mech, + OM_uint32 reqFlags, + OM_uint32 timeReq, + gss_channel_bindings_t chanBindings, + gss_buffer_t inputToken, + gss_buffer_t outputToken) +{ + GSSEAP_NOT_IMPLEMENTED; +} + +static OM_uint32 +eapGssSmInitEstablished(OM_uint32 *minor, + gss_cred_id_t cred, + gss_ctx_id_t ctx, + gss_name_t target, + gss_OID mech, + OM_uint32 reqFlags, + OM_uint32 timeReq, + gss_channel_bindings_t chanBindings, + gss_buffer_t inputToken, + gss_buffer_t outputToken) +{ + /* Called with already established context */ + *minor = EINVAL; + return GSS_S_BAD_STATUS; +} + +static struct eap_gss_initiator_sm { + enum gss_eap_token_type inputTokenType; + enum gss_eap_token_type outputTokenType; + OM_uint32 (*processToken)(OM_uint32 *, + gss_cred_id_t, + gss_ctx_id_t, + gss_name_t, + gss_OID, + OM_uint32, + OM_uint32, + gss_channel_bindings_t, + gss_buffer_t, + gss_buffer_t); +} eapGssInitiatorSm[] = { + { TOK_TYPE_EAP_REQ, TOK_TYPE_EAP_RESP, eapGssSmInitAuthenticate }, + { TOK_TYPE_EAP_REQ, TOK_TYPE_EAP_RESP, eapGssSmInitKeyTransport }, + { TOK_TYPE_EAP_REQ, TOK_TYPE_EAP_RESP, eapGssSmInitSecureAssoc }, + { TOK_TYPE_GSS_CB, TOK_TYPE_NONE, eapGssSmInitGssChannelBindings }, + { TOK_TYPE_NONE, TOK_TYPE_NONE, eapGssSmInitEstablished }, +}; + OM_uint32 gss_init_sec_context(OM_uint32 *minor, gss_cred_id_t cred, - gss_ctx_id_t *pCtx, + gss_ctx_id_t *context_handle, gss_name_t target_name, gss_OID mech_type, OM_uint32 req_flags, @@ -154,5 +462,94 @@ gss_init_sec_context(OM_uint32 *minor, OM_uint32 *ret_flags, OM_uint32 *time_rec) { - GSSEAP_NOT_IMPLEMENTED; + OM_uint32 major, tmpMinor; + gss_ctx_id_t ctx = *context_handle; + struct eap_gss_initiator_sm *sm = NULL; + gss_buffer_desc innerInputToken, innerOutputToken; + + *minor = 0; + + innerOutputToken.length = 0; + innerOutputToken.value = NULL; + + output_token->length = 0; + output_token->value = NULL; + + if (cred != GSS_C_NO_CREDENTIAL && !(cred->flags & CRED_FLAG_INITIATE)) { + return GSS_S_NO_CRED; + } + + if (ctx == GSS_C_NO_CONTEXT) { + if (input_token != GSS_C_NO_BUFFER && input_token->length != 0) { + return GSS_S_DEFECTIVE_TOKEN; + } + + major = gssEapAllocContext(minor, &ctx); + if (GSS_ERROR(major)) + return major; + + ctx->flags |= CTX_FLAG_INITIATOR; + + *context_handle = ctx; + } + + GSSEAP_MUTEX_LOCK(&ctx->mutex); + + sm = &eapGssInitiatorSm[ctx->state]; + + if (input_token != GSS_C_NO_BUFFER) { + major = gssEapVerifyToken(minor, ctx, input_token, + sm->inputTokenType, &innerInputToken); + if (GSS_ERROR(major)) + goto cleanup; + } else { + innerInputToken.length = 0; + innerInputToken.value = NULL; + } + + /* + * Advance through state machine whilst empty tokens are emitted and + * the status is not GSS_S_COMPLETE or an error status. + */ + do { + major = (sm->processToken)(minor, + cred, + ctx, + target_name, + mech_type, + req_flags, + time_req, + input_chan_bindings, + &innerInputToken, + &innerOutputToken); + if (GSS_ERROR(major)) + goto cleanup; + } while (major == GSS_S_CONTINUE_NEEDED && innerOutputToken.length == 0); + + if (actual_mech_type != NULL) { + if (!gssEapInternalizeOid(ctx->mechanismUsed, actual_mech_type)) + duplicateOid(&tmpMinor, ctx->mechanismUsed, actual_mech_type); + } + if (innerOutputToken.length != 0) { + major = gssEapMakeToken(minor, ctx, &innerOutputToken, + sm->outputTokenType, output_token); + if (GSS_ERROR(major)) + goto cleanup; + } + if (ret_flags != NULL) + *ret_flags = ctx->gssFlags; + if (time_rec != NULL) + gss_context_time(&tmpMinor, ctx, time_rec); + + assert(ctx->state == EAP_STATE_ESTABLISHED || major == GSS_S_CONTINUE_NEEDED); + +cleanup: + GSSEAP_MUTEX_UNLOCK(&ctx->mutex); + + if (GSS_ERROR(major)) + gssEapReleaseContext(&tmpMinor, context_handle); + + gss_release_buffer(&tmpMinor, &innerOutputToken); + + return major; }