]> git.ipfire.org Git - thirdparty/freeradius-server.git/commitdiff
Use async user DN lookup in LDAP authentication
authorNick Porter <nick@portercomputing.co.uk>
Tue, 20 Sep 2022 16:47:20 +0000 (17:47 +0100)
committerNick Porter <nick@portercomputing.co.uk>
Tue, 4 Apr 2023 07:30:10 +0000 (08:30 +0100)
src/modules/rlm_ldap/rlm_ldap.c

index b4925d59a1512fc2484836fdf712f539faa592aa..10758b16233cc91ec9dbd7b3e36fc3d3da9c9f81 100644 (file)
@@ -257,6 +257,18 @@ global_lib_autoinst_t const *rlm_ldap_lib[] = {
        GLOBAL_LIB_TERMINATOR
 };
 
+/** Holds state of in progress async authentication
+ *
+ */
+typedef struct {
+       char const              *dn;
+       char const              *password;
+       rlm_ldap_t const        *inst;
+       fr_ldap_thread_t        *thread;
+       fr_ldap_thread_trunk_t  *ttrunk;
+       ldap_auth_mod_env_t     *mod_env;
+} ldap_auth_ctx_t;
+
 static xlat_arg_parser_t const ldap_escape_xlat_arg[] = {
        { .required = true, .concat = true, .type = FR_TYPE_STRING },
        XLAT_ARG_PARSER_TERMINATOR
@@ -1028,13 +1040,55 @@ cleanup:
        return 1;
 }
 
+/** Perform async lookup of user DN if required for authentication
+ *
+ */
+static unlang_action_t mod_authenticate_start(UNUSED rlm_rcode_t *p_result, UNUSED int *priority,
+                                             request_t *request, void *uctx)
+{
+       ldap_auth_ctx_t *auth_ctx = talloc_get_type_abort(uctx, ldap_auth_ctx_t);
+
+       return rlm_ldap_find_user_async(auth_ctx, auth_ctx->inst, request, &auth_ctx->mod_env->user_base,
+                                       &auth_ctx->mod_env->user_filter, auth_ctx->ttrunk, NULL, NULL);
+}
+
+/** Initiate async LDAP bind to authenticate user
+ *
+ */
+static unlang_action_t mod_authenticate_resume(rlm_rcode_t *p_result, UNUSED int *priority,
+                                              request_t *request, void *uctx)
+{
+       ldap_auth_ctx_t *auth_ctx = talloc_get_type_abort(uctx, ldap_auth_ctx_t);
+
+       /*
+        *      Arriving here from an LDAP search will mean the dn in auth_ctx is NULL.
+        */
+       if (!auth_ctx->dn) auth_ctx->dn = rlm_find_user_dn_cached(request);
+
+       /*
+        *      No DN found - can't authenticate the user.
+        */
+       if (!auth_ctx->dn) {
+       fail:
+               talloc_free(auth_ctx);
+               RETURN_MODULE_FAIL;
+       }
+
+       /*
+        *      Attempt a bind using the thread specific connection for bind auths
+        */
+       if (fr_ldap_bind_auth_async(request, auth_ctx->thread, auth_ctx->dn, auth_ctx->password) < 0) goto fail;
+
+       RETURN_MODULE_RCODE(unlang_interpret_synchronous(unlang_interpret_event_list(request), request));
+}
+
 static unlang_action_t CC_HINT(nonnull) mod_authenticate(rlm_rcode_t *p_result, module_ctx_t const *mctx, request_t *request)
 {
        rlm_ldap_t const        *inst = talloc_get_type_abort_const(mctx->inst->data, rlm_ldap_t);
        fr_ldap_thread_t        *thread = talloc_get_type_abort(module_rlm_thread_by_data(inst)->data, fr_ldap_thread_t);
-       rlm_rcode_t             rcode;
-       char const              *dn;
        fr_ldap_thread_trunk_t  *ttrunk = NULL;
+       ldap_auth_ctx_t         *auth_ctx;
+       ldap_auth_mod_env_t     *mod_env = talloc_get_type_abort(mctx->env_data, ldap_auth_mod_env_t);
 
        char                    sasl_mech_buff[LDAP_MAX_DN_STR_LEN];
        char                    sasl_proxy_buff[LDAP_MAX_DN_STR_LEN];
@@ -1096,16 +1150,15 @@ static unlang_action_t CC_HINT(nonnull) mod_authenticate(rlm_rcode_t *p_result,
                if (tmpl_expand(&sasl.mech, sasl_mech_buff, sizeof(sasl_mech_buff), request,
                                inst->user_sasl.mech, fr_ldap_escape_func, inst) < 0) {
                        RPEDEBUG("Failed expanding user.sasl.mech");
-                       rcode = RLM_MODULE_FAIL;
-                       goto finish;
+               fail:
+                       RETURN_MODULE_RCODE(RLM_MODULE_FAIL);
                }
 
                if (inst->user_sasl.proxy) {
                        if (tmpl_expand(&sasl.proxy, sasl_proxy_buff, sizeof(sasl_proxy_buff), request,
                                        inst->user_sasl.proxy, fr_ldap_escape_func, inst) < 0) {
                                RPEDEBUG("Failed expanding user.sasl.proxy");
-                               rcode = RLM_MODULE_FAIL;
-                               goto finish;
+                               goto fail;
                        }
                }
 
@@ -1113,30 +1166,31 @@ static unlang_action_t CC_HINT(nonnull) mod_authenticate(rlm_rcode_t *p_result,
                        if (tmpl_expand(&sasl.realm, sasl_realm_buff, sizeof(sasl_realm_buff), request,
                                        inst->user_sasl.realm, fr_ldap_escape_func, inst) < 0) {
                                RPEDEBUG("Failed expanding user.sasl.realm");
-                               rcode = RLM_MODULE_FAIL;
-                               goto finish;
+                               goto fail;
                        }
                }
        }
 
        RDEBUG2("Login attempt by \"%pV\"", &username->data);
 
-       /*
-        *      Get the DN by doing a search.
-        */
-       dn = rlm_ldap_find_user(inst, request, ttrunk, NULL, false, NULL, NULL, &rcode);
-       if (!dn) RETURN_MODULE_RCODE(rcode);
+       auth_ctx = talloc(unlang_interpret_frame_talloc_ctx(request), ldap_auth_ctx_t);
+       *auth_ctx = (ldap_auth_ctx_t){
+               .password = password->vp_strvalue,
+               .thread = thread,
+               .inst = inst,
+               .mod_env = mod_env,
+               .ttrunk = ttrunk
+       };
 
        /*
-        *      Attempt a bind using the thread specific connection for bind auths
+        *      Check for a cahed copy of the DN
         */
-       if (fr_ldap_bind_auth_async(request, thread, dn, password->vp_strvalue) < 0) RETURN_MODULE_FAIL;
+       auth_ctx->dn = rlm_find_user_dn_cached(request);
 
-       rcode = unlang_interpret_synchronous(unlang_interpret_event_list(request), request);
+       if (unlang_function_push(request, auth_ctx->dn ? NULL : mod_authenticate_start, mod_authenticate_resume,
+                                NULL, 0, UNLANG_SUB_FRAME, auth_ctx) < 0) RETURN_MODULE_FAIL;
 
-finish:
-
-       RETURN_MODULE_RCODE(rcode);
+       return UNLANG_ACTION_PUSHED_CHILD;
 }
 
 /** Search for and apply an LDAP profile