#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