From: Arran Cudbard-Bell Date: Wed, 31 Jan 2018 17:47:47 +0000 (-0700) Subject: Make tls-verify-cert work with async calls X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=da7207b5803af540e0beffa1d0c56a92b72e078a;p=thirdparty%2Ffreeradius-server.git Make tls-verify-cert work with async calls --- diff --git a/src/include/unlang.h b/src/include/unlang.h index 90037c4a770..223e0f4ee0c 100644 --- a/src/include/unlang.h +++ b/src/include/unlang.h @@ -94,9 +94,9 @@ typedef unlang_action_t (*unlang_op_resume_t)(REQUEST *request, rlm_rcode_t *pre * @param[in,out] uctx Provided by whatever pushed the function. Is opaque to the * interpreter, but should be usable by the function. * All input (args) and output will be done using this structure. - * @return an action for the interpreter to perform. + * @return an #unlang_action_t. */ -typedef unlang_action_t (*unlang_function_t)(REQUEST *request, void *uctx); +typedef unlang_action_t (*unlang_function_t)(REQUEST *request, rlm_rcode_t *presult, int *priority, void *uctx); /** An unlang operation * @@ -121,7 +121,8 @@ typedef struct { ///< in debug mode. } unlang_op_t; -void unlang_push_function(REQUEST *request, unlang_function_t func, unlang_function_t repeat, void *uctx); +void unlang_push_function(REQUEST *request, + unlang_function_t func, unlang_function_t repeat, void *uctx); bool unlang_section(CONF_SECTION *cs); diff --git a/src/main/unlang_op.c b/src/main/unlang_op.c index 43724198c88..93f018e1547 100644 --- a/src/main/unlang_op.c +++ b/src/main/unlang_op.c @@ -129,23 +129,22 @@ static unlang_action_t unlang_function_call(REQUEST *request, unlang_frame_state_func_t *state = talloc_get_type_abort(frame->state, unlang_frame_state_func_t); unlang_t *instruction = frame->instruction; unlang_action_t ua; + char const *caller; - if (!frame->repeat) { - ua = state->func(request, state->uctx); - } else { - ua = state->repeat(request, state->uctx); - } + *priority = instruction->actions[*presult]; /* - * The success/failure of these functions - * should not affect the rcode in any way. - * - * Only the module/xlat which pushed the - * function can interpret its result, - * which will be written to uf->uctx. + * Don't let the callback mess with the current + * module permanently. */ - *presult = RLM_MODULE_OK; - *priority = instruction->actions[*presult]; + caller = request->module; + request->module = NULL; + if (!frame->repeat) { + ua = state->func(request, presult, priority, state->uctx); + } else { + ua = state->repeat(request, presult, priority, state->uctx); + } + request->module = caller; return ua; } diff --git a/src/modules/rlm_eap/rlm_eap.c b/src/modules/rlm_eap/rlm_eap.c index 69cec836600..8b6384e6142 100644 --- a/src/modules/rlm_eap/rlm_eap.c +++ b/src/modules/rlm_eap/rlm_eap.c @@ -323,23 +323,19 @@ static eap_type_t eap_process_nak(rlm_eap_t *inst, REQUEST *request, * - RLM_MODULE_HANDLED if we're done with this round. * - RLM_MODULE_REJECT if the user should be rejected. */ -static rlm_rcode_t mod_authenticate_result(REQUEST *request, void *instance, UNUSED void *thread, void *uctx) +static rlm_rcode_t mod_authenticate_result(REQUEST *request, void *instance, UNUSED void *thread, + eap_session_t *eap_session, rlm_rcode_t result) { rlm_eap_t *inst = talloc_get_type_abort(instance, rlm_eap_t); - eap_auth_rctx_t *rctx = talloc_get_type_abort(uctx, eap_auth_rctx_t); - eap_session_t *eap_session = rctx->eap_session; rlm_eap_method_t *method = &inst->methods[eap_session->type]; rlm_rcode_t rcode; - rad_assert(rctx->rcode != RLM_MODULE_UNKNOWN); - - request->module = rctx->caller; /* Restore original module name */ RDEBUG2("Submodule %s returned", method->submodule->name); /* * The submodule failed. Die. */ - if (rctx->rcode == RLM_MODULE_INVALID) { + if (result == RLM_MODULE_INVALID) { eap_fail(eap_session); eap_session_destroy(&eap_session); @@ -418,50 +414,23 @@ static rlm_rcode_t mod_authenticate_result(REQUEST *request, void *instance, UNU eap_session_freeze(&eap_session); finish: - talloc_free(rctx); /* Free rctx */ return rcode; } -/** Destroy an EAP session if the request currently processing it is destroyed - * - * We do this so we don't leak EAP sessions. - * - */ -static void mod_authenticate_signal(UNUSED REQUEST *request, UNUSED void *instance, UNUSED void *thread, - void *uctx, fr_state_signal_t action) -{ - eap_auth_rctx_t *rctx = talloc_get_type_abort(uctx, eap_auth_rctx_t); - eap_session_t *eap_session = rctx->eap_session; - - if (action != FR_SIGNAL_DONE) return; - - eap_session_destroy(&eap_session); - talloc_free(rctx); /* Would be cleaned up anyway but doesn't hurt */ -} - -/** Call an eap submodule using the unlang stack +/** Call mod_authenticate_result asynchronously from the unlang interpreter * * @param[in] request The current request. - * @param[in] uctx Describing the module to call. - * @return - * - UNLANG_ACTION_CALCULATE_RESULT. - * - UNLANG_ACTION_YIELD. + * @param[in] instance of rlm_eap. + * @param[in] thread UNUSED. + * @param[in] uctx the eap_session_t. + * @return The result of this round of authentication. */ -static unlang_action_t eap_call_submodule(UNUSED REQUEST *request, void *uctx) +static rlm_rcode_t mod_authenticate_result_async(REQUEST *request, void *instance, void *thread, void *uctx) { - eap_auth_rctx_t *rctx = talloc_get_type_abort(uctx, eap_auth_rctx_t); - rlm_eap_t *inst = rctx->inst; - eap_session_t *eap_session = rctx->eap_session; - rlm_eap_method_t *method = &inst->methods[eap_session->type]; - - request->module = rctx->caller; - RDEBUG2("Calling submodule %s", method->submodule->name); - - request->module = method->submodule->name; - rctx->rcode = eap_session->process(method->submodule_inst->data, eap_session); - request->module = NULL; + eap_session_t *eap_session = talloc_get_type_abort(uctx, eap_session_t); + rlm_rcode_t result = unlang_stack_result(request); - return UNLANG_ACTION_CALCULATE_RESULT; + return mod_authenticate_result(request, instance, thread, eap_session, result); } /** Select the correct callback based on a response @@ -478,15 +447,17 @@ static unlang_action_t eap_call_submodule(UNUSED REQUEST *request, void *uctx) * - RLM_MODULE_YIELD Yield control back to the interpreter so it can * call the submodule. */ -static rlm_rcode_t eap_method_select(rlm_eap_t *inst, eap_session_t *eap_session) +static rlm_rcode_t eap_method_select(rlm_eap_t *inst, void *thread, eap_session_t *eap_session) { - eap_type_data_t *type = &eap_session->this_round->response->type; - REQUEST *request = eap_session->request; + eap_type_data_t *type = &eap_session->this_round->response->type; + REQUEST *request = eap_session->request; - eap_type_t next = inst->default_method; - VALUE_PAIR *vp; + rlm_eap_method_t const *method; + + eap_type_t next = inst->default_method; + VALUE_PAIR *vp; - eap_auth_rctx_t *rctx; + rlm_rcode_t rcode; /* * Session must have been thawed... @@ -591,23 +562,34 @@ static rlm_rcode_t eap_method_select(rlm_eap_t *inst, eap_session_t *eap_session eap_session->type = type->num; module_call: - MEM(rctx = talloc(request, eap_auth_rctx_t)); - rctx->caller = request->module; - rctx->inst = inst; - rctx->eap_session = eap_session; - rctx->rcode = RLM_MODULE_UNKNOWN; + method = &inst->methods[eap_session->type]; + + unlang_module_yield(request, mod_authenticate_result_async, NULL, eap_session); /* * mod_authenticate_result will be called after * eap_call_submodule finishes. */ - unlang_module_yield(request, mod_authenticate_result, mod_authenticate_signal, rctx); - unlang_push_function(request, eap_call_submodule, NULL, rctx); + RDEBUG2("Calling submodule %s", method->submodule->name); +// caller = request->module; +// request->module = method->submodule->name; + rcode = eap_session->process(method->submodule_inst->data, eap_session); +// request->module = caller; + + /* + * If the submodule yielded, then setup a resumption + * frame for when it finishes. + */ + if (rcode == RLM_MODULE_YIELD) return RLM_MODULE_YIELD; - return RLM_MODULE_YIELD; + /* + * If the submodule didn't yield call the result + * function directly using the C stack. + */ + return mod_authenticate_result(request, inst, thread, eap_session, rcode); } -static rlm_rcode_t mod_authenticate(void *instance, UNUSED void *thread, REQUEST *request) +static rlm_rcode_t mod_authenticate(void *instance, void *thread, REQUEST *request) { rlm_eap_t *inst = talloc_get_type_abort(instance, rlm_eap_t); eap_session_t *eap_session; @@ -646,14 +628,7 @@ static rlm_rcode_t mod_authenticate(void *instance, UNUSED void *thread, REQUEST * or with simple types like Identity and NAK, * process it ourselves. */ - if (eap_method_select(inst, eap_session) != RLM_MODULE_YIELD) { - RDEBUG2("Cleaning up EAP session"); - eap_fail(eap_session); - eap_session_destroy(&eap_session); - return RLM_MODULE_INVALID; - } - - return RLM_MODULE_YIELD; + return eap_method_select(inst, thread, eap_session); } /* diff --git a/src/modules/rlm_eap/types/rlm_eap_tls/rlm_eap_tls.c b/src/modules/rlm_eap/types/rlm_eap_tls/rlm_eap_tls.c index a807d7ea3fd..007dc7da2e3 100644 --- a/src/modules/rlm_eap/types/rlm_eap_tls/rlm_eap_tls.c +++ b/src/modules/rlm_eap/types/rlm_eap_tls/rlm_eap_tls.c @@ -56,15 +56,85 @@ static CONF_PARSER submodule_config[] = { * Do authentication, by letting EAP-TLS do most of the work. */ static rlm_rcode_t CC_HINT(nonnull) mod_process(void *instance, eap_session_t *eap_session); -static rlm_rcode_t mod_process(void *type_arg, eap_session_t *eap_session) + +static unlang_action_t eap_tls_virtual_server_result(REQUEST *request, rlm_rcode_t *presult, + UNUSED int *priority, void *uctx) +{ + eap_session_t *eap_session = talloc_get_type_abort(uctx, eap_session_t); + + switch (*presult) { + case RLM_MODULE_OK: + case RLM_MODULE_UPDATED: + if (eap_tls_success(eap_session) < 0) *presult = RLM_MODULE_FAIL; + break; + + default: + REDEBUG2("Certificate rejected by the virtual server"); + eap_tls_fail(eap_session); + *presult = RLM_MODULE_REJECT; + break; + } + + return UNLANG_ACTION_CALCULATE_RESULT; +} + +static rlm_rcode_t eap_tls_virtual_server(rlm_eap_tls_t *inst, eap_session_t *eap_session) +{ + REQUEST *request = eap_session->request; + CONF_SECTION *server_cs; + CONF_SECTION *section; + VALUE_PAIR *vp; + + /* set the virtual server to use */ + vp = fr_pair_find_by_num(request->control, 0, FR_VIRTUAL_SERVER, TAG_ANY); + if (vp) { + server_cs = virtual_server_find(vp->vp_strvalue); + if (!server_cs) { + REDEBUG2("Virtual server \"%s\" not found", vp->vp_strvalue); + error: + eap_tls_fail(eap_session); + return RLM_MODULE_INVALID; + } + } else { + server_cs = virtual_server_find(inst->virtual_server); + rad_assert(server_cs); + } + + section = cf_section_find(server_cs, "recv", "Access-Request"); + if (!section) { + REDEBUG2("Failed finding 'recv Access-Request { ... }' section of virtual server %s", + cf_section_name2(server_cs)); + goto error; + } + + if (!unlang_section(section)) { + REDEBUG("Failed to find pre-compiled unlang for section %s %s { ... }", + cf_section_name1(server_cs), cf_section_name2(server_cs)); + goto error; + } + + RDEBUG2("Validating certificate"); + + /* + * Catch the interpreter on the way back up the stack + */ + unlang_push_function(request, NULL, eap_tls_virtual_server_result, eap_session); + + /* + * Push unlang instructions for the virtual server section + */ + unlang_push_section(request, section, RLM_MODULE_NOOP, UNLANG_SUB_FRAME); + + return RLM_MODULE_YIELD; +} + +static rlm_rcode_t mod_process(void *instance, eap_session_t *eap_session) { eap_tls_status_t status; eap_tls_session_t *eap_tls_session = talloc_get_type_abort(eap_session->opaque, eap_tls_session_t); tls_session_t *tls_session = eap_tls_session->tls_session; REQUEST *request = eap_session->request; - rlm_eap_tls_t *inst; - - inst = type_arg; + rlm_eap_tls_t *inst = talloc_get_type_abort(instance, rlm_eap_tls_t); status = eap_tls_process(eap_session); if ((status == EAP_TLS_INVALID) || (status == EAP_TLS_FAIL)) { @@ -82,49 +152,10 @@ static rlm_rcode_t mod_process(void *type_arg, eap_session_t *eap_session) * it accepts the certificates, too. */ case EAP_TLS_ESTABLISHED: - if (inst->virtual_server) { - VALUE_PAIR *vp; - REQUEST *fake; - - /* create a fake request */ - fake = request_alloc_fake(request); - rad_assert(!fake->packet->vps); - - fake->packet->vps = fr_pair_list_copy(fake->packet, request->packet->vps); - - /* set the virtual server to use */ - if ((vp = fr_pair_find_by_num(request->control, 0, FR_VIRTUAL_SERVER, TAG_ANY)) != NULL) { - fake->server_cs = virtual_server_find(vp->vp_strvalue); - if (!fake->server_cs) { - REDEBUG2("Virtual server \"%s\" not found", vp->vp_strvalue); - talloc_free(fake); - eap_tls_fail(eap_session); - return RLM_MODULE_INVALID; - } - } else { - fake->server_cs = virtual_server_find(inst->virtual_server); - rad_assert(fake->server_cs); - } - - RDEBUG2("Validating certificate"); - rad_virtual_server(fake); - - /* copy the reply vps back to our reply */ - fr_pair_list_mcopy_by_num(request->reply, &request->reply->vps, &fake->reply->vps, 0, 0, - TAG_ANY); - - /* reject if virtual server didn't return accept */ - if (fake->reply->code != FR_CODE_ACCESS_ACCEPT) { - RDEBUG2("Certificate rejected by the virtual server"); - talloc_free(fake); - eap_tls_fail(eap_session); - return RLM_MODULE_REJECT; - } - - talloc_free(fake); - /* success */ - } - break; + if (inst->virtual_server) return eap_tls_virtual_server(inst, eap_session); + if (eap_tls_success(eap_session) < 0) return RLM_MODULE_FAIL; + + return RLM_MODULE_OK; /* * The TLS code is still working on the TLS @@ -155,19 +186,15 @@ static rlm_rcode_t mod_process(void *type_arg, eap_session_t *eap_session) return RLM_MODULE_REJECT; } - - if (eap_tls_success(eap_session) < 0) return RLM_MODULE_FAIL; - - return RLM_MODULE_OK; } /* * Send an initial eap-tls request to the peer, using the libeap functions. */ -static rlm_rcode_t mod_session_init(void *type_arg, eap_session_t *eap_session) +static rlm_rcode_t mod_session_init(void *uctx, eap_session_t *eap_session) { eap_tls_session_t *eap_tls_session; - rlm_eap_tls_t *inst = talloc_get_type_abort(type_arg, rlm_eap_tls_t); + rlm_eap_tls_t *inst = talloc_get_type_abort(uctx, rlm_eap_tls_t); VALUE_PAIR *vp; bool client_cert; @@ -204,7 +231,7 @@ static rlm_rcode_t mod_session_init(void *type_arg, eap_session_t *eap_session) eap_session->process = mod_process; - return RLM_MODULE_OK; + return RLM_MODULE_HANDLED; } /*