From: Nick Porter Date: Thu, 18 May 2023 16:14:57 +0000 (+0100) Subject: Change module environment to more generic call environment (#4998) X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=efae834a877828603c0ab477120d6e8aaeb61399;p=thirdparty%2Ffreeradius-server.git Change module environment to more generic call environment (#4998) * Move module_env_t to its own header and rename to call_env_t * Move and rename module_env_parsed_t * Rename module mod_env_ctx to call_env_ctx * Move and rename module_method_env_t * Move and rename module env helper macros * Move and rename module env parsing functions and make more generic * Rework expanding of call environments to make it generic * Add a call_method_env to xlat_t Along with associated registration function * Add dictionary to xlat function call for resolving module env * Parse xlat call env when allocating instance data * Add env_data to xlat frame state, xlat_ctx_t and supporting macro * Add expansion of call env to unlang_xlat_repeat * Rename mod/module _env -> call_env in rlm_ldap * Rename module / mod _env -> call_env in rlm_smtp * Rename module_env -> call_env in rlm_files --- diff --git a/src/lib/server/module.h b/src/lib/server/module.h index 3d00700dad5..06d51173d99 100644 --- a/src/lib/server/module.h +++ b/src/lib/server/module.h @@ -35,15 +35,13 @@ extern "C" { #include #include #include +#include #include typedef struct module_s module_t; typedef struct module_method_name_s module_method_name_t; typedef struct module_instance_s module_instance_t; typedef struct module_thread_instance_s module_thread_instance_t; -typedef struct module_env_s module_env_t; -typedef struct module_env_parsed_s module_env_parsed_t; -typedef struct module_method_env_s module_method_env_t; typedef struct module_list_t module_list_t; #define MODULE_TYPE_THREAD_SAFE (0 << 0) //!< Module is threadsafe. @@ -120,15 +118,6 @@ typedef int (*module_thread_detach_t)(module_thread_inst_ctx_t const *mctx); extern "C" { #endif -FR_DLIST_TYPES(mod_env_parsed) -FR_DLIST_TYPEDEFS(mod_env_parsed, mod_env_parsed_head_t, mod_env_parsed_entry_t) - -struct module_method_env_s { - size_t inst_size; //!< Size of per call module env. - char const *inst_type; //!< Type of per call module env. - module_env_t const *env; //!< Parsing rules for module method env. -}; - /** Named methods exported by a module * */ @@ -137,7 +126,7 @@ struct module_method_name_s { char const *name2; //!< The packet type i.e Access-Request, Access-Reject. module_method_t method; //!< Module method to call - module_method_env_t const *method_env; //!< Call specific conf parsing. + call_method_env_t const *method_env; //!< Call specific conf parsing. }; #define MODULE_NAME_TERMINATOR { NULL } @@ -235,153 +224,6 @@ struct module_thread_instance_s { uint64_t active_callers; //! number of active callers. i.e. number of current yields }; -typedef enum { - MOD_ENV_TYPE_VALUE_BOX = 1, - MOD_ENV_TYPE_VALUE_BOX_LIST -} mod_env_dest_t; - -/** Per method module config - * - * Similar to a CONF_PARSER used to hold details of conf pairs - * which are evaluated per call for each module method. - * - * This allows the conf pairs to be evaluated within the appropriate context - * and use the appropriate dictionaries for where the module is in use. - */ -struct module_env_s { - char const *name; //!< Of conf pair to pass to tmpl_tokenizer. - char const *dflt; //!< Default string to pass to the tmpl_tokenizer if no CONF_PAIR found. - fr_token_t dflt_quote; //!< Default quoting for the default string. - - uint32_t type; //!< To cast boxes to. Also contains flags controlling parser behaviour. - - size_t offset; //!< Where to write results in the output structure when the tmpls are evaluated. - - union { - struct { - bool required; //!< Tmpl must produce output - bool concat; //!< If the tmpl produced multiple boxes they should be concatenated. - bool single; //!< If the tmpl produces more than one box this is an error. - bool multi; //!< Multiple instances of the conf pairs are allowed. Resulting - ///< boxes are stored in an array - one entry per conf pair. - bool nullable; //!< Tmpl expansions are allowed to produce no output. - mod_env_dest_t type; //!< Type of structure boxes will be written to. - size_t size; //!< Size of structure boxes will be written to. - char const *type_name; //!< Name of structure type boxes will be written to. - size_t tmpl_offset; //!< Where to write pointer to tmpl in the output structure. Optional. - } pair; - - struct { - char const *ident2; //!< Second identifier for a section - module_env_t const *subcs; //!< Nested definitions for subsection. - } section; - }; -}; - -#define MODULE_ENV_TERMINATOR { NULL } - -struct module_env_parsed_s { - mod_env_parsed_entry_t entry; //!< Entry in list of parsed module_env. - tmpl_t *tmpl; //!< Tmpl produced from parsing conf pair. - size_t opt_count; //!< Number of instances found of this option. - size_t multi_index; //!< Array index for this instance. - module_env_t const *rule; //!< Used to produce this. -}; - -FR_DLIST_FUNCS(mod_env_parsed, module_env_parsed_t, entry) - -/** Derive whether tmpl can only emit a single box. - */ -#define FR_MODULE_ENV_SINGLE(_s, _f, _c) \ -_Generic((((_s *)NULL)->_f), \ - fr_value_box_t : __builtin_choose_expr(_c, false, true), \ - fr_value_box_t * : __builtin_choose_expr(_c, false, true), \ - fr_value_box_list_t : false, \ - fr_value_box_list_t * : false \ -) - -/** Derive whether multi conf pairs are allowed from target field type. - */ -#define FR_MODULE_ENV_MULTI(_s, _f) \ -_Generic((((_s *)NULL)->_f), \ - fr_value_box_t : false, \ - fr_value_box_t * : true, \ - fr_value_box_list_t : false, \ - fr_value_box_list_t * : true \ -) - -/** Only FR_TYPE_STRING and FR_TYPE_OCTETS can be concatenated. - */ -#define FR_MODULE_ENV_CONCAT(_c, _ct) \ -__builtin_choose_expr(FR_BASE_TYPE(_ct) == FR_TYPE_STRING, _c, \ -__builtin_choose_expr(FR_BASE_TYPE(_ct) == FR_TYPE_OCTETS, _c, \ -__builtin_choose_expr(_c, (void)0, false))) - -/** Mapping from field types to destination type enum - */ -#define FR_MODULE_ENV_DST_TYPE(_s, _f) \ -_Generic((((_s *)NULL)->_f), \ - fr_value_box_t : MOD_ENV_TYPE_VALUE_BOX, \ - fr_value_box_t * : MOD_ENV_TYPE_VALUE_BOX, \ - fr_value_box_list_t : MOD_ENV_TYPE_VALUE_BOX_LIST, \ - fr_value_box_list_t * : MOD_ENV_TYPE_VALUE_BOX_LIST \ -) - -#define FR_MODULE_ENV_DST_SIZE(_s, _f) \ -_Generic((((_s *)NULL)->_f), \ - fr_value_box_t : sizeof(fr_value_box_t), \ - fr_value_box_t * : sizeof(fr_value_box_t), \ - fr_value_box_list_t : sizeof(fr_value_box_list_t), \ - fr_value_box_list_t * : sizeof(fr_value_box_list_t) \ -) - -#define FR_MODULE_ENV_DST_TYPE_NAME(_s, _f) \ -_Generic((((_s *)NULL)->_f), \ - fr_value_box_t : "fr_value_box_t", \ - fr_value_box_t * : "fr_value_box_t", \ - fr_value_box_list_t : "fr_value_box_list_t", \ - fr_value_box_list_t * : "fr_value_box_list_t" \ -) - -#define FR_MODULE_ENV_OFFSET(_name, _cast_type, _struct, _field, _dflt, _dflt_quote, _required, _nullable, _concat) \ - .name = _name, \ - .type = _cast_type, \ - .offset = offsetof(_struct, _field), \ - .dflt = _dflt, \ - .dflt_quote = _dflt_quote, \ - .pair = { .required = _required, \ - .concat = FR_MODULE_ENV_CONCAT(_concat, _cast_type), \ - .single = FR_MODULE_ENV_SINGLE(_struct, _field, _concat), \ - .multi = FR_MODULE_ENV_MULTI(_struct, _field), \ - .nullable = _nullable, \ - .type = FR_MODULE_ENV_DST_TYPE(_struct, _field), \ - .size = FR_MODULE_ENV_DST_SIZE(_struct, _field), \ - .type_name = FR_MODULE_ENV_DST_TYPE_NAME(_struct, _field) } - -/** Version of the above which sets optional field for pointer to tmpl - */ -#define FR_MODULE_ENV_TMPL_OFFSET(_name, _cast_type, _struct, _field, _tmpl_field, _dflt, _dflt_quote, _required, _nullable, _concat) \ - .name = _name, \ - .type = _cast_type, \ - .offset = offsetof(_struct, _field), \ - .dflt = _dflt, \ - .dflt_quote = _dflt_quote, \ - .pair = { .required = _required, \ - .concat = FR_MODULE_ENV_CONCAT(_concat, _cast_type), \ - .single = FR_MODULE_ENV_SINGLE(_struct, _field, _concat), \ - .multi = FR_MODULE_ENV_MULTI(_struct, _field), \ - .nullable = _nullable, \ - .type = FR_MODULE_ENV_DST_TYPE(_struct, _field), \ - .size = FR_MODULE_ENV_DST_SIZE(_struct, _field), \ - .type_name = FR_MODULE_ENV_DST_TYPE_NAME(_struct, _field), \ - .tmpl_offset = offsetof(_struct, _tmpl_field) } - -#define FR_MODULE_ENV_SUBSECTION(_name, _ident2, _subcs ) \ - .name = _name, \ - .type = FR_TYPE_SUBSECTION, \ - .section = { .ident2 = _ident2, \ - .subcs = _subcs } - /** A list of modules * * This allows modules to be instantiated and freed in phases, diff --git a/src/lib/server/module_rlm.c b/src/lib/server/module_rlm.c index 7fd80c073a7..4992d72c96d 100644 --- a/src/lib/server/module_rlm.c +++ b/src/lib/server/module_rlm.c @@ -431,7 +431,7 @@ bool module_rlm_section_type_set(request_t *request, fr_dict_attr_t const *type_ * * If the module exists but the method doesn't exist, then `method` is set to NULL. */ -module_instance_t *module_rlm_by_name_and_method(module_method_t *method, module_method_env_t const **method_env, +module_instance_t *module_rlm_by_name_and_method(module_method_t *method, call_method_env_t const **method_env, UNUSED rlm_components_t *component, char const **name1, char const **name2, char const *name) diff --git a/src/lib/server/module_rlm.h b/src/lib/server/module_rlm.h index e01450c8410..4eb10f38aee 100644 --- a/src/lib/server/module_rlm.h +++ b/src/lib/server/module_rlm.h @@ -84,7 +84,7 @@ bool module_rlm_section_type_set(request_t *request, fr_dict_attr_t const *type * * @{ */ -module_instance_t *module_rlm_by_name_and_method(module_method_t *method, module_method_env_t const ** method_env, +module_instance_t *module_rlm_by_name_and_method(module_method_t *method, call_method_env_t const ** method_env, rlm_components_t *component, char const **name1, char const **name2, char const *asked_name); diff --git a/src/lib/unlang/all.mk b/src/lib/unlang/all.mk index 460c3dc32ff..821a6023c0a 100644 --- a/src/lib/unlang/all.mk +++ b/src/lib/unlang/all.mk @@ -2,6 +2,7 @@ TARGET := libfreeradius-unlang$(L) SOURCES := base.c \ call.c \ + call_env.c \ caller.c \ compile.c \ condition.c \ diff --git a/src/lib/unlang/call_env.c b/src/lib/unlang/call_env.c new file mode 100644 index 00000000000..54615449807 --- /dev/null +++ b/src/lib/unlang/call_env.c @@ -0,0 +1,345 @@ +/* + * This program 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 unlang/call_env.c + * @brief Call environment parsing functions + * + * @copyright 2023 Network RADIUS SAS (legal@networkradius.com) + */ +RCSID("$Id$") + +#include +#include +#include +#include +#include "call_env.h" + +/** Parse per call env + * + * Used for config options which must be parsed in the context in which + * the module is being called. + * + * @param[in] ctx To allocate parsed environment in. + * @param[out] parsed Where to write parsed environment. + * @param[in] name Module name for error messages. + * @param[in] dict_def Default dictionary to use when tokenizing tmpls. + * @param[in] cs Module config. + * @param[in] call_env to parse. + * @return + * - 0 on success; + * - <0 on failure; + */ +int call_env_parse(TALLOC_CTX *ctx, call_env_parsed_head_t *parsed, char const *name, fr_dict_t const *dict_def, + CONF_SECTION const *cs, call_env_t const *call_env) { + CONF_PAIR const *cp, *next; + call_env_parsed_t *call_env_parsed; + ssize_t len, opt_count, multi_index; + char const *value; + fr_token_t quote; + fr_type_t type; + + while (call_env->name) { + if (FR_BASE_TYPE(call_env->type) == FR_TYPE_SUBSECTION) { + CONF_SECTION const *subcs; + subcs = cf_section_find(cs, call_env->name, call_env->section.ident2); + if (!subcs) goto next; + + if (call_env_parse(ctx, parsed, name, dict_def, subcs, call_env->section.subcs) < 0) return -1; + goto next; + } + + cp = cf_pair_find(cs, call_env->name); + + if (!cp && !call_env->dflt) { + if (!call_env->pair.required) goto next; + + cf_log_err(cs, "Module %s missing required option %s", name, call_env->name); + return -1; + } + + /* + * Check for additional conf pairs and error + * if there is one and multi is not allowed. + */ + if (!call_env->pair.multi && ((next = cf_pair_find_next(cs, cp, call_env->name)))) { + cf_log_err(cf_pair_to_item(next), "Invalid duplicate configuration item '%s'", call_env->name); + return -1; + } + + opt_count = cf_pair_count(cs, call_env->name); + if (opt_count == 0) opt_count = 1; + + for (multi_index = 0; multi_index < opt_count; multi_index ++) { + MEM(call_env_parsed = talloc_zero(ctx, call_env_parsed_t)); + call_env_parsed->rule = call_env; + call_env_parsed->opt_count = opt_count; + call_env_parsed->multi_index = multi_index; + + if (cp) { + value = cf_pair_value(cp); + len = talloc_array_length(value) - 1; + quote = cf_pair_value_quote(cp); + } else { + value = call_env->dflt; + len = strlen(value); + quote = call_env->dflt_quote; + } + + type = FR_BASE_TYPE(call_env->type); + if (tmpl_afrom_substr(call_env_parsed, &call_env_parsed->tmpl, &FR_SBUFF_IN(value, len), + quote, NULL, &(tmpl_rules_t){ + .cast = (type == FR_TYPE_VOID ? FR_TYPE_NULL : type), + .attr = { + .list_def = request_attr_request, + .dict_def = dict_def + } + }) < 0) { + error: + talloc_free(call_env_parsed); + cf_log_perr(cp, "Failed to parse '%s' for %s", cf_pair_value(cp), call_env->name); + return -1; + } + + /* + * Ensure only valid TMPL types are produced. + */ + switch (call_env_parsed->tmpl->type) { + case TMPL_TYPE_ATTR: + case TMPL_TYPE_DATA: + case TMPL_TYPE_EXEC: + case TMPL_TYPE_XLAT: + break; + + default: + cf_log_err(cp, "'%s' expands to invalid tmpl type %s", value, + fr_table_str_by_value(tmpl_type_table, call_env_parsed->tmpl->type, "")); + goto error; + } + + call_env_parsed_insert_tail(parsed, call_env_parsed); + + cp = cf_pair_find_next(cs, cp, call_env->name); + } + next: + call_env++; + } + + return 0; +} + +/** Perform a quick assessment of how many parsed call env will be produced. + * + * @param[in,out] vallen Where to write the sum of the length of pair values. + * @param[in] cs Conf section to search for pairs. + * @param[in] call_env to parse. + * @return Number of parsed_call_env expected to be required. + */ +size_t call_env_count(size_t *vallen, CONF_SECTION const *cs, call_env_t const *call_env) { + size_t pair_count, tmpl_count = 0; + CONF_PAIR const *cp; + + while (call_env->name) { + if (FR_BASE_TYPE(call_env->type) == FR_TYPE_SUBSECTION) { + CONF_SECTION const *subcs; + subcs = cf_section_find(cs, call_env->name, call_env->section.ident2); + if (!subcs) goto next; + + tmpl_count += call_env_count(vallen, subcs, call_env->section.subcs); + goto next; + } + pair_count = 0; + cp = NULL; + while ((cp = cf_pair_find_next(cs, cp, call_env->name))) { + pair_count++; + *vallen += talloc_array_length(cf_pair_value(cp)); + } + if (!pair_count && call_env->dflt) { + pair_count = 1; + *vallen += strlen(call_env->dflt); + } + tmpl_count += pair_count; + next: + call_env++; + } + + return tmpl_count; +} + +/** Parse the result of call_env tmpl expansion + */ +static inline CC_HINT(always_inline) int call_env_value_parse(TALLOC_CTX *ctx, request_t *request, void *out, + void **tmpl_out, call_env_parsed_t const *env, + fr_value_box_list_t *tmpl_expanded) +{ + fr_value_box_t *vb = fr_value_box_list_head(tmpl_expanded); + + if (!vb) { + if (!env->rule->pair.nullable) { + RPEDEBUG("Failed to evaluate required module option %s", env->rule->name); + return -1; + } + return 0; + } + + /* + * Concatenate multiple boxes if needed + */ + if (env->rule->pair.concat && + fr_value_box_list_concat_in_place(vb, vb, tmpl_expanded, FR_BASE_TYPE(env->rule->type), + FR_VALUE_BOX_LIST_FREE, true, SIZE_MAX) < 0 ) { + RPEDEBUG("Failed concatenating values for %s", env->rule->name); + return -1; + } + + if (env->rule->pair.single && (fr_value_box_list_num_elements(tmpl_expanded) > 1)) { + RPEDEBUG("%d values found for %s. Only one is allowed", + fr_value_box_list_num_elements(tmpl_expanded), env->rule->name); + return -1; + } + + while ((vb = fr_value_box_list_pop_head(tmpl_expanded))) { + switch (env->rule->pair.type) { + case CALL_ENV_TYPE_VALUE_BOX: + fr_value_box_copy_shallow(ctx, (fr_value_box_t *)(out), vb); + break; + + case CALL_ENV_TYPE_VALUE_BOX_LIST: + if (!fr_value_box_list_initialised((fr_value_box_list_t *)out)) fr_value_box_list_init((fr_value_box_list_t *)out); + fr_value_box_list_insert_tail((fr_value_box_list_t *)out, vb); + break; + } + } + + if (tmpl_out) *tmpl_out = env->tmpl; + + return 0; +} + +/** Context to keep track of expansion of call environments + * + */ +typedef struct { + call_env_parsed_head_t const *call_env_parsed; //!< Head of the parsed list of tmpls to expand. + call_env_parsed_t const *last_expanded; //!< The last expanded tmpl. + fr_value_box_list_t tmpl_expanded; //!< List to write value boxes to as tmpls are expanded. + void **env_data; //!< Final destination structure for value boxes. +} call_env_ctx_t; + +/** Start the expansion of a call environment tmpl. + * + */ +static unlang_action_t call_env_expand_start(UNUSED rlm_rcode_t *p_result, UNUSED int *priority, request_t *request, void *uctx) +{ + call_env_ctx_t *call_env_ctx = talloc_get_type_abort(uctx, call_env_ctx_t); + TALLOC_CTX *ctx; + call_env_parsed_t const *env; + void *out; + + call_env_ctx->last_expanded = call_env_parsed_next(call_env_ctx->call_env_parsed, call_env_ctx->last_expanded); + if (!call_env_ctx->last_expanded) return UNLANG_ACTION_CALCULATE_RESULT; + + ctx = *call_env_ctx->env_data; + env = call_env_ctx->last_expanded; + + /* + * Multi pair options should allocate boxes in the context of the array + */ + if (env->rule->pair.multi) { + out = ((uint8_t *)(*call_env_ctx->env_data)) + env->rule->offset; + + /* + * For multi pair options, allocate the array before expanding the first entry. + */ + if (env->multi_index == 0) { + void *array; + MEM(array = _talloc_zero_array((*call_env_ctx->env_data), env->rule->pair.size, + env->opt_count, env->rule->pair.type_name)); + *(void **)out = array; + } + ctx = *(void **)out; + } + + if (unlang_tmpl_push(ctx, &call_env_ctx->tmpl_expanded, request, call_env_ctx->last_expanded->tmpl, + NULL) < 0) return UNLANG_ACTION_FAIL; + + return UNLANG_ACTION_PUSHED_CHILD; +} + +/** Extract expanded call environment tmpl and store in env_data + * + * If there are more tmpls to expand, push the next expansion. + */ +static unlang_action_t call_env_expand_repeat(UNUSED rlm_rcode_t *p_result, UNUSED int *priority, + request_t *request, void *uctx) +{ + void *out, *tmpl_out = NULL; + call_env_ctx_t *call_env_ctx = talloc_get_type_abort(uctx, call_env_ctx_t); + call_env_parsed_t const *env; + + env = call_env_ctx->last_expanded; + if (!env) return UNLANG_ACTION_CALCULATE_RESULT; + + /* + * Find the location of the output + */ + out = ((uint8_t*)(*call_env_ctx->env_data)) + env->rule->offset; + + /* + * If this is a multi pair option, the output is an array. + * Find the correct offset in the array + */ + if (env->rule->pair.multi) { + void *array = *(void **)out; + out = ((uint8_t *)array) + env->rule->pair.size * env->multi_index; + } + + if (env->rule->pair.tmpl_offset) tmpl_out = ((uint8_t *)call_env_ctx->env_data) + env->rule->pair.tmpl_offset; + + if (call_env_value_parse(*call_env_ctx->env_data, request, out, tmpl_out, env, + &call_env_ctx->tmpl_expanded) < 0) return UNLANG_ACTION_FAIL; + + if (!call_env_parsed_next(call_env_ctx->call_env_parsed, env)) return UNLANG_ACTION_CALCULATE_RESULT; + + return unlang_function_push(request, call_env_expand_start, call_env_expand_repeat, NULL, 0, UNLANG_SUB_FRAME, + call_env_ctx); +} + +/** Initialise the expansion of a call environment + * + * @param[in] ctx in which to allocate destination structure for resulting value boxes. + * @param[in] request Current request. + * @param[in,out] env_data Where the destination structure should be created. + * @param[in] call_env Call environment being expanded. + * @param[in] call_env_parsed Parsed tmpls for the call environment. + */ +unlang_action_t call_env_expand(TALLOC_CTX *ctx, request_t *request, void **env_data, call_method_env_t const *call_env, + call_env_parsed_head_t const *call_env_parsed) +{ + call_env_ctx_t *call_env_ctx; + + MEM(call_env_ctx = talloc_zero(ctx, call_env_ctx_t)); + MEM(*env_data = talloc_zero_array(ctx, uint8_t, call_env->inst_size)); + talloc_set_name_const(*env_data, call_env->inst_type); + call_env_ctx->env_data = env_data; + call_env_ctx->call_env_parsed = call_env_parsed; + fr_value_box_list_init(&call_env_ctx->tmpl_expanded); + + return unlang_function_push(request, call_env_expand_start, call_env_expand_repeat, NULL, 0, UNLANG_SUB_FRAME, + call_env_ctx); +} diff --git a/src/lib/unlang/call_env.h b/src/lib/unlang/call_env.h new file mode 100644 index 00000000000..1ca2acdac2c --- /dev/null +++ b/src/lib/unlang/call_env.h @@ -0,0 +1,205 @@ +#pragma once +/* + * This program 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, 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 Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** + * $Id$ + * + * @file unlang/call_env.h + * @brief Structures and functions for handling call environments. + * + * @copyright 2023 Network RADIUS SAS (legal@networkradius.com) + */ +RCSIDH(call_env_h, "$Id$") + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +typedef struct call_env_s call_env_t; +typedef struct call_env_parsed_s call_env_parsed_t; +typedef struct call_method_env_s call_method_env_t; + +FR_DLIST_TYPES(call_env_parsed) +FR_DLIST_TYPEDEFS(call_env_parsed, call_env_parsed_head_t, call_env_parsed_entry_t) + +typedef enum { + CALL_ENV_TYPE_VALUE_BOX = 1, + CALL_ENV_TYPE_VALUE_BOX_LIST +} call_env_dest_t; + +/** Per method call config + * + * Similar to a CONF_PARSER used to hold details of conf pairs + * which are evaluated per call for each module method / xlat. + * + * This allows the conf pairs to be evaluated within the appropriate context + * and use the appropriate dictionaries for where the module is in use. + */ +struct call_env_s { + char const *name; //!< Of conf pair to pass to tmpl_tokenizer. + char const *dflt; //!< Default string to pass to the tmpl_tokenizer if no CONF_PAIR found. + fr_token_t dflt_quote; //!< Default quoting for the default string. + + uint32_t type; //!< To cast boxes to. Also contains flags controlling parser behaviour. + + size_t offset; //!< Where to write results in the output structure when the tmpls are evaluated. + + union { + struct { + bool required; //!< Tmpl must produce output + bool concat; //!< If the tmpl produced multiple boxes they should be concatenated. + bool single; //!< If the tmpl produces more than one box this is an error. + bool multi; //!< Multiple instances of the conf pairs are allowed. Resulting + ///< boxes are stored in an array - one entry per conf pair. + bool nullable; //!< Tmpl expansions are allowed to produce no output. + call_env_dest_t type; //!< Type of structure boxes will be written to. + size_t size; //!< Size of structure boxes will be written to. + char const *type_name; //!< Name of structure type boxes will be written to. + size_t tmpl_offset; //!< Where to write pointer to tmpl in the output structure. Optional. + } pair; + + struct { + char const *ident2; //!< Second identifier for a section + call_env_t const *subcs; //!< Nested definitions for subsection. + } section; + }; +}; + +#define CALL_ENV_TERMINATOR { NULL } + +struct call_env_parsed_s { + call_env_parsed_entry_t entry; //!< Entry in list of parsed call_env. + tmpl_t *tmpl; //!< Tmpl produced from parsing conf pair. + size_t opt_count; //!< Number of instances found of this option. + size_t multi_index; //!< Array index for this instance. + call_env_t const *rule; //!< Used to produce this. +}; + +FR_DLIST_FUNCS(call_env_parsed, call_env_parsed_t, entry) + +struct call_method_env_s { + size_t inst_size; //!< Size of per call env. + char const *inst_type; //!< Type of per call env. + call_env_t const *env; //!< Parsing rules for call method env. +}; + +/** Derive whether tmpl can only emit a single box. + */ +#define FR_CALL_ENV_SINGLE(_s, _f, _c) \ +_Generic((((_s *)NULL)->_f), \ + fr_value_box_t : __builtin_choose_expr(_c, false, true), \ + fr_value_box_t * : __builtin_choose_expr(_c, false, true), \ + fr_value_box_list_t : false, \ + fr_value_box_list_t * : false \ +) + +/** Derive whether multi conf pairs are allowed from target field type. + */ +#define FR_CALL_ENV_MULTI(_s, _f) \ +_Generic((((_s *)NULL)->_f), \ + fr_value_box_t : false, \ + fr_value_box_t * : true, \ + fr_value_box_list_t : false, \ + fr_value_box_list_t * : true \ +) + +/** Only FR_TYPE_STRING and FR_TYPE_OCTETS can be concatenated. + */ +#define FR_CALL_ENV_CONCAT(_c, _ct) \ +__builtin_choose_expr(FR_BASE_TYPE(_ct) == FR_TYPE_STRING, _c, \ +__builtin_choose_expr(FR_BASE_TYPE(_ct) == FR_TYPE_OCTETS, _c, \ +__builtin_choose_expr(_c, (void)0, false))) + +/** Mapping from field types to destination type enum + */ +#define FR_CALL_ENV_DST_TYPE(_s, _f) \ +_Generic((((_s *)NULL)->_f), \ + fr_value_box_t : CALL_ENV_TYPE_VALUE_BOX, \ + fr_value_box_t * : CALL_ENV_TYPE_VALUE_BOX, \ + fr_value_box_list_t : CALL_ENV_TYPE_VALUE_BOX_LIST, \ + fr_value_box_list_t * : CALL_ENV_TYPE_VALUE_BOX_LIST \ +) + +#define FR_CALL_ENV_DST_SIZE(_s, _f) \ +_Generic((((_s *)NULL)->_f), \ + fr_value_box_t : sizeof(fr_value_box_t), \ + fr_value_box_t * : sizeof(fr_value_box_t), \ + fr_value_box_list_t : sizeof(fr_value_box_list_t), \ + fr_value_box_list_t * : sizeof(fr_value_box_list_t) \ +) + +#define FR_CALL_ENV_DST_TYPE_NAME(_s, _f) \ +_Generic((((_s *)NULL)->_f), \ + fr_value_box_t : "fr_value_box_t", \ + fr_value_box_t * : "fr_value_box_t", \ + fr_value_box_list_t : "fr_value_box_list_t", \ + fr_value_box_list_t * : "fr_value_box_list_t" \ +) + +#define FR_CALL_ENV_OFFSET(_name, _cast_type, _struct, _field, _dflt, _dflt_quote, _required, _nullable, _concat) \ + .name = _name, \ + .type = _cast_type, \ + .offset = offsetof(_struct, _field), \ + .dflt = _dflt, \ + .dflt_quote = _dflt_quote, \ + .pair = { .required = _required, \ + .concat = FR_CALL_ENV_CONCAT(_concat, _cast_type), \ + .single = FR_CALL_ENV_SINGLE(_struct, _field, _concat), \ + .multi = FR_CALL_ENV_MULTI(_struct, _field), \ + .nullable = _nullable, \ + .type = FR_CALL_ENV_DST_TYPE(_struct, _field), \ + .size = FR_CALL_ENV_DST_SIZE(_struct, _field), \ + .type_name = FR_CALL_ENV_DST_TYPE_NAME(_struct, _field) } + +/** Version of the above which sets optional field for pointer to tmpl + */ +#define FR_CALL_ENV_TMPL_OFFSET(_name, _cast_type, _struct, _field, _tmpl_field, _dflt, _dflt_quote, _required, _nullable, _concat) \ + .name = _name, \ + .type = _cast_type, \ + .offset = offsetof(_struct, _field), \ + .dflt = _dflt, \ + .dflt_quote = _dflt_quote, \ + .pair = { .required = _required, \ + .concat = FR_CALL_ENV_CONCAT(_concat, _cast_type), \ + .single = FR_CALL_ENV_SINGLE(_struct, _field, _concat), \ + .multi = FR_CALL_ENV_MULTI(_struct, _field), \ + .nullable = _nullable, \ + .type = FR_CALL_ENV_DST_TYPE(_struct, _field), \ + .size = FR_CALL_ENV_DST_SIZE(_struct, _field), \ + .type_name = FR_CALL_ENV_DST_TYPE_NAME(_struct, _field), \ + .tmpl_offset = offsetof(_struct, _tmpl_field) } + +#define FR_CALL_ENV_SUBSECTION(_name, _ident2, _subcs ) \ + .name = _name, \ + .type = FR_TYPE_SUBSECTION, \ + .section = { .ident2 = _ident2, \ + .subcs = _subcs } + +int call_env_parse(TALLOC_CTX *ctx, call_env_parsed_head_t *parsed, char const *name, fr_dict_t const *dict_def, + CONF_SECTION const *cs, call_env_t const *call_env); + +size_t call_env_count(size_t *vallen, CONF_SECTION const *cs, call_env_t const *call_env); + +unlang_action_t call_env_expand(TALLOC_CTX *ctx, request_t *request, void **env_data, call_method_env_t const *call_env, + call_env_parsed_head_t const *call_env_parsed); + +#ifdef __cplusplus +} +#endif diff --git a/src/lib/unlang/compile.c b/src/lib/unlang/compile.c index fb5a4da8411..1699edc7aba 100644 --- a/src/lib/unlang/compile.c +++ b/src/lib/unlang/compile.c @@ -4655,158 +4655,9 @@ check_for_loop: return subcs; } -/** Parse per call module env - * - * Used for config options which must be parsed in the context in which - * the module is being called. - * - * @param[in] single Module call being compiled. - * @param[in] unlang_ctx Current compilation context. - * @param[in] cs Module config. - * @param[in] module_env to parse. - * @return - * - 0 on success; - * - <0 on failure; - */ -static int method_env_parse(unlang_module_t *single, unlang_compile_t *unlang_ctx, CONF_SECTION const *cs, - module_env_t const *module_env) { - CONF_PAIR const *cp, *next; - module_env_parsed_t *module_env_parsed; - ssize_t len, opt_count, multi_index; - char const *value; - fr_token_t quote; - fr_type_t type; - - while (module_env->name) { - if (FR_BASE_TYPE(module_env->type) == FR_TYPE_SUBSECTION) { - CONF_SECTION const *subcs; - subcs = cf_section_find(cs, module_env->name, module_env->section.ident2); - if (!subcs) goto next; - - if (method_env_parse(single, unlang_ctx, subcs, module_env->section.subcs) < 0) return -1; - goto next; - } - - cp = cf_pair_find(cs, module_env->name); - - if (!cp && !module_env->dflt) { - if (!module_env->pair.required) goto next; - - cf_log_err(cs, "Module %s missing required option %s", single->self.name, module_env->name); - return -1; - } - - /* - * Check for additional conf pairs and error - * if there is one and multi is not allowed. - */ - if (!module_env->pair.multi && ((next = cf_pair_find_next(cs, cp, module_env->name)))) { - cf_log_err(cf_pair_to_item(next), "Invalid duplicate configuration item '%s'", module_env->name); - return -1; - } - - opt_count = cf_pair_count(cs, module_env->name); - if (opt_count == 0) opt_count = 1; - - for (multi_index = 0; multi_index < opt_count; multi_index ++) { - MEM(module_env_parsed = talloc_zero(single->mod_env_ctx, module_env_parsed_t)); - module_env_parsed->rule = module_env; - module_env_parsed->opt_count = opt_count; - module_env_parsed->multi_index = multi_index; - - if (cp) { - value = cf_pair_value(cp); - len = talloc_array_length(value) - 1; - quote = cf_pair_value_quote(cp); - } else { - value = module_env->dflt; - len = strlen(value); - quote = module_env->dflt_quote; - } - - type = FR_BASE_TYPE(module_env->type); - if (tmpl_afrom_substr(module_env_parsed, &module_env_parsed->tmpl, &FR_SBUFF_IN(value, len), - quote, NULL, &(tmpl_rules_t){ - .cast = (type == FR_TYPE_VOID ? FR_TYPE_NULL : type), - .attr = { - .list_def = request_attr_request, - .dict_def = unlang_ctx->rules->attr.dict_def - } - }) < 0) { - error: - talloc_free(module_env_parsed); - cf_log_perr(cp, "Failed to parse '%s' for %s", cf_pair_value(cp), module_env->name); - return -1; - } - - /* - * Ensure only valid TMPL types are produced. - */ - switch (module_env_parsed->tmpl->type) { - case TMPL_TYPE_ATTR: - case TMPL_TYPE_DATA: - case TMPL_TYPE_EXEC: - case TMPL_TYPE_XLAT: - break; - - default: - cf_log_err(cp, "'%s' expands to invalid tmpl type %s", value, - fr_table_str_by_value(tmpl_type_table, module_env_parsed->tmpl->type, "")); - goto error; - } - - mod_env_parsed_insert_tail(&single->mod_env_parsed, module_env_parsed); - - cp = cf_pair_find_next(cs, cp, module_env->name); - } - next: - module_env++; - } - - return 0; -} - -/** Perform a quick assesment of how many parsed module env will be produced. - * - * @param[in,out] vallen Where to write the sum of the length of pair values. - * @param[in] cs Conf section to search for pairs. - * @param[in] module_env to parse. - * @return Number of parsed_module_env expected to be required. - */ -static size_t method_env_count(size_t *vallen, CONF_SECTION const *cs, module_env_t const *module_env) { - size_t pair_count, tmpl_count = 0; - CONF_PAIR const *cp; - - while (module_env->name) { - if (FR_BASE_TYPE(module_env->type) == FR_TYPE_SUBSECTION) { - CONF_SECTION const *subcs; - subcs = cf_section_find(cs, module_env->name, module_env->section.ident2); - if (!subcs) goto next; - - tmpl_count += method_env_count(vallen, subcs, module_env->section.subcs); - goto next; - } - pair_count = 0; - cp = NULL; - while ((cp = cf_pair_find_next(cs, cp, module_env->name))) { - pair_count++; - *vallen += talloc_array_length(cf_pair_value(cp)); - } - if (!pair_count && module_env->dflt) { - pair_count = 1; - *vallen += strlen(module_env->dflt); - } - tmpl_count += pair_count; - next: - module_env++; - } - - return tmpl_count; -} - static unlang_t *compile_module(unlang_t *parent, unlang_compile_t *unlang_ctx, CONF_ITEM *ci, module_instance_t *inst, module_method_t method, - module_method_env_t const *method_env, char const *realname) + call_method_env_t const *method_env, char const *realname) { module_rlm_t const *mrlm = module_rlm_from_module(inst->module); unlang_t *c; @@ -4869,12 +4720,13 @@ static unlang_t *compile_module(unlang_t *parent, unlang_compile_t *unlang_ctx, * The pool size is a rough estimate based on each tmpl also allocating at least two children, * for which we allow twice the length of the value to be parsed. */ - count = method_env_count(&vallen, inst->dl_inst->conf, method_env->env); - MEM(single->mod_env_ctx = _talloc_pooled_object(single, 0, "mod_env_ctx", count * 4, - (sizeof(module_env_parsed_t) + sizeof(tmpl_t)) * count + vallen * 2)); + count = call_env_count(&vallen, inst->dl_inst->conf, method_env->env); + MEM(single->call_env_ctx = _talloc_pooled_object(single, 0, "call_env_ctx", count * 4, + (sizeof(call_env_parsed_t) + sizeof(tmpl_t)) * count + vallen * 2)); - mod_env_parsed_init(&single->mod_env_parsed); - if (method_env_parse(single, unlang_ctx, inst->dl_inst->conf, method_env->env) < 0) { + call_env_parsed_init(&single->call_env_parsed); + if (call_env_parse(single->call_env_ctx, &single->call_env_parsed, single->self.name, + unlang_ctx->rules->attr.dict_def, inst->dl_inst->conf, method_env->env) < 0) { error: talloc_free(c); return NULL; @@ -4942,7 +4794,7 @@ static unlang_t *compile_item(unlang_t *parent, unlang_compile_t *unlang_ctx, CO bool policy; unlang_op_compile_t compile; unlang_t *c; - module_method_env_t const *method_env = NULL; + call_method_env_t const *method_env = NULL; if (cf_item_is_section(ci)) { cs = cf_item_to_section(ci); diff --git a/src/lib/unlang/module.c b/src/lib/unlang/module.c index f2f9f1b2ac2..4c758064308 100644 --- a/src/lib/unlang/module.c +++ b/src/lib/unlang/module.c @@ -875,57 +875,6 @@ static void unlang_module_event_retry_handler(UNUSED fr_event_list_t *el, fr_tim frame->signal(request, frame, FR_SIGNAL_TIMEOUT); } -/** Parse the result of module_env tmpl expansion - */ -static inline CC_HINT(always_inline) int module_env_value_parse(request_t *request, void *out, void **tmpl_out, - unlang_frame_state_module_t *state) { - fr_value_box_t *vb; - module_env_parsed_t const *env = state->last_expanded; - - vb = fr_value_box_list_head(&state->tmpl_expanded); - - if (!vb) { - if (!env->rule->pair.nullable) { - RPEDEBUG("Failed to evaluate required module option %s", env->rule->name); - return -1; - } - return 0; - } - - /* - * Concatenate multiple boxes if needed - */ - if (env->rule->pair.concat && - fr_value_box_list_concat_in_place(vb, vb, &state->tmpl_expanded, FR_BASE_TYPE(env->rule->type), - FR_VALUE_BOX_LIST_FREE, true, SIZE_MAX) < 0 ) { - RPEDEBUG("Failed concatenating values for %s", env->rule->name); - return -1; - } - - if (env->rule->pair.single && (fr_value_box_list_num_elements(&state->tmpl_expanded) > 1)) { - RPEDEBUG("%d values found for %s. Only one is allowed", - fr_value_box_list_num_elements(&state->tmpl_expanded), env->rule->name); - return -1; - } - - while ((vb = fr_value_box_list_pop_head(&state->tmpl_expanded))) { - switch (env->rule->pair.type) { - case MOD_ENV_TYPE_VALUE_BOX: - fr_value_box_copy_shallow(state->env_data, (fr_value_box_t *)(out), vb); - break; - - case MOD_ENV_TYPE_VALUE_BOX_LIST: - if (!fr_value_box_list_initialised((fr_value_box_list_t *)out)) fr_value_box_list_init((fr_value_box_list_t *)out); - fr_value_box_list_insert_tail((fr_value_box_list_t *)out, vb); - break; - } - } - - if (tmpl_out) *tmpl_out = env->tmpl; - - return 0; -} - static unlang_action_t unlang_module(rlm_rcode_t *p_result, request_t *request, unlang_stack_frame_t *frame) { unlang_module_t *mc; @@ -961,93 +910,21 @@ static unlang_action_t unlang_module(rlm_rcode_t *p_result, request_t *request, goto done; } - if (mc->method_env) { - void *out, **array, *tmpl_out = NULL; - module_env_parsed_t const *env; - TALLOC_CTX *ctx; - - switch (state->env_state) { - /* - * We have a method env and this is the first call. - * Allocate the data structure. - */ - case MOD_ENV_EXP_INIT: - MEM(state->env_data = talloc_zero_array(state, uint8_t, mc->method_env->inst_size)); - talloc_set_name_const(state->env_data, mc->method_env->inst_type); - fr_value_box_list_init(&state->tmpl_expanded); - state->env_state = MOD_ENV_EXP_PROC; - break; - - /* - * Processing of module env is underway, parse the - * value returned but the last expansion. - */ - case MOD_ENV_EXP_PROC: - env = state->last_expanded; - - /* - * Find the location of the output - */ - out = ((uint8_t *)state->env_data) + env->rule->offset; - - /* - * If this is a multi pair option, the output is an array. - * Find the correct offset in the array. - */ - if (env->rule->pair.multi) { - array = *(void **)out; - out = ((uint8_t *)array) + env->rule->pair.size * env->multi_index; - } + if (mc->method_env && !state->env_data) { + ua = call_env_expand(state, request, &state->env_data, mc->method_env, &mc->call_env_parsed); + switch (ua) { + case UNLANG_ACTION_FAIL: + goto fail; - if (env->rule->pair.tmpl_offset) tmpl_out = ((uint8_t *)state->env_data) + env->rule->pair.tmpl_offset; + case UNLANG_ACTION_PUSHED_CHILD: + frame_repeat(frame, unlang_module); + return UNLANG_ACTION_PUSHED_CHILD; - if (module_env_value_parse(request, out, tmpl_out, state) < 0) { - ua = UNLANG_ACTION_FAIL; - goto fail; - } + default: break; - - case MOD_ENV_EXP_DONE: - goto expansion_done; } - - /* - * Look for the next tmpl to expand - */ - state->last_expanded = mod_env_parsed_next(&mc->mod_env_parsed, state->last_expanded); - if (!state->last_expanded) { - state->env_state = MOD_ENV_EXP_DONE; - goto expansion_done; - } - env = state->last_expanded; - ctx = state->env_data; - - /* - * Multi pair options should allocate boxes in the context of the array - */ - if (env->rule->pair.multi) { - out = ((uint8_t *)state->env_data) + env->rule->offset; - - /* - * For multi pair options, allocate the array before expanding the first entry. - */ - if (env->multi_index == 0) { - MEM(array = _talloc_zero_array(state->env_data, env->rule->pair.size, - env->opt_count, env->rule->pair.type_name)); - *(void **)out = array; - } - ctx = *(void **)out; - } - - frame_repeat(frame, unlang_module); - if (unlang_tmpl_push(ctx, &state->tmpl_expanded, request, state->last_expanded->tmpl, NULL) < 0) { - ua = UNLANG_ACTION_FAIL; - goto fail; - } - return UNLANG_ACTION_PUSHED_CHILD; } -expansion_done: /* * Grab the thread/module specific data if any exists. */ diff --git a/src/lib/unlang/module_priv.h b/src/lib/unlang/module_priv.h index 1faf1f294bf..b1122159a50 100644 --- a/src/lib/unlang/module_priv.h +++ b/src/lib/unlang/module_priv.h @@ -36,21 +36,12 @@ typedef struct { unlang_t self; //!< Common fields in all #unlang_t tree nodes. module_instance_t *instance; //!< Global instance of the module we're calling. module_method_t method; //!< The entry point into the module. - module_method_env_t const *method_env; //!< Module environment for this method. - mod_env_parsed_head_t mod_env_parsed; //!< The per call parsed module environment. - TALLOC_CTX *mod_env_ctx; //!< A talloc pooled object for parsed module env + call_method_env_t const *method_env; //!< Call environment for this method. + call_env_parsed_head_t call_env_parsed; //!< The per call parsed call environment. + TALLOC_CTX *call_env_ctx; //!< A talloc pooled object for parsed call env ///< to be allocated from. } unlang_module_t; -/** What state the module env for the current call is in. - * - */ -typedef enum { - MOD_ENV_EXP_INIT = 0, //!< Expansion not yet started. - MOD_ENV_EXP_PROC, //!< Expansion in progress. - MOD_ENV_EXP_DONE //!< All expansions done. -} module_env_state_t; - /** A module stack entry * * Represents a single module call on the unlang stack. @@ -63,9 +54,6 @@ typedef struct { ///< shared between all threads, so we can't ///< cache thread-specific data in the #unlang_t. - module_env_state_t env_state; //!< State of the current call module_env. - module_env_parsed_t const *last_expanded; //!< Last environment tmpl expanded. - fr_value_box_list_t tmpl_expanded; //!< Value boxes produced by last expanded tmpl. void *env_data; //!< Expanded per call module environment tmpls. #ifndef NDEBUG diff --git a/src/lib/unlang/xlat.c b/src/lib/unlang/xlat.c index 91720793a07..ce663ca251f 100644 --- a/src/lib/unlang/xlat.c +++ b/src/lib/unlang/xlat.c @@ -45,6 +45,7 @@ typedef struct { rindent_t indent; //!< indentation + void *env_data; //!< Expanded per call environment tmpls. /* * For func and alternate */ @@ -118,7 +119,7 @@ static void unlang_xlat_event_timeout_handler(UNUSED fr_event_list_t *el, fr_tim */ ev->timeout(XLAT_CTX(ev->inst->data, ev->thread->data, - ev->thread->mctx, + ev->thread->mctx, NULL, UNCONST(void *, ev->rctx)), ev->request, now); @@ -304,8 +305,28 @@ static unlang_action_t unlang_xlat_repeat(rlm_rcode_t *p_result, request_t *requ xlat_action_t xa; xlat_exp_head_t const *child = NULL; + /* + * If the xlat is a function with a method_env, expand it before calling the function. + */ + if ((state->exp->type == XLAT_FUNC) && state->exp->call.func->call_env && !state->env_data) { + unlang_action_t ua = call_env_expand(state, request, &state->env_data, + state->exp->call.func->call_env, + &state->exp->call.inst->call_env_parsed); + switch (ua) { + case UNLANG_ACTION_FAIL: + goto fail; + + case UNLANG_ACTION_PUSHED_CHILD: + frame_repeat(frame, unlang_xlat_repeat); + return UNLANG_ACTION_PUSHED_CHILD; + + default: + break; + } + } + xa = xlat_frame_eval_repeat(state->ctx, &state->values, &child, - &state->alternate, request, state->head, &state->exp, &state->out); + &state->alternate, request, state->head, &state->exp, state->env_data, &state->out); switch (xa) { case XLAT_ACTION_PUSH_CHILD: fr_assert(child); diff --git a/src/lib/unlang/xlat.h b/src/lib/unlang/xlat.h index 0077ae5b39b..f9aba933c28 100644 --- a/src/lib/unlang/xlat.h +++ b/src/lib/unlang/xlat.h @@ -65,6 +65,7 @@ typedef size_t (*xlat_escape_legacy_t)(request_t *request, char *out, size_t out #include #include +#include #include /** Instance data for an xlat expansion node @@ -78,6 +79,9 @@ struct xlat_inst { xlat_exp_t *node; //!< Node this data relates to. void *data; //!< xlat node specific instance data. + call_env_parsed_head_t call_env_parsed; //!< The per call parsed environment. + TALLOC_CTX *call_env_ctx; //!< A talloc pooled object for parsed call env + ///< to be allocated from. }; /** Thread specific instance data for xlat expansion node diff --git a/src/lib/unlang/xlat_ctx.h b/src/lib/unlang/xlat_ctx.h index cd88fa6b4d4..19ca0961a7f 100644 --- a/src/lib/unlang/xlat_ctx.h +++ b/src/lib/unlang/xlat_ctx.h @@ -43,6 +43,7 @@ typedef struct { void const *inst; //!< xlat instance data. void *thread; //!< xlat threadinstance data. module_ctx_t const *mctx; //!< Synthesised module calling ctx. + void *env_data; //!< Expanded module env data. void *rctx; //!< Resume context. } xlat_ctx_t; @@ -79,9 +80,11 @@ typedef struct { * @param[in] _inst Instance data of the module being called. * @param[in] _thread Instance data of the thread being called. * @param[in] _mctx Module ctx. + * @param[in] _env_data Expanded module env. * @param[in] _rctx resume ctx data. */ -#define XLAT_CTX(_inst, _thread, _mctx, _rctx) &(xlat_ctx_t){ .inst = _inst, .thread = _thread, .mctx = _mctx, .rctx = _rctx } +#define XLAT_CTX(_inst, _thread, _mctx, _env_data, _rctx) &(xlat_ctx_t){ .inst = _inst, .thread = _thread, \ + .mctx = _mctx, .env_data = _env_data, .rctx = _rctx } /** Wrapper to create a xlat_inst_ctx_t as a compound literal * diff --git a/src/lib/unlang/xlat_eval.c b/src/lib/unlang/xlat_eval.c index 77d3b211c0b..ea81109d47e 100644 --- a/src/lib/unlang/xlat_eval.c +++ b/src/lib/unlang/xlat_eval.c @@ -796,7 +796,7 @@ void xlat_signal(xlat_func_signal_t signal, xlat_exp_t const *exp, { xlat_thread_inst_t *t = xlat_thread_instance_find(exp); - signal(XLAT_CTX(exp->call.inst, t->data, t->mctx, rctx), request, action); + signal(XLAT_CTX(exp->call.inst, t->data, t->mctx, NULL, rctx), request, action); } /** Call an xlat's resumption method @@ -835,11 +835,11 @@ xlat_action_t xlat_frame_eval_resume(TALLOC_CTX *ctx, fr_dcursor_t *out, VALUE_BOX_TALLOC_LIST_VERIFY(result); if (node->type != XLAT_FUNC) { - xa = resume(ctx, out, XLAT_CTX(NULL, NULL, NULL, rctx), request, result); + xa = resume(ctx, out, XLAT_CTX(NULL, NULL, NULL, NULL, rctx), request, result); } else { xlat_thread_inst_t *t; t = xlat_thread_instance_find(node); - xa = resume(ctx, out, XLAT_CTX(node->call.inst->data, t->data, t->mctx, rctx), request, result); + xa = resume(ctx, out, XLAT_CTX(node->call.inst->data, t->data, t->mctx, NULL, rctx), request, result); VALUE_BOX_TALLOC_LIST_VERIFY(result); RDEBUG2("| %%%c%s:...%c", @@ -892,12 +892,13 @@ xlat_action_t xlat_frame_eval_resume(TALLOC_CTX *ctx, fr_dcursor_t *out, * @param[in] head of the list to evaluate * @param[in,out] in xlat node to evaluate. Advanced as we process * additional #xlat_exp_t. + * @param[in] env_data Expanded module env. * @param[in] result of a previous nested evaluation. */ xlat_action_t xlat_frame_eval_repeat(TALLOC_CTX *ctx, fr_dcursor_t *out, xlat_exp_head_t const **child, bool *alternate, request_t *request, xlat_exp_head_t const *head, xlat_exp_t const **in, - fr_value_box_list_t *result) + void *env_data, fr_value_box_list_t *result) { xlat_exp_t const *node = *in; @@ -941,7 +942,7 @@ xlat_action_t xlat_frame_eval_repeat(TALLOC_CTX *ctx, fr_dcursor_t *out, VALUE_BOX_TALLOC_LIST_VERIFY(result); xa = node->call.func->func(ctx, out, - XLAT_CTX(node->call.inst->data, t->data, t->mctx, NULL), + XLAT_CTX(node->call.inst->data, t->data, t->mctx, env_data, NULL), request, result); VALUE_BOX_TALLOC_LIST_VERIFY(result); @@ -1236,7 +1237,7 @@ xlat_action_t xlat_frame_eval(TALLOC_CTX *ctx, fr_dcursor_t *out, xlat_exp_head_ xlat_debug_log_expansion(request, node, NULL, __LINE__); xa = node->call.func->func(ctx, out, - XLAT_CTX(node->call.func->uctx, NULL, NULL, NULL), + XLAT_CTX(node->call.func->uctx, NULL, NULL, NULL, NULL), request, NULL); fr_dcursor_next(out); @@ -1262,7 +1263,7 @@ xlat_action_t xlat_frame_eval(TALLOC_CTX *ctx, fr_dcursor_t *out, xlat_exp_head_ * If there's no children we can just * call the function directly. */ - xa = xlat_frame_eval_repeat(ctx, out, child, NULL, request, head, in, &result); + xa = xlat_frame_eval_repeat(ctx, out, child, NULL, request, head, in, NULL, &result); if (xa != XLAT_ACTION_DONE || (!*in)) goto finish; continue; diff --git a/src/lib/unlang/xlat_func.c b/src/lib/unlang/xlat_func.c index 728e9139aaa..5de229bab30 100644 --- a/src/lib/unlang/xlat_func.c +++ b/src/lib/unlang/xlat_func.c @@ -337,6 +337,16 @@ int xlat_func_mono_set(xlat_t *x, xlat_arg_parser_t const args[]) return 0; } +/** Register call environment of an xlat + * + * @param[in,out] x to have it's module method env registered. + * @param[in] env to be registered. + */ +void xlat_func_call_env_set(xlat_t *x, call_method_env_t const *env) +{ + x->call_env = env; +} + /** Specify flags that alter the xlat's behaviour * * @param[in] x xlat to set flags for. diff --git a/src/lib/unlang/xlat_func.h b/src/lib/unlang/xlat_func.h index e4146ec3fdf..7d23b1f20b0 100644 --- a/src/lib/unlang/xlat_func.h +++ b/src/lib/unlang/xlat_func.h @@ -60,6 +60,8 @@ int xlat_func_args_set(xlat_t *xlat, xlat_arg_parser_t const args[]) CC_HINT(no int xlat_func_mono_set(xlat_t *xlat, xlat_arg_parser_t const *arg) CC_HINT(nonnull); +void xlat_func_call_env_set(xlat_t *x, call_method_env_t const *env) CC_HINT(nonnull); + void xlat_func_flags_set(xlat_t *x, xlat_func_flags_t flags) CC_HINT(nonnull); void xlat_func_print_set(xlat_t *xlat, xlat_print_t func); diff --git a/src/lib/unlang/xlat_inst.c b/src/lib/unlang/xlat_inst.c index 580424a2952..9713b0a7a1a 100644 --- a/src/lib/unlang/xlat_inst.c +++ b/src/lib/unlang/xlat_inst.c @@ -245,6 +245,27 @@ static xlat_inst_t *xlat_inst_alloc(xlat_exp_t *node) } } + /* + * If the xlat has a call env defined, parse it. + */ + if (call->func->call_env) { + size_t count, vallen = 0; + CONF_SECTION *cs = call->func->mctx->inst->conf; + call_method_env_t const *call_env = call->func->call_env; + + count = call_env_count(&vallen, cs, call_env->env); + MEM(xi->call_env_ctx = _talloc_pooled_object(xi, 0, "call_env_ctx", count * 4, + (sizeof(call_env_parsed_t) + sizeof(tmpl_t)) * count + vallen * 2)); + call_env_parsed_init(&xi->call_env_parsed); + if (call_env_parse(xi->call_env_ctx, &xi->call_env_parsed, call->func->mctx->inst->name, + call->dict, call->func->mctx->inst->conf, call_env->env) < 0) { + talloc_free(xi); + return NULL; + } + fr_assert_msg(call_env->inst_size, "Method environment for module %s, xlat %s declared, " + "but no inst_size set", call->func->mctx->inst->name, call->func->name); + } + return xi; } diff --git a/src/lib/unlang/xlat_priv.h b/src/lib/unlang/xlat_priv.h index 01118271a7b..f49aa329327 100644 --- a/src/lib/unlang/xlat_priv.h +++ b/src/lib/unlang/xlat_priv.h @@ -88,6 +88,10 @@ typedef struct xlat_s { xlat_input_type_t input_type; //!< Type of input used. xlat_arg_parser_t const *args; //!< Definition of args consumed. + call_method_env_t const *call_env; //!< Optional tmpl expansions performed before calling the + ///< xlat. Typically used for xlats which refer to tmpls + ///< in their module config. + fr_type_t return_type; //!< Function is guaranteed to return one or more boxes ///< of this type. If the return type is FR_TYPE_VOID ///< then the xlat function can return any type of output. @@ -128,6 +132,8 @@ typedef struct { ///< into the instance tree. xlat_input_type_t input_type; //!< The input type used inferred from the ///< bracketing style. + + fr_dict_t const *dict; //!< Dictionary to use when resolving module env tmpls } xlat_call_t; /** An xlat expansion node @@ -295,7 +301,7 @@ xlat_action_t xlat_frame_eval_resume(TALLOC_CTX *ctx, fr_dcursor_t *out, xlat_action_t xlat_frame_eval_repeat(TALLOC_CTX *ctx, fr_dcursor_t *out, xlat_exp_head_t const **child, bool *alternate, request_t *request, xlat_exp_head_t const *head, xlat_exp_t const **in, - fr_value_box_list_t *result) CC_HINT(nonnull(1,2,3,5)); + void *env_data, fr_value_box_list_t *result) CC_HINT(nonnull(1,2,3,5)); xlat_action_t xlat_frame_eval(TALLOC_CTX *ctx, fr_dcursor_t *out, xlat_exp_head_t const **child, request_t *request, xlat_exp_head_t const *head, xlat_exp_t const **in); diff --git a/src/lib/unlang/xlat_tokenize.c b/src/lib/unlang/xlat_tokenize.c index 146d9b0eeaf..6934d7da5e2 100644 --- a/src/lib/unlang/xlat_tokenize.c +++ b/src/lib/unlang/xlat_tokenize.c @@ -322,6 +322,7 @@ static inline int xlat_tokenize_function_mono(xlat_exp_head_t *head, return -1; } node->call.func = func; + if (t_rules) node->call.dict = t_rules->attr.dict_def; node->flags = func->flags; } @@ -582,6 +583,7 @@ int xlat_tokenize_function_args(xlat_exp_head_t *head, fr_sbuff_t *in, return -1; } node->call.func = func; + if (t_rules) node->call.dict = t_rules->attr.dict_def; node->flags = func->flags; } @@ -1865,6 +1867,7 @@ int xlat_resolve(xlat_exp_head_t *head, xlat_res_rules_t const *xr_rules) } xlat_exp_set_type(node, XLAT_FUNC); + node->call.dict = xr_rules->tr_rules->dict_def; /* * Check input arguments of our freshly diff --git a/src/modules/rlm_files/rlm_files.c b/src/modules/rlm_files/rlm_files.c index c4d5dbe9c55..21898f0eef6 100644 --- a/src/modules/rlm_files/rlm_files.c +++ b/src/modules/rlm_files/rlm_files.c @@ -674,16 +674,16 @@ static unlang_action_t CC_HINT(nonnull) mod_post_auth(rlm_rcode_t *p_result, mod * it is still evaluated during module instantiation to determine the tree type in use * so more restructuring is needed to make the module protocol agnostic. */ -static const module_env_t module_env[] = { - { FR_MODULE_ENV_OFFSET("key", FR_TYPE_VOID, rlm_files_env_t, key, "%{%{Stripped-User-Name}:-%{User-Name}}", - T_DOUBLE_QUOTED_STRING, true, false, false) }, - MODULE_ENV_TERMINATOR +static const call_env_t call_env[] = { + { FR_CALL_ENV_OFFSET("key", FR_TYPE_VOID, rlm_files_env_t, key, "%{%{Stripped-User-Name}:-%{User-Name}}", + T_DOUBLE_QUOTED_STRING, true, false, false) }, + CALL_ENV_TERMINATOR }; -static const module_method_env_t method_env = { +static const call_method_env_t method_env = { .inst_size = sizeof(rlm_files_env_t), .inst_type = "rlm_files_env_t", - .env = module_env + .env = call_env }; /* globally exported name */ diff --git a/src/modules/rlm_ldap/groups.c b/src/modules/rlm_ldap/groups.c index cba18c7557d..6e52b18276f 100644 --- a/src/modules/rlm_ldap/groups.c +++ b/src/modules/rlm_ldap/groups.c @@ -535,7 +535,7 @@ unlang_action_t rlm_ldap_cacheable_userobj(rlm_rcode_t *p_result, request_t *req MEM(group_ctx = talloc_zero(unlang_interpret_frame_talloc_ctx(request), ldap_group_userobj_ctx_t)); group_ctx->inst = inst; group_ctx->ttrunk = ttrunk; - group_ctx->base_dn = &autz_ctx->mod_env->group_base; + group_ctx->base_dn = &autz_ctx->call_env->group_base; group_ctx->list_ctx = tmpl_list_ctx(request, request_attr_control); fr_assert(group_ctx->list_ctx != NULL); @@ -755,7 +755,7 @@ unlang_action_t rlm_ldap_cacheable_groupobj(rlm_rcode_t *p_result, request_t *re RETURN_MODULE_OK; } - if (autz_ctx->mod_env->group_base.type != FR_TYPE_STRING) { + if (autz_ctx->call_env->group_base.type != FR_TYPE_STRING) { REDEBUG("Missing group base_dn"); RETURN_MODULE_INVALID; } @@ -763,7 +763,7 @@ unlang_action_t rlm_ldap_cacheable_groupobj(rlm_rcode_t *p_result, request_t *re MEM(group_ctx = talloc_zero(unlang_interpret_frame_talloc_ctx(request), ldap_group_groupobj_ctx_t)); group_ctx->inst = inst; group_ctx->ttrunk = autz_ctx->ttrunk; - group_ctx->base_dn = &autz_ctx->mod_env->group_base; + group_ctx->base_dn = &autz_ctx->call_env->group_base; if (fr_ldap_xlat_filter(request, filters, NUM_ELEMENTS(filters), group_ctx->filter, sizeof(group_ctx->filter)) < 0) { diff --git a/src/modules/rlm_ldap/rlm_ldap.c b/src/modules/rlm_ldap/rlm_ldap.c index 2bcd428c737..ef3696005c6 100644 --- a/src/modules/rlm_ldap/rlm_ldap.c +++ b/src/modules/rlm_ldap/rlm_ldap.c @@ -49,23 +49,23 @@ typedef struct { fr_value_box_t user_sasl_authname; fr_value_box_t user_sasl_proxy; fr_value_box_t user_sasl_realm; -} ldap_auth_mod_env_t; +} ldap_auth_call_env_t; typedef struct { fr_value_box_t user_base; fr_value_box_t user_filter; -} ldap_usermod_mod_env_t; - -static const module_env_t sasl_module_env[] = { - { FR_MODULE_ENV_OFFSET("mech", FR_TYPE_STRING, ldap_auth_mod_env_t, user_sasl_mech, - NULL, T_INVALID, false, false, false) }, - { FR_MODULE_ENV_OFFSET("authname", FR_TYPE_STRING, ldap_auth_mod_env_t, user_sasl_authname, - NULL, T_INVALID, false, false, false) }, - { FR_MODULE_ENV_OFFSET("proxy", FR_TYPE_STRING, ldap_auth_mod_env_t, user_sasl_proxy, - NULL, T_INVALID, false, true, false) }, - { FR_MODULE_ENV_OFFSET("realm", FR_TYPE_STRING, ldap_auth_mod_env_t, user_sasl_realm, - NULL, T_INVALID, false, true, false) }, - MODULE_ENV_TERMINATOR +} ldap_usermod_call_env_t; + +static const call_env_t sasl_call_env[] = { + { FR_CALL_ENV_OFFSET("mech", FR_TYPE_STRING, ldap_auth_call_env_t, user_sasl_mech, + NULL, T_INVALID, false, false, false) }, + { FR_CALL_ENV_OFFSET("authname", FR_TYPE_STRING, ldap_auth_call_env_t, user_sasl_authname, + NULL, T_INVALID, false, false, false) }, + { FR_CALL_ENV_OFFSET("proxy", FR_TYPE_STRING, ldap_auth_call_env_t, user_sasl_proxy, + NULL, T_INVALID, false, true, false) }, + { FR_CALL_ENV_OFFSET("realm", FR_TYPE_STRING, ldap_auth_call_env_t, user_sasl_realm, + NULL, T_INVALID, false, true, false) }, + CALL_ENV_TERMINATOR }; static CONF_PARSER profile_config[] = { @@ -73,12 +73,12 @@ static CONF_PARSER profile_config[] = { CONF_PARSER_TERMINATOR }; -static const module_env_t autz_profile_module_env[] = { - { FR_MODULE_ENV_OFFSET("default", FR_TYPE_STRING, ldap_autz_mod_env_t, default_profile, - NULL, T_INVALID, false, false, true) }, - { FR_MODULE_ENV_OFFSET("filter", FR_TYPE_STRING, ldap_autz_mod_env_t, profile_filter, - "(&)", T_SINGLE_QUOTED_STRING, false, false, true ) }, //!< Correct filter for when the DN is known. - MODULE_ENV_TERMINATOR +static const call_env_t autz_profile_call_env[] = { + { FR_CALL_ENV_OFFSET("default", FR_TYPE_STRING, ldap_autz_call_env_t, default_profile, + NULL, T_INVALID, false, false, true) }, + { FR_CALL_ENV_OFFSET("filter", FR_TYPE_STRING, ldap_autz_call_env_t, profile_filter, + "(&)", T_SINGLE_QUOTED_STRING, false, false, true ) }, //!< Correct filter for when the DN is known. + CALL_ENV_TERMINATOR }; /* @@ -95,29 +95,29 @@ static CONF_PARSER user_config[] = { CONF_PARSER_TERMINATOR }; -static const module_env_t auth_user_module_env[] = { - { FR_MODULE_ENV_OFFSET("base_dn", FR_TYPE_STRING, ldap_auth_mod_env_t, user_base, - "", T_SINGLE_QUOTED_STRING, true, false, true) }, - { FR_MODULE_ENV_OFFSET("filter", FR_TYPE_STRING, ldap_auth_mod_env_t, user_filter, - NULL, T_INVALID, false, true, true) }, - { FR_MODULE_ENV_SUBSECTION("sasl", NULL, sasl_module_env) }, - MODULE_ENV_TERMINATOR +static const call_env_t auth_user_call_env[] = { + { FR_CALL_ENV_OFFSET("base_dn", FR_TYPE_STRING, ldap_auth_call_env_t, user_base, + "", T_SINGLE_QUOTED_STRING, true, false, true) }, + { FR_CALL_ENV_OFFSET("filter", FR_TYPE_STRING, ldap_auth_call_env_t, user_filter, + NULL, T_INVALID, false, true, true) }, + { FR_CALL_ENV_SUBSECTION("sasl", NULL, sasl_call_env) }, + CALL_ENV_TERMINATOR }; -static const module_env_t autz_user_module_env[] = { - { FR_MODULE_ENV_OFFSET("base_dn", FR_TYPE_STRING, ldap_autz_mod_env_t, user_base, - "", T_SINGLE_QUOTED_STRING, true, false, true) }, - { FR_MODULE_ENV_OFFSET("filter", FR_TYPE_STRING, ldap_autz_mod_env_t, user_filter, - NULL, T_INVALID, false, true, true) }, - MODULE_ENV_TERMINATOR +static const call_env_t autz_user_call_env[] = { + { FR_CALL_ENV_OFFSET("base_dn", FR_TYPE_STRING, ldap_autz_call_env_t, user_base, + "", T_SINGLE_QUOTED_STRING, true, false, true) }, + { FR_CALL_ENV_OFFSET("filter", FR_TYPE_STRING, ldap_autz_call_env_t, user_filter, + NULL, T_INVALID, false, true, true) }, + CALL_ENV_TERMINATOR }; -static const module_env_t usermod_user_module_env[] = { - { FR_MODULE_ENV_OFFSET("base_dn", FR_TYPE_STRING, ldap_usermod_mod_env_t, user_base, - "", T_SINGLE_QUOTED_STRING, true, false, true) }, - { FR_MODULE_ENV_OFFSET("filter", FR_TYPE_STRING, ldap_usermod_mod_env_t, user_filter, - NULL, T_INVALID, false, true, true) }, - MODULE_ENV_TERMINATOR +static const call_env_t usermod_user_call_env[] = { + { FR_CALL_ENV_OFFSET("base_dn", FR_TYPE_STRING, ldap_usermod_call_env_t, user_base, + "", T_SINGLE_QUOTED_STRING, true, false, true) }, + { FR_CALL_ENV_OFFSET("filter", FR_TYPE_STRING, ldap_usermod_call_env_t, user_filter, + NULL, T_INVALID, false, true, true) }, + CALL_ENV_TERMINATOR }; /* @@ -139,10 +139,10 @@ static CONF_PARSER group_config[] = { CONF_PARSER_TERMINATOR }; -static const module_env_t autz_group_module_env[] = { - { FR_MODULE_ENV_OFFSET("base_dn", FR_TYPE_STRING, ldap_autz_mod_env_t, group_base, - NULL, T_INVALID, false, false, true) }, - MODULE_ENV_TERMINATOR +static const call_env_t autz_group_call_env[] = { + { FR_CALL_ENV_OFFSET("base_dn", FR_TYPE_STRING, ldap_autz_call_env_t, group_base, + NULL, T_INVALID, false, false, true) }, + CALL_ENV_TERMINATOR }; /* @@ -196,41 +196,41 @@ static const CONF_PARSER module_config[] = { }; /* - * Method specific module environments + * Method specific call environments */ -static const module_env_t authenticate_module_env[] = { - { FR_MODULE_ENV_SUBSECTION("user", NULL, auth_user_module_env) }, - MODULE_ENV_TERMINATOR +static const call_env_t authenticate_call_env[] = { + { FR_CALL_ENV_SUBSECTION("user", NULL, auth_user_call_env) }, + CALL_ENV_TERMINATOR }; -static const module_env_t authorize_module_env[] = { - { FR_MODULE_ENV_SUBSECTION("user", NULL, autz_user_module_env) }, - { FR_MODULE_ENV_SUBSECTION("group", NULL, autz_group_module_env) }, - { FR_MODULE_ENV_SUBSECTION("profile", NULL, autz_profile_module_env) }, - MODULE_ENV_TERMINATOR +static const call_env_t authorize_call_env[] = { + { FR_CALL_ENV_SUBSECTION("user", NULL, autz_user_call_env) }, + { FR_CALL_ENV_SUBSECTION("group", NULL, autz_group_call_env) }, + { FR_CALL_ENV_SUBSECTION("profile", NULL, autz_profile_call_env) }, + CALL_ENV_TERMINATOR }; -static const module_env_t usermod_module_env[] = { - { FR_MODULE_ENV_SUBSECTION("user", NULL, usermod_user_module_env) }, - MODULE_ENV_TERMINATOR +static const call_env_t usermod_call_env[] = { + { FR_CALL_ENV_SUBSECTION("user", NULL, usermod_user_call_env) }, + CALL_ENV_TERMINATOR }; -static const module_method_env_t authenticate_method_env = { - .inst_size = sizeof(ldap_auth_mod_env_t), - .inst_type = "ldap_auth_mod_env_t", - .env = authenticate_module_env +static const call_method_env_t authenticate_method_env = { + .inst_size = sizeof(ldap_auth_call_env_t), + .inst_type = "ldap_auth_call_env_t", + .env = authenticate_call_env }; -static const module_method_env_t authorize_method_env = { - .inst_size = sizeof(ldap_autz_mod_env_t), - .inst_type = "ldap_autz_mod_env_t", - .env = authorize_module_env +static const call_method_env_t authorize_method_env = { + .inst_size = sizeof(ldap_autz_call_env_t), + .inst_type = "ldap_autz_call_env_t", + .env = authorize_call_env }; -static const module_method_env_t usermod_method_env = { - .inst_size = sizeof(ldap_usermod_mod_env_t), - .inst_type = "ldap_usermod_mod_env_t", - .env = usermod_module_env +static const call_method_env_t usermod_method_env = { + .inst_size = sizeof(ldap_usermod_call_env_t), + .inst_type = "ldap_usermod_call_env_t", + .env = usermod_call_env }; static fr_dict_t const *dict_freeradius; @@ -280,7 +280,7 @@ typedef struct { char const *password; rlm_ldap_t const *inst; fr_ldap_thread_t *thread; - ldap_auth_mod_env_t *mod_env; + ldap_auth_call_env_t *call_env; } ldap_auth_ctx_t; /** Holds state of in progress async profile lookups @@ -298,7 +298,7 @@ typedef struct { */ typedef struct { rlm_ldap_t const *inst; - ldap_usermod_mod_env_t *mod_env; + ldap_usermod_call_env_t *call_env; char const *dn; char *passed[LDAP_MAX_ATTRMAP * 2]; LDAPMod *mod_p[LDAP_MAX_ATTRMAP + 1]; @@ -1008,8 +1008,8 @@ static unlang_action_t mod_authenticate_start(rlm_rcode_t *p_result, UNUSED int inst->handle_config.admin_password, request, &inst->handle_config); if (!ttrunk) RETURN_MODULE_FAIL; - return rlm_ldap_find_user_async(auth_ctx, auth_ctx->inst, request, &auth_ctx->mod_env->user_base, - &auth_ctx->mod_env->user_filter, ttrunk, NULL, NULL); + return rlm_ldap_find_user_async(auth_ctx, auth_ctx->inst, request, &auth_ctx->call_env->user_base, + &auth_ctx->call_env->user_filter, ttrunk, NULL, NULL); } /** Initiate async LDAP bind to authenticate user @@ -1037,13 +1037,13 @@ static unlang_action_t mod_authenticate_resume(rlm_rcode_t *p_result, UNUSED int /* * Attempt a bind using the thread specific trunk for bind auths */ - if (auth_ctx->mod_env->user_sasl_mech.type == FR_TYPE_STRING) { + if (auth_ctx->call_env->user_sasl_mech.type == FR_TYPE_STRING) { #ifdef WITH_SASL - ldap_auth_mod_env_t *mod_env = auth_ctx->mod_env; - if (fr_ldap_sasl_bind_auth_async(request, auth_ctx->thread, mod_env->user_sasl_mech.vb_strvalue, - auth_ctx->dn, mod_env->user_sasl_authname.vb_strvalue, - auth_ctx->password, mod_env->user_sasl_proxy.vb_strvalue, - mod_env->user_sasl_realm.vb_strvalue) < 0) goto fail; + ldap_auth_call_env_t *call_env = auth_ctx->call_env; + if (fr_ldap_sasl_bind_auth_async(request, auth_ctx->thread, call_env->user_sasl_mech.vb_strvalue, + auth_ctx->dn, call_env->user_sasl_authname.vb_strvalue, + auth_ctx->password, call_env->user_sasl_proxy.vb_strvalue, + call_env->user_sasl_realm.vb_strvalue) < 0) goto fail; #else RDEBUG("Configuration item 'sasl.mech' is not supported. " "The linked version of libldap does not provide ldap_sasl_bind( function"); @@ -1060,7 +1060,7 @@ static unlang_action_t CC_HINT(nonnull) mod_authenticate(rlm_rcode_t *p_result, rlm_ldap_t const *inst = talloc_get_type_abort_const(mctx->inst->data, rlm_ldap_t); fr_ldap_thread_t *thread = talloc_get_type_abort(module_rlm_thread_by_data(inst)->data, fr_ldap_thread_t); ldap_auth_ctx_t *auth_ctx; - ldap_auth_mod_env_t *mod_env = talloc_get_type_abort(mctx->env_data, ldap_auth_mod_env_t); + ldap_auth_call_env_t *call_env = talloc_get_type_abort(mctx->env_data, ldap_auth_call_env_t); fr_pair_t *username, *password; @@ -1112,7 +1112,7 @@ static unlang_action_t CC_HINT(nonnull) mod_authenticate(rlm_rcode_t *p_result, .password = password->vp_strvalue, .thread = thread, .inst = inst, - .mod_env = mod_env + .call_env = call_env }; /* @@ -1224,7 +1224,7 @@ static unlang_action_t rlm_ldap_map_profile(request_t *request, ldap_autz_ctx_t } return fr_ldap_trunk_search(&ret, profile_ctx, &profile_ctx->query, request, ttrunk, dn, - LDAP_SCOPE_BASE, autz_ctx->mod_env->profile_filter.vb_strvalue, + LDAP_SCOPE_BASE, autz_ctx->call_env->profile_filter.vb_strvalue, expanded->attrs, NULL, NULL, true); } @@ -1235,8 +1235,8 @@ static unlang_action_t mod_authorize_start(UNUSED rlm_rcode_t *p_result, UNUSED request_t *request, void *uctx) { ldap_autz_ctx_t *autz_ctx = talloc_get_type_abort(uctx, ldap_autz_ctx_t); - return rlm_ldap_find_user_async(autz_ctx, autz_ctx->inst, request, &autz_ctx->mod_env->user_base, - &autz_ctx->mod_env->user_filter, autz_ctx->ttrunk, autz_ctx->expanded.attrs, + return rlm_ldap_find_user_async(autz_ctx, autz_ctx->inst, request, &autz_ctx->call_env->user_base, + &autz_ctx->call_env->user_filter, autz_ctx->ttrunk, autz_ctx->expanded.attrs, &autz_ctx->query); } @@ -1263,7 +1263,7 @@ static unlang_action_t mod_authorize_resume(rlm_rcode_t *p_result, UNUSED int *p { ldap_autz_ctx_t *autz_ctx = talloc_get_type_abort(uctx, ldap_autz_ctx_t); rlm_ldap_t const *inst = talloc_get_type_abort_const(autz_ctx->inst, rlm_ldap_t); - ldap_autz_mod_env_t *mod_env = talloc_get_type_abort(autz_ctx->mod_env, ldap_autz_mod_env_t); + ldap_autz_call_env_t *call_env = talloc_get_type_abort(autz_ctx->call_env, ldap_autz_call_env_t); int ldap_errno; rlm_rcode_t rcode = RLM_MODULE_OK; LDAP *handle = fr_ldap_handle_thread_local(); @@ -1393,11 +1393,11 @@ static unlang_action_t mod_authorize_resume(rlm_rcode_t *p_result, UNUSED int *p /* * Apply ONE user profile, or a default user profile. */ - if (mod_env->default_profile.type == FR_TYPE_STRING) { + if (call_env->default_profile.type == FR_TYPE_STRING) { unlang_action_t ret; REPEAT_MOD_AUTHORIZE_RESUME; - ret = rlm_ldap_map_profile(request, autz_ctx, mod_env->default_profile.vb_strvalue, + ret = rlm_ldap_map_profile(request, autz_ctx, call_env->default_profile.vb_strvalue, &autz_ctx->expanded); switch (ret) { case UNLANG_ACTION_FAIL: @@ -1494,7 +1494,7 @@ static unlang_action_t CC_HINT(nonnull) mod_authorize(rlm_rcode_t *p_result, mod fr_ldap_thread_t *thread = talloc_get_type_abort(module_rlm_thread_by_data(inst)->data, fr_ldap_thread_t); ldap_autz_ctx_t *autz_ctx; fr_ldap_map_exp_t *expanded; - ldap_autz_mod_env_t *mod_env = talloc_get_type_abort(mctx->env_data, ldap_autz_mod_env_t); + ldap_autz_call_env_t *call_env = talloc_get_type_abort(mctx->env_data, ldap_autz_call_env_t); MEM(autz_ctx = talloc_zero(unlang_interpret_frame_talloc_ctx(request), ldap_autz_ctx_t)); talloc_set_destructor(autz_ctx, autz_ctx_free); @@ -1533,7 +1533,7 @@ static unlang_action_t CC_HINT(nonnull) mod_authorize(rlm_rcode_t *p_result, mod autz_ctx->dlinst = mctx->inst; autz_ctx->inst = inst; - autz_ctx->mod_env = mod_env; + autz_ctx->call_env = call_env; autz_ctx->status = LDAP_AUTZ_FIND; if (unlang_function_push(request, mod_authorize_start, mod_authorize_resume, mod_authorize_cancel, @@ -1550,8 +1550,8 @@ static unlang_action_t user_modify_start(UNUSED rlm_rcode_t *p_result, UNUSED in { ldap_user_modify_ctx_t *usermod_ctx = talloc_get_type_abort(uctx, ldap_user_modify_ctx_t); - return rlm_ldap_find_user_async(usermod_ctx, usermod_ctx->inst, request, &usermod_ctx->mod_env->user_base, - &usermod_ctx->mod_env->user_filter, usermod_ctx->ttrunk, NULL, NULL); + return rlm_ldap_find_user_async(usermod_ctx, usermod_ctx->inst, request, &usermod_ctx->call_env->user_base, + &usermod_ctx->call_env->user_filter, usermod_ctx->ttrunk, NULL, NULL); } /** Cancel an in progress user modification. @@ -1631,11 +1631,11 @@ static unlang_action_t user_modify_resume(rlm_rcode_t *p_result, UNUSED int *pri * @param[in] inst rlm_ldap instance. * @param[in] request Current request. * @param[in] section that holds the map to process. - * @param[in] mod_env Module environment. Contains expanded base and filter to find user. + * @param[in] call_env Call environment. Contains expanded base and filter to find user. * @return one of the RLM_MODULE_* values. */ static unlang_action_t user_modify(rlm_rcode_t *p_result, rlm_ldap_t const *inst, request_t *request, - ldap_acct_section_t *section, ldap_usermod_mod_env_t *mod_env) + ldap_acct_section_t *section, ldap_usermod_call_env_t *call_env) { rlm_rcode_t rcode = RLM_MODULE_FAIL; fr_ldap_thread_t *thread = talloc_get_type_abort(module_rlm_thread_by_data(inst)->data, fr_ldap_thread_t); @@ -1686,7 +1686,7 @@ static unlang_action_t user_modify(rlm_rcode_t *p_result, rlm_ldap_t const *inst MEM(usermod_ctx = talloc(unlang_interpret_frame_talloc_ctx(request), ldap_user_modify_ctx_t)); *usermod_ctx = (ldap_user_modify_ctx_t) { .inst = inst, - .mod_env = mod_env + .call_env = call_env }; /* @@ -1832,9 +1832,9 @@ error: static unlang_action_t CC_HINT(nonnull) mod_accounting(rlm_rcode_t *p_result, module_ctx_t const *mctx, request_t *request) { rlm_ldap_t const *inst = talloc_get_type_abort_const(mctx->inst->data, rlm_ldap_t); - ldap_usermod_mod_env_t *mod_env = talloc_get_type_abort(mctx->env_data, ldap_usermod_mod_env_t); + ldap_usermod_call_env_t *call_env = talloc_get_type_abort(mctx->env_data, ldap_usermod_call_env_t); - if (inst->accounting) return user_modify(p_result, inst, request, inst->accounting, mod_env); + if (inst->accounting) return user_modify(p_result, inst, request, inst->accounting, call_env); RETURN_MODULE_NOOP; } @@ -1842,9 +1842,9 @@ static unlang_action_t CC_HINT(nonnull) mod_accounting(rlm_rcode_t *p_result, mo static unlang_action_t CC_HINT(nonnull) mod_post_auth(rlm_rcode_t *p_result, module_ctx_t const *mctx, request_t *request) { rlm_ldap_t const *inst = talloc_get_type_abort_const(mctx->inst->data, rlm_ldap_t); - ldap_usermod_mod_env_t *mod_env = talloc_get_type_abort(mctx->env_data, ldap_usermod_mod_env_t); + ldap_usermod_call_env_t *call_env = talloc_get_type_abort(mctx->env_data, ldap_usermod_call_env_t); - if (inst->postauth) return user_modify(p_result, inst, request, inst->postauth, mod_env); + if (inst->postauth) return user_modify(p_result, inst, request, inst->postauth, call_env); RETURN_MODULE_NOOP; } diff --git a/src/modules/rlm_ldap/rlm_ldap.h b/src/modules/rlm_ldap/rlm_ldap.h index d8d8311a2e0..4f44c46f412 100644 --- a/src/modules/rlm_ldap/rlm_ldap.h +++ b/src/modules/rlm_ldap/rlm_ldap.h @@ -137,7 +137,7 @@ typedef struct { //!< No value should be set if profiles are not being used //!< as there is an associated performance penalty. fr_value_box_t profile_filter; //!< Filter to use when searching for profiles. -} ldap_autz_mod_env_t; +} ldap_autz_call_env_t; /** State list for resumption of authorization * @@ -164,7 +164,7 @@ typedef struct { fr_ldap_map_exp_t expanded; fr_ldap_query_t *query; fr_ldap_thread_trunk_t *ttrunk; - ldap_autz_mod_env_t *mod_env; + ldap_autz_call_env_t *call_env; LDAPMessage *entry; ldap_autz_status_t status; struct berval **profile_values; diff --git a/src/modules/rlm_smtp/rlm_smtp.c b/src/modules/rlm_smtp/rlm_smtp.c index 3786d67b425..3537ceb8bbd 100644 --- a/src/modules/rlm_smtp/rlm_smtp.c +++ b/src/modules/rlm_smtp/rlm_smtp.c @@ -838,7 +838,7 @@ static unlang_action_t CC_HINT(nonnull) mod_mail(rlm_rcode_t *p_result, module_c { rlm_smtp_t const *inst = talloc_get_type_abort_const(mctx->inst->data, rlm_smtp_t); rlm_smtp_thread_t *t = talloc_get_type_abort(mctx->thread, rlm_smtp_thread_t); - rlm_smtp_env_t *mod_env = talloc_get_type_abort(mctx->env_data, rlm_smtp_env_t); + rlm_smtp_env_t *call_env = talloc_get_type_abort(mctx->env_data, rlm_smtp_env_t); fr_curl_io_request_t *randle = NULL; fr_mail_ctx_t *mail_ctx; const char *envelope_address; @@ -866,9 +866,9 @@ static unlang_action_t CC_HINT(nonnull) mod_mail(rlm_rcode_t *p_result, module_c * a onetime connection is used, otherwise a persistent one * can be used. */ - randle = (mod_env->username_tmpl && - !tmpl_is_data(mod_env->username_tmpl)) ? smtp_slab_reserve(t->slab_onetime) : - smtp_slab_reserve(t->slab_persist); + randle = (call_env->username_tmpl && + !tmpl_is_data(call_env->username_tmpl)) ? smtp_slab_reserve(t->slab_onetime) : + smtp_slab_reserve(t->slab_persist); if (!randle) { RDEBUG2("A handle could not be allocated for the request"); RETURN_MODULE_FAIL; @@ -888,12 +888,12 @@ static unlang_action_t CC_HINT(nonnull) mod_mail(rlm_rcode_t *p_result, module_c FR_CURL_REQUEST_SET_OPTION(CURLOPT_UPLOAD, 1L); /* Set the username and password if they have been provided */ - if (mod_env->username.vb_strvalue) { - FR_CURL_REQUEST_SET_OPTION(CURLOPT_USERNAME, mod_env->username.vb_strvalue); + if (call_env->username.vb_strvalue) { + FR_CURL_REQUEST_SET_OPTION(CURLOPT_USERNAME, call_env->username.vb_strvalue); - if (!mod_env->password.vb_strvalue) goto skip_auth; + if (!call_env->password.vb_strvalue) goto skip_auth; - FR_CURL_REQUEST_SET_OPTION(CURLOPT_PASSWORD, mod_env->password.vb_strvalue); + FR_CURL_REQUEST_SET_OPTION(CURLOPT_PASSWORD, call_env->password.vb_strvalue); RDEBUG2("Username and password set"); } skip_auth: @@ -1238,18 +1238,18 @@ static int mod_thread_detach(module_thread_inst_ctx_t const *mctx) return 0; } -static const module_env_t module_env[] = { - { FR_MODULE_ENV_TMPL_OFFSET("username", FR_TYPE_STRING, rlm_smtp_env_t, username, username_tmpl, NULL, +static const call_env_t call_env[] = { + { FR_CALL_ENV_TMPL_OFFSET("username", FR_TYPE_STRING, rlm_smtp_env_t, username, username_tmpl, NULL, T_DOUBLE_QUOTED_STRING, false, true, true) }, - { FR_MODULE_ENV_OFFSET("password", FR_TYPE_STRING, rlm_smtp_env_t, password, NULL, + { FR_CALL_ENV_OFFSET("password", FR_TYPE_STRING, rlm_smtp_env_t, password, NULL, T_DOUBLE_QUOTED_STRING, false, true, true) }, - MODULE_ENV_TERMINATOR + CALL_ENV_TERMINATOR }; -static const module_method_env_t method_env = { +static const call_method_env_t method_env = { .inst_size = sizeof(rlm_smtp_env_t), .inst_type = "rlm_smtp_env_t", - .env = module_env + .env = call_env }; /*