]> git.ipfire.org Git - thirdparty/freeradius-server.git/commitdiff
v4: More changes to support moving LDAP to use trunks (#4257)
authorNick Porter <nick@portercomputing.co.uk>
Thu, 7 Oct 2021 16:26:15 +0000 (17:26 +0100)
committerGitHub <noreply@github.com>
Thu, 7 Oct 2021 16:26:15 +0000 (11:26 -0500)
* Free LDAP query if it is cancelled

* Correct lookup values for supported LDAP extensions

* Remove unused clients.c

* Initialise tree of outstanding LDAP queries

* Define sync_ldap_query_t

A structure to support synchronous use of async ldap queries

* Define callbacks to support use of async LDAP queries in place of sync ones

A temporary set of wrapper functions before fully rewriting rlm_ldap to
be fully async

* Define SET_LDAP_CTRLS macro to copy LDAP server / client controls

* Define fr_ldap_trunk_search()

Temporary wrapper function to perform an async ldap search on a trunk
connection

* Define fr_ldap_trunk_modify()

Temporary wrapper function to perform an async ldap modification on a
trunk connection

src/lib/ldap/base.c
src/lib/ldap/base.h
src/lib/ldap/connection.c
src/modules/rlm_ldap/clients.c [deleted file]

index aeaeb9017ff25f4e38f238404aaa2bc0f45f1d75..1a7955660f0b32b3133ca39f482f5f989c09b046 100644 (file)
@@ -35,6 +35,7 @@ USES_APPLE_DEPRECATED_API
 
 #include <freeradius-devel/server/base.h>
 #include <freeradius-devel/ldap/base.h>
+#include <freeradius-devel/unlang/function.h>
 
 LDAP *ldap_global_handle;                      //!< Hack for OpenLDAP libldap global initialisation.
 
@@ -57,8 +58,8 @@ fr_table_num_sorted_t const fr_ldap_connection_states[] = {
 size_t fr_ldap_connection_states_len = NUM_ELEMENTS(fr_ldap_connection_states);
 
 fr_table_num_sorted_t const fr_ldap_supported_extensions[] = {
-       { L("bindname"),        LDAP_DEREF_NEVER        },
-       { L("x-bindpw"),        LDAP_DEREF_SEARCHING    }
+       { L("bindname"),        LDAP_EXT_BINDNAME       },
+       { L("x-bindpw"),        LDAP_EXT_BINDPW         }
 };
 size_t fr_ldap_supported_extensions_len = NUM_ELEMENTS(fr_ldap_supported_extensions);
 
@@ -94,6 +95,11 @@ fr_table_num_sorted_t const fr_ldap_dereference[] = {
 };
 size_t fr_ldap_dereference_len = NUM_ELEMENTS(fr_ldap_dereference);
 
+typedef struct {
+       fr_ldap_query_t *query;
+       LDAPMessage     **result;
+} sync_ldap_query_t;
+
 /** Prints information to the debug log on the current timeout settings
  *
  * There are so many different timers in LDAP it's often hard to debug
@@ -774,6 +780,123 @@ fr_ldap_rcode_t fr_ldap_search_async(int *msgid, request_t *request,
        return LDAP_PROC_SUCCESS;
 }
 
+/** Submit an LDAP query to be handled by a trunk conneciton
+ *
+ */
+static unlang_action_t ldap_trunk_query_start(UNUSED rlm_rcode_t *p_result, UNUSED int *priority, request_t *request, void *uctx)
+{
+       fr_ldap_query_t *query = talloc_get_type_abort(uctx, fr_ldap_query_t);
+
+       fr_trunk_request_enqueue(&query->treq, query->ttrunk->trunk, request, query, NULL);
+
+       return UNLANG_ACTION_YIELD;
+}
+
+/** Handle the return code from parsed LDAP results to set the module rcode
+ *
+ */
+static unlang_action_t ldap_trunk_query_results(rlm_rcode_t *p_result, UNUSED int *priority, UNUSED request_t *request, void *uctx)
+{
+       fr_ldap_query_t         *query = talloc_get_type_abort(uctx, fr_ldap_query_t);
+
+       switch (query->ret) {
+       case LDAP_RESULT_PENDING:
+               /* The query we want hasn't returned yet */
+               return UNLANG_ACTION_YIELD;
+
+       case LDAP_RESULT_SUCCESS:
+               RETURN_MODULE_OK;
+
+       case LDAP_RESULT_BAD_DN:
+       case LDAP_RESULT_NO_RESULT:
+               RETURN_MODULE_NOTFOUND;
+
+       default:
+               RETURN_MODULE_FAIL;
+       }
+}
+
+/** Signal an LDAP query running on a trunk connection to cancel
+ *
+ */
+static void ldap_trunk_query_cancel(UNUSED request_t *request, fr_state_signal_t action, void *uctx)
+{
+       fr_ldap_query_t *query = talloc_get_type_abort(uctx, fr_ldap_query_t);
+
+       if (action != FR_SIGNAL_CANCEL) return;
+
+       fr_trunk_request_signal_cancel(query->treq);
+
+}
+
+#define SET_LDAP_CTRLS(_dest, _src) \
+do { \
+       if (!_src) break; \
+       int     i; \
+       for (i = 0; i < LDAP_MAX_CONTROLS; i++) { \
+               if (!(_src[i])) break; \
+               _dest[i].control = _src[i]; \
+       } \
+} while (0)
+
+/** Run an async search LDAP query on a trunk connection
+ *
+ * @param[in] ctx              to allocate the query in.
+ * @param[out] query           that has been allocated.
+ * @param[in] request          this query relates to.
+ * @param[in] ttrunk           to submit the query to.
+ * @param[in] base_dn          for the search.
+ * @param[in] scope            of the search.
+ * @param[in] filter           for the search.
+ * @param[in] attrs            to be returned.
+ * @param[in] serverctrls      specific to this query.
+ * @param[in] clientctrls      specific to this query.
+ */
+int fr_ldap_trunk_search(TALLOC_CTX *ctx, fr_ldap_query_t **query, request_t *request, fr_ldap_thread_trunk_t *ttrunk, char const *base_dn,
+                        int scope, char const *filter, char const * const *attrs, LDAPControl **serverctrls, LDAPControl **clientctrls )
+{
+       *query = fr_ldap_query_alloc(ctx);
+
+       (*query)->type = LDAP_REQUEST_SEARCH;
+       (*query)->request = request;
+       (*query)->ttrunk = ttrunk;
+       (*query)->dn = base_dn;
+       (*query)->search.scope = scope;
+       (*query)->search.filter = filter;
+       memcpy(&(*query)->search.attrs,  &attrs, sizeof((*query)->search.attrs));
+       SET_LDAP_CTRLS((*query)->serverctrls, serverctrls);
+       SET_LDAP_CTRLS((*query)->clientctrls, clientctrls);
+
+       return unlang_function_push(request, ldap_trunk_query_start, ldap_trunk_query_results, ldap_trunk_query_cancel, UNLANG_TOP_FRAME, *query);
+}
+
+/** Run an async modification LDAP query on a trunk connection
+ *
+ * @param[in] ctx              to allocate the query in.
+ * @param[out] query           that has been allocated.
+ * @param[in] request          this query relates to.
+ * @param[in] ttrunk           to submit the query to.
+ * @param[in] dn               of the object being modified.
+ * @param[in] mods             to be performed.
+ * @param[in] serverctrls      specific to this query.
+ * @param[in] clientctrls      specific to this query.
+ */
+int fr_ldap_trunk_modify(TALLOC_CTX *ctx, fr_ldap_query_t **query, request_t *request, fr_ldap_thread_trunk_t *ttrunk,
+                        char const *dn, LDAPMod *mods[], LDAPControl **serverctrls, LDAPControl **clientctrls)
+{
+       *query = fr_ldap_query_alloc(ctx);
+
+       (*query)->type = LDAP_REQUEST_MODIFY;
+       (*query)->request = request;
+       (*query)->ttrunk = ttrunk;
+       (*query)->dn = dn;
+       (*query)->mods = mods;
+       SET_LDAP_CTRLS((*query)->serverctrls, serverctrls);
+       SET_LDAP_CTRLS((*query)->clientctrls, clientctrls);
+
+       return unlang_function_push(request, ldap_trunk_query_start, ldap_trunk_query_results, ldap_trunk_query_cancel, UNLANG_TOP_FRAME, *query);
+}
+
 /** Modify something in the LDAP directory
  *
  * Binds as the administrative user and attempts to modify an LDAP object.
index 80756c14f93f4c25618e86224193ce89ce0c3887..906f585b29d3dde002d3d1d112f712f5cc582a0a 100644 (file)
@@ -551,6 +551,13 @@ static inline int8_t fr_ldap_query_cmp(void const *one, void const *two)
 
 fr_ldap_query_t *fr_ldap_query_alloc(TALLOC_CTX *ctx);
 
+int fr_ldap_trunk_search(TALLOC_CTX *ctx, fr_ldap_query_t **query, request_t *request, fr_ldap_thread_trunk_t *ttrunk,
+                        char const *base_dn, int scope, char const *filter, char const * const *attrs,
+                        LDAPControl **serverctrls, LDAPControl **clientctrls);
+
+int fr_ldap_trunk_modify(TALLOC_CTX *ctx, fr_ldap_query_t **query, request_t *request, fr_ldap_thread_trunk_t *ttrunk,
+                        char const *dn, LDAPMod *mods[], LDAPControl **serverctrls, LDAPControl **clientctrls);
+
 /*
  *     ldap.c - Wrappers arounds OpenLDAP functions.
  */
index 49eb103405b4b340b5cfe87fc4e9ef94199e6946..d586b2fcf0e116eccb5c632b58ea1c6f7993a678 100644 (file)
@@ -483,6 +483,11 @@ static fr_connection_state_t _ldap_connection_init(void **h, fr_connection_t *co
        state = fr_ldap_state_next(c);
        if (state == FR_LDAP_STATE_ERROR) goto error;
 
+       /*
+        *      Initialise tree for outstanding queries handled by this connection
+        */
+       MEM(c->queries = fr_rb_inline_talloc_alloc(c, fr_ldap_query_t, node, fr_ldap_query_cmp, NULL));
+
        *h = c; /* Set the handle */
 
        return FR_CONNECTION_STATE_CONNECTING;
@@ -607,6 +612,11 @@ static void ldap_request_cancel_mux(fr_trunk_connection_t *tconn, fr_connection_
                fr_rb_remove(ldap_conn->queries, query);
 
                fr_trunk_request_signal_cancel_complete(treq);
+
+               /*
+                *      Ensure any query resouces are cleared straight away
+                */
+               talloc_free(query);
        }
 }
 
diff --git a/src/modules/rlm_ldap/clients.c b/src/modules/rlm_ldap/clients.c
deleted file mode 100644 (file)
index 1028116..0000000
+++ /dev/null
@@ -1,273 +0,0 @@
-/*
- *   This program is is free software; you can redistribute it and/or modify
- *   it under the terms of the GNU General Public License as published by
- *   the Free Software Foundation; either version 2 of the License, or (at
- *   your option) any later version.
- *
- *   This program is distributed in the hope that it will be useful,
- *   but WITHOUT ANY WARRANTY; without even the implied warranty of
- *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *   GNU General Public License for more details.
- *
- *   You should have received a copy of the GNU General Public License
- *   along with this program; if not, write to the Free Software
- *   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
- */
-
-/**
- * $Id$
- * @file clients.c
- * @brief LDAP module dynamic clients.
- *
- * @author Arran Cudbard-Bell (a.cudbardb@freeradius.org)
- * @copyright 2013,2015 Arran Cudbard-Bell (a.cudbardb@freeradius.org)
- * @copyright 2013-2015 The FreeRADIUS Server Project.
- */
-RCSID("$Id$")
-
-USES_APPLE_DEPRECATED_API
-
-#include <freeradius-devel/util/debug.h>
-#include <ctype.h>
-
-#define LOG_PREFIX "rlm_ldap (%s) - "
-#define LOG_PREFIX_ARGS inst->name
-
-#include "rlm_ldap.h"
-
-/** Iterate over pairs in mapping section recording their values in an array
- *
- * This array is the list of attributes we retrieve from LDAP, and is NULL
- * terminated.
- *
- * If we hit a CONF_SECTION we recurse and process its CONF_PAIRS too.
- *
- * @param[out] values array of char pointers.
- * @param[in,out] idx records current array offset.
- * @param[in] cs to iterate over.
- * @return
- *     - 0 on success.
- *     - -1 on failure.
- */
-static int rlm_ldap_client_get_attrs(char const **values, int *idx, CONF_SECTION const *cs)
-{
-       CONF_ITEM const *ci;
-
-       for (ci = cf_item_next(cs, NULL);
-            ci != NULL;
-            ci = cf_item_next(cs, ci)) {
-               char const *value;
-
-               if (cf_item_is_section(ci)) {
-                       if (rlm_ldap_client_get_attrs(values, idx, cf_item_to_section(ci)) < 0) return -1;
-                       continue;
-               }
-
-               value = cf_pair_value(cf_item_to_pair(ci));
-               if (!value) return -1;
-
-               values[(*idx)++] = value;
-       }
-
-       values[*idx] = NULL;
-
-       return 0;
-}
-
-typedef struct {
-       fr_ldap_connection_t *conn;
-       LDAPMessage *entry;
-} ldap_client_data_t;
-
-static int _get_client_value(char **out, CONF_PAIR const *cp, void *data)
-{
-       struct berval **values;
-       ldap_client_data_t *this = data;
-
-       values = ldap_get_values_len(this->conn->handle, this->entry, cf_pair_value(cp));
-       if (!values) {
-               *out = NULL;
-               return 0;
-       }
-
-       *out = fr_ldap_berval_to_string(NULL, values[0]);
-       ldap_value_free_len(values);
-
-       if (!*out) return -1;
-       return 0;
-}
-
-/** Load clients from LDAP on server start
- *
- * @param[in] inst rlm_ldap configuration.
- * @param[in] tmpl to use as the base for the new client.
- * @param[in] map to load client attribute/LDAP attribute mappings from.
- * @return
- *     - 0 on success.
- *     - -1 on failure.
- */
-int rlm_ldap_client_load(rlm_ldap_t const *inst, CONF_SECTION *tmpl, CONF_SECTION *map)
-{
-       int             ret = 0;
-       fr_ldap_rcode_t status;
-       fr_ldap_connection_t    *conn = NULL;
-
-       char const      **attrs = NULL;
-
-       CONF_PAIR       *cp;
-       int             count = 0, idx = 0;
-
-       LDAPMessage     *result = NULL;
-       LDAPMessage     *entry;
-       char            *dn = NULL;
-
-       RADCLIENT       *c;
-
-       DEBUG("Loading dynamic clients");
-
-       fr_assert(inst->clientobj_base_dn);
-
-       count = cf_pair_count(map);
-       count++;
-
-       /*
-        *      Create an array of LDAP attributes to feed to fr_ldap_search.
-        */
-       attrs = talloc_array(inst, char const *, count);
-       if (rlm_ldap_client_get_attrs(attrs, &idx, map) < 0) {
-               talloc_free(attrs);
-               return -1;
-       }
-
-       conn = mod_conn_get(inst, NULL);
-       if (!conn) {
-               talloc_free(attrs);
-               return -1;
-       }
-
-       /*
-        *      Perform all searches as the admin user.
-        */
-       if (conn->rebound) {
-               status = fr_ldap_bind(NULL, &conn,
-                                     conn->config->admin_identity, conn->config->admin_password,
-                                     &(conn->config->admin_sasl),
-                                     NULL,
-                                     NULL, NULL);
-               if (status != LDAP_PROC_SUCCESS) {
-                       ret = -1;
-                       goto finish;
-               }
-
-               fr_assert(conn);
-
-               conn->rebound = false;
-       }
-
-       status = fr_ldap_search(&result, NULL, &conn, inst->clientobj_base_dn, inst->clientobj_scope,
-                               inst->clientobj_filter, attrs, NULL, NULL);
-       switch (status) {
-       case LDAP_PROC_SUCCESS:
-               break;
-
-       case LDAP_PROC_NO_RESULT:
-               INFO("No clients were found in the directory");
-               ret = 0;
-               goto finish;
-
-       default:
-               ret = -1;
-               goto finish;
-       }
-
-       fr_assert(conn);
-       entry = ldap_first_entry(conn->handle, result);
-       if (!entry) {
-               int ldap_errno;
-
-               ldap_get_option(conn->handle, LDAP_OPT_RESULT_CODE, &ldap_errno);
-               ERROR("Failed retrieving entry: %s", ldap_err2string(ldap_errno));
-
-               ret = -1;
-               goto finish;
-       }
-
-       do {
-               ldap_client_data_t      data;
-
-               CONF_SECTION            *client;
-               char                    *id;
-
-               struct berval           **values;
-
-               id = dn = ldap_get_dn(conn->handle, entry);
-               if (!dn) {
-                       int ldap_errno;
-
-                       ldap_get_option(conn->handle, LDAP_OPT_RESULT_CODE, &ldap_errno);
-                       ERROR("Retrieving object DN from entry failed: %s", ldap_err2string(ldap_errno));
-
-                       goto finish;
-               }
-               fr_ldap_util_normalise_dn(dn, dn);
-
-               cp = cf_pair_find(map, "identifier");
-               if (cp) {
-                       values = ldap_get_values_len(conn->handle, entry, cf_pair_value(cp));
-                       if (values) id = fr_ldap_berval_to_string(NULL, values[0]);
-                       ldap_value_free_len(values);
-               }
-
-               /*
-                *      Iterate over mapping sections
-                */
-               client = tmpl ? cf_section_dup(NULL, NULL, tmpl, "client", id, true) :
-                               cf_section_alloc(NULL, NULL, "client", id);
-
-               data.conn = conn;
-               data.entry = entry;
-
-               if (client_map_section(client, map, _get_client_value, &data) < 0) {
-                       talloc_free(client);
-                       ret = -1;
-                       goto finish;
-               }
-
-               /*
-                *@todo these should be parented from something
-                */
-               c = client_afrom_cs(NULL, client, NULL);
-               if (!c) {
-                       talloc_free(client);
-                       ret = -1;
-                       goto finish;
-               }
-
-               /*
-                *      Client parents the CONF_SECTION which defined it
-                */
-               talloc_steal(c, client);
-
-               if (!client_add(NULL, c)) {
-                       ERROR("Failed to add client \"%s\", possible duplicate?", dn);
-                       ret = -1;
-                       client_free(c);
-                       goto finish;
-               }
-
-               DEBUG("Client \"%s\" added", dn);
-
-               ldap_memfree(dn);
-               dn = NULL;
-       } while ((entry = ldap_next_entry(conn->handle, entry)));
-
-finish:
-       talloc_free(attrs);
-       if (dn) ldap_memfree(dn);
-       if (result) ldap_msgfree(result);
-
-       mod_conn_release(inst, NULL, conn);
-
-       return ret;
-}
-