]> git.ipfire.org Git - thirdparty/freeradius-server.git/commitdiff
Add rlm_ldap_find_user_async() and supporting functions
authorNick Porter <nick@portercomputing.co.uk>
Tue, 20 Sep 2022 16:37:21 +0000 (17:37 +0100)
committerNick Porter <nick@portercomputing.co.uk>
Tue, 4 Apr 2023 07:30:09 +0000 (08:30 +0100)
src/modules/rlm_ldap/rlm_ldap.h
src/modules/rlm_ldap/user.c

index ea63e50b785a674f1020f29a7ef46ccb9b5df51c..72f3ce500bed59a750aab7cb21fb8e208e0b4adb 100644 (file)
@@ -161,6 +161,10 @@ static inline char const *rlm_find_user_dn_cached(request_t *request)
        return vp->vp_strvalue;
 }
 
+int rlm_ldap_find_user_async(TALLOC_CTX *ctx, rlm_ldap_t const *inst, request_t *request, fr_value_box_t *base,
+                            fr_value_box_t *filter_box, fr_ldap_thread_trunk_t *ttrunk, char const *attrs[],
+                            fr_ldap_query_t **query_out);
+
 char const *rlm_ldap_find_user(rlm_ldap_t const *inst, request_t *request, fr_ldap_thread_trunk_t *tconn,
                               char const *attrs[], bool force, LDAPMessage **result, LDAP **handle, rlm_rcode_t *rcode);
 
index 603884291c619a7702a4a66a9c767b9016c56b0a..ace6382c8549a6b988332dcffa8a9c83bde031f7 100644 (file)
@@ -35,6 +35,145 @@ USES_APPLE_DEPRECATED_API
 
 #include "rlm_ldap.h"
 
