]> git.ipfire.org Git - thirdparty/freeradius-server.git/commitdiff
Make tls-verify-cert work with async calls
authorArran Cudbard-Bell <a.cudbardb@freeradius.org>
Wed, 31 Jan 2018 17:47:47 +0000 (10:47 -0700)
committerArran Cudbard-Bell <a.cudbardb@freeradius.org>
Wed, 31 Jan 2018 17:47:58 +0000 (10:47 -0700)
src/include/unlang.h
src/main/unlang_op.c
src/modules/rlm_eap/rlm_eap.c
src/modules/rlm_eap/types/rlm_eap_tls/rlm_eap_tls.c

index 90037c4a7701188da6439dea960843dec4d02194..223e0f4ee0c71361d8cb5d13b6959a6362ffe6f3 100644 (file)
@@ -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);
 
index 43724198c88d9c433528ddc0407980fbef5aedee..93f018e154770c883f309cd2e0fc3eee15fb3fa0 100644 (file)
@@ -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;
 }
index 69cec836600045ddcca1a3b51842ca095e616c20..8b6384e6142426e9e67b04774430848e90519e00 100644 (file)
@@ -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);
 }
 
 /*
index a807d7ea3fd4bed2aa67af78c2e562cd3dfc6e4e..007dc7da2e3c464f4c79e5796f72dd79a8c05887 100644 (file)
@@ -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;
 }
 
 /*