From: Nick Porter Date: Thu, 7 Oct 2021 16:26:15 +0000 (+0100) Subject: v4: More changes to support moving LDAP to use trunks (#4257) X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=847e46a7481e08eed9ed691c830e77025494942e;p=thirdparty%2Ffreeradius-server.git v4: More changes to support moving LDAP to use trunks (#4257) * 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 --- diff --git a/src/lib/ldap/base.c b/src/lib/ldap/base.c index aeaeb9017ff..1a7955660f0 100644 --- a/src/lib/ldap/base.c +++ b/src/lib/ldap/base.c @@ -35,6 +35,7 @@ USES_APPLE_DEPRECATED_API #include #include +#include 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. diff --git a/src/lib/ldap/base.h b/src/lib/ldap/base.h index 80756c14f93..906f585b29d 100644 --- a/src/lib/ldap/base.h +++ b/src/lib/ldap/base.h @@ -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. */ diff --git a/src/lib/ldap/connection.c b/src/lib/ldap/connection.c index 49eb103405b..d586b2fcf0e 100644 --- a/src/lib/ldap/connection.c +++ b/src/lib/ldap/connection.c @@ -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 index 1028116b9da..00000000000 --- a/src/modules/rlm_ldap/clients.c +++ /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 -#include - -#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; -} -