+/** Holds state of user searches in progress
+ *
+ */
+typedef struct {
+       rlm_ldap_t const        *inst;
+       fr_ldap_thread_trunk_t  *ttrunk;
+       char const              *base_dn;
+       char const              *filter;
+       char const * const      *attrs;
+       fr_ldap_query_t         *query;
+       fr_ldap_query_t         **out;
+} ldap_user_find_ctx_t;
+
+/** Process the results of an async user lookup
+ *
+ */
+static unlang_action_t ldap_find_user_async_result(rlm_rcode_t *p_result, UNUSED int *priority, request_t *request, void *uctx)
+{
+       ldap_user_find_ctx_t    *user_ctx = talloc_get_type_abort(uctx, ldap_user_find_ctx_t);
+       fr_ldap_query_t         *query = user_ctx->query;
+       LDAPMessage             *entry;
+       int                     cnt, ldap_errno;
+       char                    *dn;
+       fr_pair_t               *vp;
+
+       cnt = ldap_count_entries(query->ldap_conn->handle, query->result);
+       if (cnt == 0) RETURN_MODULE_NOTFOUND;
+
+       if ((!user_ctx->inst->userobj_sort_ctrl) && (cnt > 1)) {
+               REDEBUG("Ambiguous search result, returned %i unsorted entries (should return 1 or 0).  "
+                       "Enable sorting, or specify a more restrictive base_dn, filter or scope", cnt);
+               REDEBUG("The following entries were returned:");
+               RINDENT();
+               for (entry = ldap_first_entry(query->ldap_conn->handle, query->result);
+                    entry;
+                    entry = ldap_next_entry(query->ldap_conn->handle, entry)) {
+                       dn = ldap_get_dn(query->ldap_conn->handle, entry);
+                       REDEBUG("%s", dn);
+                       ldap_memfree(dn);
+               }
+               REXDENT();
+               RETURN_MODULE_INVALID;
+       }
+
+       entry = ldap_first_entry(query->ldap_conn->handle, query->result);
+       if (!entry) {
+               ldap_get_option(query->ldap_conn->handle, LDAP_OPT_RESULT_CODE, &ldap_errno);
+               REDEBUG("Failed retrieving entry: %s",
+                       ldap_err2string(ldap_errno));
+
+               RETURN_MODULE_FAIL;
+       }
+
+       dn = ldap_get_dn(query->ldap_conn->handle, entry);
+       if (!dn) {
+               ldap_get_option(query->ldap_conn->handle, LDAP_OPT_RESULT_CODE, &ldap_errno);
+               REDEBUG("Retrieving object DN from entry failed: %s", ldap_err2string(ldap_errno));
+
+               RETURN_MODULE_FAIL;
+       }
+       fr_ldap_util_normalise_dn(dn, dn);
+
+       RDEBUG2("User object found at DN \"%s\"", dn);
+
+       MEM(pair_update_control(&vp, attr_ldap_userdn) >= 0);
+       fr_pair_value_strdup(vp, dn, false);
+       ldap_memfree(dn);
+       if (user_ctx->out) *user_ctx->out = user_ctx->query;
+
+       RETURN_MODULE_OK;
+}
+
+/** Cancel a user search
+ */
+static void ldap_find_user_async_cancel(UNUSED request_t *request, UNUSED fr_signal_t action, void *uctx)
+{
+       ldap_user_find_ctx_t    *user_ctx = talloc_get_type_abort(uctx, ldap_user_find_ctx_t);
+
+       /*
+        *      If the query is not in flight, just return
+        */
+       if (!user_ctx->query || !user_ctx->query->treq) return;
+
+       fr_trunk_request_signal_cancel(user_ctx->query->treq);
+}
+
+/** Initiate asynchronous retrieval of the DN of a user object
+ *
+ * Retrieves the DN of a user and adds it to the control list as LDAP-UserDN.
+ * Will also retrieve any attributes passed.
+ *
+ * This potentially allows for all authorization and authentication checks to be performed in one
+ * ldap search operation, which is a big bonus given the number of crappy, slow *cough*AD*cough*
+ * LDAP directory servers out there.
+ *
+ * @param[in] ctx      in which to allocate the query.
+ * @param[in] inst     rlm_ldap configuration.
+ * @param[in] request  Current request.
+ * @param[in] base     DN to search in.
+ * @param[in] filter   to use in LDAP search.
+ * @param[in] ttrunk   LDAP thread trunk to use.
+ * @param[in] attrs    Additional attributes to retrieve, may be NULL.
+ * @param[in] query_out        Where to put a pointer to the LDAP query structure -
+ *                     for extracting extra returned attributes, may be NULL.
+ * @return
+ *     - UNLANG_ACTION_YIELD on success.
+ *     - UNLANG_ACTION_FAIL on failure.
+ */
+unlang_action_t rlm_ldap_find_user_async(TALLOC_CTX *ctx, rlm_ldap_t const *inst, request_t *request,
+                                        fr_value_box_t *base, fr_value_box_t *filter,
+                                        fr_ldap_thread_trunk_t *ttrunk, char const *attrs[], fr_ldap_query_t **query_out)
+{
+       static char const       *tmp_attrs[] = { NULL };
+       ldap_user_find_ctx_t    *user_ctx;
+       LDAPControl             *serverctrls[] = { inst->userobj_sort_ctrl, NULL };
+
+       if (!attrs) memset(&attrs, 0, sizeof(tmp_attrs));
+
+       user_ctx = talloc_zero(ctx, ldap_user_find_ctx_t);
+       *user_ctx = (ldap_user_find_ctx_t) {
+               .inst = inst,
+               .ttrunk = ttrunk,
+               .base_dn = base->vb_strvalue,
+               .attrs = attrs,
+               .out = query_out
+       };
+
+       if (filter) user_ctx->filter = filter->vb_strvalue;
+       if (unlang_function_push(request, NULL, ldap_find_user_async_result, ldap_find_user_async_cancel,
+                                ~FR_SIGNAL_CANCEL, UNLANG_SUB_FRAME, user_ctx) < 0) {
+               talloc_free(user_ctx);
+               return UNLANG_ACTION_FAIL;
+       }
+
+       return fr_ldap_trunk_search(NULL, user_ctx, &user_ctx->query, request, user_ctx->ttrunk,
+                                   user_ctx->base_dn, user_ctx->inst->userobj_scope, user_ctx->filter,
+                                   user_ctx->attrs, serverctrls, NULL, true);
+}
+
 /** Retrieve the DN of a user object
  *
  * Retrieves the DN of a user and adds it to the control list as LDAP-UserDN. Will also retrieve any