USES_APPLE_DEPRECATED_API
#include <freeradius-devel/util/debug.h>
+#include <freeradius-devel/util/uri.h>
#include "rlm_ldap.h"
#include <freeradius-devel/server/map_proc.h>
+
+/*
+ * Xlat pointer to thread handling the query
+ */
+typedef struct {
+ fr_ldap_thread_t *t;
+} ldap_xlat_thread_inst_t;
+
static CONF_PARSER sasl_mech_dynamic[] = {
{ FR_CONF_OFFSET("mech", FR_TYPE_TMPL | FR_TYPE_NOT_EMPTY, fr_ldap_sasl_t_dynamic_t, mech) },
{ FR_CONF_OFFSET("proxy", FR_TYPE_TMPL, fr_ldap_sasl_t_dynamic_t, proxy) },
return XLAT_ACTION_DONE;
}
-/** Expand an LDAP URL into a query, and return a string result from that query.
+/** Escape function for a part of an LDAP URI
*
- * @ingroup xlat_functions
*/
-static ssize_t ldap_xlat(UNUSED TALLOC_CTX *ctx, char **out, size_t outlen,
- void const *mod_inst, UNUSED void const *xlat_inst,
- request_t *request, char const *fmt)
+static int uri_part_escape(fr_value_box_t *vb, UNUSED void *uctx)
{
- fr_ldap_rcode_t status;
- size_t len = 0;
- rlm_ldap_t const *inst = mod_inst;
-
- LDAPURLDesc *ldap_url;
- LDAPMessage *result = NULL;
- LDAPMessage *entry = NULL;
+ fr_sbuff_t sbuff;
+ fr_sbuff_uctx_talloc_t sbuff_ctx;
+ size_t len;
- struct berval **values;
+ /*
+ * Maximum space needed for output would be 3 times the input if every
+ * char needed escaping
+ */
+ if (!fr_sbuff_init_talloc(vb, &sbuff, &sbuff_ctx, vb->length * 3, vb->length * 3)) {
+ fr_strerror_printf_push("Failed to allocate buffer for escaped argument");
+ return -1;
+ }
- fr_ldap_connection_t *conn;
- int ldap_errno;
+ /*
+ * Call the escape function, including the space for the trailing NULL
+ */
+ len = fr_ldap_escape_func(NULL, fr_sbuff_buff(&sbuff), vb->length * 3 + 1, vb->vb_strvalue, NULL);
- char const *url;
- char const **attrs;
+ fr_sbuff_trim_talloc(&sbuff, len);
+ fr_value_box_clear_value(vb);
+ fr_value_box_strdup_shallow(vb, NULL, fr_sbuff_buff(&sbuff), vb->tainted);
- LDAPControl *server_ctrls[] = { NULL, NULL };
+ return 0;
+}
- url = fmt;
+/** Callback when LDAP query times out
+ *
+ */
+static void ldap_query_timeout(UNUSED fr_event_list_t *el, UNUSED fr_time_t now, void *uctx)
+{
+ fr_ldap_query_t *query = talloc_get_type_abort(uctx, fr_ldap_query_t);
+ request_t *request = query->request;
- if (!ldap_is_ldap_url(url)) {
- REDEBUG("String passed does not look like an LDAP URL");
- return -1;
+ RERROR("Timeout waiting for LDAP query");
+ if (query->msgid) {
+ fr_trunk_request_signal_cancel(query->treq);
}
- if (ldap_url_parse(url, &ldap_url)){
- REDEBUG("Parsing LDAP URL failed");
- return -1;
- }
+ query->ret = LDAP_RESULT_TIMEOUT;
+ unlang_interpret_mark_runnable(request);
+}
+
+/** Callback when resuming after async ldap query is completed
+ *
+ */
+static xlat_action_t ldap_xlat_resume(TALLOC_CTX *ctx, fr_dcursor_t *out, request_t *request,
+ UNUSED void const *xlat_inst, UNUSED void *xlat_thread_inst,
+ UNUSED fr_value_box_list_t *in, void *rctx)
+{
+ fr_ldap_query_t *query = talloc_get_type_abort(rctx, fr_ldap_query_t);
+ fr_ldap_connection_t *ldap_conn = query->ldap_conn;
+ fr_value_box_t *vb = NULL;
+ LDAPMessage *msg;
+ struct berval **values;
+ int count, i;
+
+ if (query->ret != LDAP_RESULT_SUCCESS) return XLAT_ACTION_FAIL;
/*
- * Nothing, empty string, "*" string, or got 2 things, die.
+ * We only parse "entries"
*/
- if (!ldap_url->lud_attrs || !ldap_url->lud_attrs[0] ||
- !*ldap_url->lud_attrs[0] ||
- (strcmp(ldap_url->lud_attrs[0], "*") == 0) ||
- ldap_url->lud_attrs[1]) {
- REDEBUG("Bad attributes list in LDAP URL. URL must specify exactly one attribute to retrieve");
+ for (msg = ldap_first_entry(ldap_conn->handle, query->result); msg; msg = ldap_next_entry(ldap_conn->handle, msg)) {
- goto free_urldesc;
+ values = ldap_get_values_len(ldap_conn->handle, msg, query->ldap_url->lud_attrs[0]);
+
+ if (!values) {
+ RDEBUG2("No \"%s\" attributes found in specified object", query->ldap_url->lud_attrs[0]);
+ continue;
+ }
+
+ count = ldap_count_values_len(values);
+ for (i = 0; i < count; i++) {
+ MEM(vb = fr_value_box_alloc_null(ctx));
+ if (fr_value_box_bstrndup(ctx, vb, NULL, values[i]->bv_val, values[i]->bv_len, true) < 0) {
+ talloc_free(vb);
+ RPERROR("Failed creating value from LDAP response");
+ break;
+ }
+ fr_dcursor_append(out, vb);
+ }
+
+ ldap_value_free_len(values);
}
- conn = mod_conn_get(inst, request);
- if (!conn) goto free_urldesc;
+ talloc_free(query);
- memcpy(&attrs, &ldap_url->lud_attrs, sizeof(attrs));
+ return XLAT_ACTION_DONE;
+}
- if (fr_ldap_parse_url_extensions(&server_ctrls[0], request, conn, ldap_url->lud_exts) < 0) goto free_socket;
+/** Callback for signalling async ldap query
+ *
+ */
+static void ldap_xlat_signal(request_t *request, UNUSED void *instance, UNUSED void *thread, void *rctx, fr_state_signal_t action)
+{
+ fr_ldap_query_t *query = talloc_get_type_abort(rctx, fr_ldap_query_t);
- status = fr_ldap_search(&result, request, &conn, ldap_url->lud_dn, ldap_url->lud_scope,
- ldap_url->lud_filter, attrs, server_ctrls, NULL);
+ if (action != FR_SIGNAL_CANCEL) return;
-#ifdef HAVE_LDAP_CREATE_SORT_CONTROL
- if (server_ctrls[0]) ldap_control_free(server_ctrls[0]);
-#endif
+ RDEBUG2("Forcefully cancelling pending LDAP query");
- switch (status) {
- case LDAP_PROC_SUCCESS:
- break;
+ fr_trunk_request_signal_cancel(query->treq);
+}
- default:
- goto free_socket;
+
+static fr_uri_part_t const ldap_uri_parts[] = {
+ { .name = "scheme", .terminals = &FR_SBUFF_TERMS(L(":")), .part_adv = { [':'] = 1 },
+ .tainted_allowed = false, .extra_skip = 2 },
+ { .name = "host", .terminals = &FR_SBUFF_TERMS(L(":"), L("/")), .part_adv = { [':'] = 1, ['/'] = 2 },
+ .tainted_allowed = false },
+ { .name = "port", .terminals = &FR_SBUFF_TERMS(L("/")), .part_adv = { ['/'] = 1 },
+ .tainted_allowed = false },
+ { .name = "dn", .terminals = &FR_SBUFF_TERMS(L("?")), .part_adv = { ['?'] = 1 },
+ .tainted_allowed = true, .func = uri_part_escape },
+ { .name = "attrs", .terminals = &FR_SBUFF_TERMS(L("?")), .part_adv = { ['?'] = 1 },
+ .tainted_allowed = false },
+ { .name = "scope", .terminals = &FR_SBUFF_TERMS(L("?")), .part_adv = { ['?'] = 1 },
+ .tainted_allowed = true, .func = uri_part_escape },
+ { .name = "filter", .terminals = &FR_SBUFF_TERMS(L("?")), .part_adv = { ['?'] = 1},
+ .tainted_allowed = true, .func = uri_part_escape },
+ { .name = "exts", .tainted_allowed = true, .func = uri_part_escape },
+ XLAT_URI_PART_TERMINATOR
+};
+
+static xlat_arg_parser_t const ldap_xlat_arg = { .required = true, .type = FR_TYPE_STRING };
+
+/** Expand an LDAP URL into a query, and return a string result from that query.
+ *
+ * @ingroup xlat_functions
+ */
+static xlat_action_t ldap_xlat(UNUSED TALLOC_CTX *ctx, UNUSED fr_dcursor_t *out, request_t *request,
+ UNUSED void const *xlat_inst, void *xlat_thread_inst,
+ fr_value_box_list_t *in)
+{
+ fr_value_box_t *in_vb = NULL;
+ ldap_xlat_thread_inst_t *xt = talloc_get_type_abort(xlat_thread_inst, ldap_xlat_thread_inst_t);
+ char *host_url;
+ fr_ldap_config_t const *handle_config = &xt->t->inst->handle_config;
+
+ fr_ldap_query_t *query = NULL;
+
+ LDAPURLDesc *ldap_url;
+
+ if (fr_uri_escape(in, ldap_uri_parts, NULL) < 0) return XLAT_ACTION_FAIL;
+
+ in_vb = fr_dlist_head(in);
+ if(fr_value_box_list_concat_in_place(in_vb, in_vb, in, FR_TYPE_STRING, FR_VALUE_BOX_LIST_FREE,
+ true, SIZE_MAX) < 0) {
+ REDEBUG("Failed concattenating input");
+ return XLAT_ACTION_FAIL;
}
- fr_assert(conn);
- fr_assert(result);
+ if (!ldap_is_ldap_url(in_vb->vb_strvalue)) {
+ REDEBUG("String passed does not look like an LDAP URL");
+ return XLAT_ACTION_FAIL;
+ }
- entry = ldap_first_entry(conn->handle, result);
- if (!entry) {
- ldap_get_option(conn->handle, LDAP_OPT_RESULT_CODE, &ldap_errno);
- REDEBUG("Failed retrieving entry: %s", ldap_err2string(ldap_errno));
- len = -1;
- goto free_result;
+ query = fr_ldap_query_alloc(unlang_interpret_frame_talloc_ctx(request));
+
+ if (ldap_url_parse(in_vb->vb_strvalue, &query->ldap_url)){
+ REDEBUG("Parsing LDAP URL failed");
+ free_query:
+ talloc_free(query);
+ return XLAT_ACTION_FAIL;
}
- values = ldap_get_values_len(conn->handle, entry, ldap_url->lud_attrs[0]);
- if (!values) {
- RDEBUG2("No \"%s\" attributes found in specified object", ldap_url->lud_attrs[0]);
- goto free_result;
+ ldap_url = query->ldap_url;
+
+ /*
+ * Nothing, empty string, "*" string, or got 2 things, die.
+ */
+ if (!ldap_url->lud_attrs || !ldap_url->lud_attrs[0] || !*ldap_url->lud_attrs[0] ||
+ (strcmp(ldap_url->lud_attrs[0], "*") == 0) || ldap_url->lud_attrs[1]) {
+ REDEBUG("Bad attributes list in LDAP URL. URL must specify exactly one attribute to retrieve");
+
+ goto free_query;
+ }
+
+ /*
+ * If the URL is <scheme>:/// the parsed host will be NULL - use config default
+ */
+ if (!ldap_url->lud_host) {
+ host_url = handle_config->server;
+ } else {
+ host_url = talloc_asprintf(query, "%s://%s:%d", ldap_url->lud_scheme,
+ ldap_url->lud_host, ldap_url->lud_port);
}
- if (values[0]->bv_len >= outlen) goto free_values;
+ query->ttrunk = fr_thread_ldap_trunk_get(xt->t, host_url, handle_config->admin_identity,
+ handle_config->admin_password, request, handle_config);
+ if (!query->ttrunk) {
+ REDEBUG("Unable to get LDAP query for xlat");
+ goto free_query;
+ }
- memcpy(*out, values[0]->bv_val, values[0]->bv_len + 1); /* +1 as strlcpy expects buffer size */
- len = values[0]->bv_len;
+ query->type = LDAP_REQUEST_SEARCH;
+ query->request = request;
-free_values:
- ldap_value_free_len(values);
-free_result:
- ldap_msgfree(result);
-free_socket:
- ldap_mod_conn_release(inst, request, conn);
-free_urldesc:
- ldap_free_urldesc(ldap_url);
+ fr_trunk_request_enqueue(&query->treq, query->ttrunk->trunk, request, query, NULL);
+
+ fr_event_timer_in(query, unlang_interpret_event_list(request), &query->ev, handle_config->res_timeout,
+ ldap_query_timeout, query);
- return len;
+ return unlang_xlat_yield(request, ldap_xlat_resume, ldap_xlat_signal, query);
}
/*
return 0;
}
+static int mod_xlat_thread_instantiate(UNUSED void *xlat_inst, void *xlat_thread_inst,
+ UNUSED xlat_exp_t const *exp, void *uctx)
+{
+ rlm_ldap_t *inst = talloc_get_type_abort(uctx, rlm_ldap_t);
+ ldap_xlat_thread_inst_t *xt = xlat_thread_inst;
+
+ xt->t = talloc_get_type_abort(module_thread_by_data(inst)->data, fr_ldap_thread_t);
+
+ return 0;
+}
+
/** Initialise thread specific data structure
*
*/
this_thread->inst = inst;
this_thread->config = &inst->handle_config;
+ this_thread->trunk_conf = &inst->trunk_conf;
this_thread->el = el;
return 0;
inst->cache_da = inst->group_da; /* Default to the group_da */
}
- xlat_register_legacy(inst, inst->name, ldap_xlat, fr_ldap_escape_func, NULL, 0, XLAT_DEFAULT_BUF_LEN);
+ xlat = xlat_register(NULL, inst->name, ldap_xlat, false);
+ xlat_func_mono(xlat, &ldap_xlat_arg);
+ xlat_async_thread_instantiate_set(xlat, mod_xlat_thread_instantiate, ldap_xlat_thread_inst_t, NULL, inst);
xlat = xlat_register(NULL, "ldap_escape", ldap_escape_xlat, false);
xlat_func_mono(xlat, &ldap_escape_xlat_arg);
xlat = xlat_register(NULL, "ldap_unescape", ldap_unescape_xlat, false);