diff --git a/src/lib/unlang/call_env.c b/src/lib/unlang/call_env.c index c8d8e0b8737b..08b58470b183 100644 --- a/src/lib/unlang/call_env.c +++ b/src/lib/unlang/call_env.c @@ -25,29 +25,47 @@ RCSID("$Id$") #include +#include +#include #include #include #include +#include +#include + #include #include "call_env.h" +struct call_env_parsed_s { + call_env_parsed_entry_t entry; //!< Entry in list of parsed call_env_parsers. + + union { + tmpl_t *tmpl; //!< Tmpl produced from parsing conf pair. + void *ptr; //!< Data produced from parsing conf pair. + } data; + + size_t count; //!< Number of CONF_PAIRs found, matching the #call_env_parser_t. + size_t multi_index; //!< Array index for this instance. + call_env_parser_t const *rule; //!< Used to produce this. +}; +FR_DLIST_FUNCS(call_env_parsed, call_env_parsed_t, entry) /** Parse the result of call_env tmpl expansion */ static inline CC_HINT(always_inline) -call_env_result_t 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) +call_env_result_t call_env_result(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; - if (tmpl_out) *tmpl_out = env->tmpl; - if (env->tmpl_only) return CALL_ENV_SUCCESS; + if (tmpl_out) *tmpl_out = env->data.tmpl; + if (call_env_parse_only(env->rule->flags)) return CALL_ENV_SUCCESS; vb = fr_value_box_list_head(tmpl_expanded); if (!vb) { if (!call_env_nullable(env->rule->flags)) { - RPEDEBUG("Failed to evaluate required module option %s = %s", env->rule->name, env->tmpl->name); + RPEDEBUG("Failed to evaluate required module option %s = %s", env->rule->name, env->data.tmpl->name); return CALL_ENV_MISSING; } return CALL_ENV_SUCCESS; @@ -71,16 +89,16 @@ call_env_result_t call_env_value_parse(TALLOC_CTX *ctx, request_t *request, void while ((vb = fr_value_box_list_pop_head(tmpl_expanded))) { switch (env->rule->pair.type) { - case CALL_ENV_TYPE_VALUE_BOX: + case CALL_ENV_RESULT_TYPE_VALUE_BOX: fr_value_box_copy_shallow(ctx, (fr_value_box_t *)(out), vb); break; - case CALL_ENV_TYPE_VALUE_BOX_LIST: + case CALL_ENV_RESULT_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; - case CALL_ENV_TYPE_TMPL_ONLY: + default: fr_assert(0); break; } @@ -116,13 +134,37 @@ static unlang_action_t call_env_expand_start(UNUSED rlm_rcode_t *p_result, UNUSE env = call_env_rctx->last_expanded; fr_assert(env != NULL); - if (!env->tmpl_only) break; + /* + * Subsections are expanded during parsing to produce a list of + * call_env_parsed_t. They are not expanded at runtime. + */ + fr_assert_msg(call_env_is_subsection(env->rule->flags) == false, "Subsections cannot be expanded at runtime"); /* - * If we only need the tmpl, just set the pointer and move the next. + * If there's an offset to copy the output to, do that. + * We may also need to expand the tmpl_t and write out the result + * to the pair offset. */ - out = (void **)((uint8_t *)*call_env_rctx->data + env->rule->parsed_offset); - *out = env->tmpl; + if (env->rule->pair.parsed.offset >= 0) { + /* + * If we only need the tmpl or data, just set the pointer and move the next. + */ + out = (void **)((uint8_t *)*call_env_rctx->data + env->rule->pair.parsed.offset); + switch (env->rule->pair.parsed.type) { + case CALL_ENV_PARSE_TYPE_TMPL: + *out = env->data.tmpl; + break; + + case CALL_ENV_PARSE_TYPE_VOID: + *out = env->data.ptr; + break; + } + } + + /* + * If this is not parse_only, we need to expand the tmpl. + */ + if (!call_env_parse_only(env->rule->flags)) break; } if (!call_env_rctx->last_expanded) { /* No more! */ @@ -138,7 +180,7 @@ static unlang_action_t call_env_expand_start(UNUSED rlm_rcode_t *p_result, UNUSE * Multi pair options should allocate boxes in the context of the array */ if (call_env_multi(env->rule->flags)) { - out = (void **)((uint8_t *)(*call_env_rctx->data) + env->rule->pair.result_offset); + out = (void **)((uint8_t *)(*call_env_rctx->data) + env->rule->pair.offset); /* * For multi pair options, allocate the array before expanding the first entry. @@ -152,7 +194,7 @@ static unlang_action_t call_env_expand_start(UNUSED rlm_rcode_t *p_result, UNUSE ctx = *out; } - if (unlang_tmpl_push(ctx, &call_env_rctx->tmpl_expanded, request, call_env_rctx->last_expanded->tmpl, + if (unlang_tmpl_push(ctx, &call_env_rctx->tmpl_expanded, request, call_env_rctx->last_expanded->data.tmpl, NULL) < 0) return UNLANG_ACTION_FAIL; return UNLANG_ACTION_PUSHED_CHILD; @@ -173,11 +215,11 @@ static unlang_action_t call_env_expand_repeat(UNUSED rlm_rcode_t *p_result, UNUS env = call_env_rctx->last_expanded; if (!env) return UNLANG_ACTION_CALCULATE_RESULT; - if (env->tmpl_only) goto tmpl_only; + if (call_env_parse_only(env->rule->flags)) goto parse_only; /* * Find the location of the output */ - out = ((uint8_t*)(*call_env_rctx->data)) + env->rule->pair.result_offset; + out = ((uint8_t*)(*call_env_rctx->data)) + env->rule->pair.offset; /* * If this is a multi pair option, the output is an array. @@ -188,10 +230,10 @@ static unlang_action_t call_env_expand_repeat(UNUSED rlm_rcode_t *p_result, UNUS out = ((uint8_t *)array) + env->rule->pair.size * env->multi_index; } -tmpl_only: - if (env->rule->parsed_offset >= 0) tmpl_out = ((uint8_t *)*call_env_rctx->data) + env->rule->parsed_offset; +parse_only: + if (env->rule->pair.parsed.offset >= 0) tmpl_out = ((uint8_t *)*call_env_rctx->data) + env->rule->pair.parsed.offset; - result = call_env_value_parse(*call_env_rctx->data, request, out, tmpl_out, env, &call_env_rctx->tmpl_expanded); + result = call_env_result(*call_env_rctx->data, request, out, tmpl_out, env, &call_env_rctx->tmpl_expanded); if (result != CALL_ENV_SUCCESS) { if (call_env_rctx->result) *call_env_rctx->result = result; return UNLANG_ACTION_FAIL; @@ -232,6 +274,53 @@ unlang_action_t call_env_expand(TALLOC_CTX *ctx, request_t *request, call_env_re call_env_rctx); } +/** Allocates a new call env parsed struct + * + */ +static inline CC_HINT(always_inline) +call_env_parsed_t *call_env_parsed_alloc(TALLOC_CTX *ctx, call_env_parser_t const *rule) +{ + call_env_parsed_t *call_env_parsed; + + MEM(call_env_parsed = talloc_zero(ctx, call_env_parsed_t)); + call_env_parsed->rule = rule; + call_env_parsed->count = 1; + call_env_parsed->multi_index = 0; + + return call_env_parsed; +} + +static inline CC_HINT(always_inline) +int call_env_parsed_valid(call_env_parsed_t const *parsed, CONF_ITEM const *ci, call_env_parser_t const *rule) +{ + tmpl_t *tmpl; + + if (rule->pair.parsed.type == CALL_ENV_PARSE_TYPE_VOID) return 0; + + tmpl = parsed->data.tmpl; + switch (tmpl->type) { + case TMPL_TYPE_DATA: + case TMPL_TYPE_EXEC: + case TMPL_TYPE_XLAT: + if (call_env_attribute(rule->flags)) { + cf_log_perr(ci, "'%s' expands to %s - attribute reference required", tmpl->name, + tmpl_type_to_str(tmpl->type)); + return -1; + } + FALL_THROUGH; + + case TMPL_TYPE_ATTR: + break; + + default: + cf_log_err(ci, "'%s' expands to invalid tmpl type %s", tmpl->name, + tmpl_type_to_str(tmpl->type)); + return -1; + } + + return 0; +} + /** Parse per call env * * Used for config options which must be parsed in the context in which @@ -240,42 +329,77 @@ unlang_action_t call_env_expand(TALLOC_CTX *ctx, request_t *request, call_env_re * @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] namespace we're operating in. * @param[in] cs Module config. - * @param[in] call_env to parse. + * @param[in] rule to parse. * @return * - 0 on success; * - <0 on failure; */ -static 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_parser_t const *call_env) { +static int call_env_parse(TALLOC_CTX *ctx, call_env_parsed_head_t *parsed, char const *name, fr_dict_t const *namespace, + CONF_SECTION const *cs, call_env_parser_t const *rule) { CONF_PAIR const *cp, *next; call_env_parsed_t *call_env_parsed; - ssize_t len, count, multi_index; - char const *value; - fr_token_t quote; + ssize_t count, multi_index; fr_type_t type; - while (call_env->name) { - if (call_env_is_subsection(call_env->flags)) { + while (rule->name) { + if (call_env_is_subsection(rule->flags)) { CONF_SECTION const *subcs; - subcs = cf_section_find(cs, call_env->name, call_env->section.ident2); + subcs = cf_section_find(cs, rule->name, rule->section.ident2); if (!subcs) { - if (!call_env_required(call_env->flags)) goto next; - cf_log_err(cs, "Module %s missing required section %s", name, call_env->name); + if (!call_env_required(rule->flags)) goto next; + cf_log_err(cs, "Module %s missing required section \"%s\"", name, rule->name); return -1; } - if (call_env_parse(ctx, parsed, name, dict_def, subcs, call_env->section.subcs) < 0) return -1; + /* + * Hand off to custom parsing function if there is one... + */ + if (rule->section.func) { + /* + * Record our position so we can process any new entries + * after the callback returns. + */ + call_env_parsed_t *last = call_env_parsed_tail(parsed); + + if (rule->section.func(ctx, parsed, namespace, cf_section_to_item(subcs), rule) < 0) { + cf_log_perr(cs, "Failed parsing configuration section %s", rule->name); + talloc_free(call_env_parsed); + return -1; + } + + call_env_parsed = last; + while ((call_env_parsed = call_env_parsed_prev(parsed, call_env_parsed))) { + if (call_env_parsed_valid(call_env_parsed, cf_section_to_item(subcs), rule) < 0) { + cf_log_err(cf_section_to_item(subcs), "Invalid data produced by %s", rule->name); + return -1; + } + count++; /* Get the total */ + } + + /* + * Now fixup the count and multi_index for each entry + * produced by the subsection callback. + */ + call_env_parsed = last; + while ((call_env_parsed = call_env_parsed_prev(parsed, call_env_parsed))) { + call_env_parsed->count = count; + call_env_parsed->multi_index = multi_index++; + } + goto next; + } + + if (call_env_parse(ctx, parsed, name, namespace, subcs, rule->section.subcs) < 0) return -1; goto next; } - cp = cf_pair_find(cs, call_env->name); + cp = cf_pair_find(cs, rule->name); - if (!cp && !call_env->pair.dflt) { - if (!call_env_required(call_env->flags)) goto next; + if (!cp && !rule->pair.dflt) { + if (!call_env_required(rule->flags)) goto next; - cf_log_err(cs, "Module %s missing required option %s", name, call_env->name); + cf_log_err(cs, "Module %s missing required option %s", name, rule->name); return -1; } @@ -283,76 +407,82 @@ static int call_env_parse(TALLOC_CTX *ctx, call_env_parsed_head_t *parsed, char * Check for additional conf pairs and error * if there is one and multi is not allowed. */ - if (!call_env_multi(call_env->flags) && ((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); + if (!call_env_multi(rule->flags) && ((next = cf_pair_find_next(cs, cp, rule->name)))) { + cf_log_err(cf_pair_to_item(next), "Invalid duplicate configuration item '%s'", rule->name); return -1; } - count = cf_pair_count(cs, call_env->name); + count = cf_pair_count(cs, rule->name); if (count == 0) count = 1; - for (multi_index = 0; multi_index < count; multi_index ++) { - MEM(call_env_parsed = talloc_zero(ctx, call_env_parsed_t)); - call_env_parsed->rule = call_env; + for (multi_index = 0; multi_index < count; multi_index++) { + CONF_PAIR *tmp_cp = NULL; + CONF_PAIR const *to_parse; + + call_env_parsed = call_env_parsed_alloc(ctx, rule); call_env_parsed->count = count; call_env_parsed->multi_index = multi_index; - if (call_env->pair.type == CALL_ENV_TYPE_TMPL_ONLY) call_env_parsed->tmpl_only = true; + /* + * With the conf_parser code we can add default pairs + * if they don't exist, but as the same CONF_SECTIONs + * are evaluated multiple times for each module call + * we can't do that here. + */ if (cp) { - value = cf_pair_value(cp); - len = talloc_array_length(value) - 1; - quote = call_env_force_quote(call_env->flags) ? call_env->pair.dflt_quote : cf_pair_value_quote(cp); + if (call_env_force_quote(rule->flags)) { + to_parse = tmp_cp = cf_pair_alloc(NULL, + cf_pair_attr(cp), cf_pair_value(cp), cf_pair_operator(cp), + cf_pair_attr_quote(cp), + call_env_force_quote(rule->flags) ? rule->pair.dflt_quote : cf_pair_value_quote(cp)); + } else { + to_parse = cp; + } } else { - value = call_env->pair.dflt; - len = strlen(value); - quote = call_env->pair.dflt_quote; - } - - type = call_env->pair.cast_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 configuration item '%s = %s'", call_env->name, value); - return -1; + to_parse = tmp_cp = cf_pair_alloc(NULL, + rule->name, rule->pair.dflt, T_OP_EQ, + T_BARE_WORD, rule->pair.dflt_quote); } /* - * Ensure only valid TMPL types are produced. + * The parsing function can either produce a tmpl_t as tmpl_afrom_substr + * would, or produce a custom structure, which will be copied into the + * result structure. */ - switch (call_env_parsed->tmpl->type) { - case TMPL_TYPE_DATA: - case TMPL_TYPE_EXEC: - case TMPL_TYPE_XLAT: - if (call_env_attribute(call_env->flags)) { - cf_log_perr(cp, "'%s' expands to %s - attribute reference required", value, - fr_table_str_by_value(tmpl_type_table, call_env_parsed->tmpl->type, - "")); + if (rule->pair.func) { + if (unlikely(rule->pair.func(ctx, &call_env_parsed->data, namespace, cf_pair_to_item(to_parse), rule) < 0)) { + error: + cf_log_perr(to_parse, "Failed to parse configuration item '%s = %s'", rule->name, cf_pair_value(to_parse)); + talloc_free(call_env_parsed); + talloc_free(tmp_cp); + return -1; + } + } else { + type = rule->pair.cast_type; + if (tmpl_afrom_substr(call_env_parsed, &call_env_parsed->data.tmpl, + &FR_SBUFF_IN(cf_pair_value(to_parse), talloc_array_length(cf_pair_value(to_parse)) - 1), + cf_pair_value_quote(to_parse), NULL, &(tmpl_rules_t){ + .cast = ((type == FR_TYPE_VOID) ? FR_TYPE_NULL : type), + .attr = { + .list_def = request_attr_request, + .dict_def = namespace + } + }) < 0) { goto error; } - FALL_THROUGH; - - case TMPL_TYPE_ATTR: - 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); + /* + * Ensure only valid data is produced. + */ + if (call_env_parsed_valid(call_env_parsed, cf_pair_to_item(to_parse), rule) < 0) goto error; - cp = cf_pair_find_next(cs, cp, call_env->name); + talloc_free(tmp_cp); + call_env_parsed_insert_tail(parsed, call_env_parsed); + cp = cf_pair_find_next(cs, cp, rule->name); } next: - call_env++; + rule++; } return 0; @@ -380,7 +510,10 @@ static size_t call_env_count(size_t *names_len, CONF_SECTION const *cs, call_env subcs = cf_section_find(cs, call_env->name, call_env->section.ident2); if (!subcs) goto next; - tmpl_count += call_env_count(names_len, subcs, call_env->section.subcs); + /* + * May only be a callback... + */ + if (call_env->section.subcs) tmpl_count += call_env_count(names_len, subcs, call_env->section.subcs); goto next; } pair_count = 0; @@ -401,6 +534,60 @@ static size_t call_env_count(size_t *names_len, CONF_SECTION const *cs, call_env return tmpl_count; } +/** Allocate a new call_env_parsed_t structure and add it to the list of parsed call envs + * + * @note tmpl_t and void * should be allocated in the context of the call_env_parsed_t + * + * @param[in] ctx to allocate the new call_env_parsed_t in. + * @param[out] head to add the new call_env_parsed_t to. + * @param[in] rule to base call_env_parsed_t around. MUST NOT BE THE RULE PASSED TO THE CALLBACK. + * The rule passed to the callback describes how to parse a subsection, but the + * subsection callback is adding rules describing how to parse its children. + * @return The new call_env_parsed_t. + */ +call_env_parsed_t *call_env_parsed_add(TALLOC_CTX *ctx, call_env_parsed_head_t *head, call_env_parser_t const *rule) +{ + call_env_parsed_t *call_env_parsed; + call_env_parser_t *our_rules; + + fr_assert_msg(call_env_is_subsection(rule->flags) == false, "Rules added by subsection callbacks cannot be subsections themselves"); + + MEM(call_env_parsed = call_env_parsed_alloc(ctx, rule)); + + /* + * Copy the rule the callback provided, there's no guarantee + * it's not stack allocated, or in some way ephemeral. + */ + MEM(our_rules = talloc(call_env_parsed, call_env_parser_t)); + memcpy(our_rules, rule, sizeof(*our_rules)); + call_env_parsed->rule = our_rules; + call_env_parsed_insert_tail(head, call_env_parsed); + + return call_env_parsed; +} + +/** Assign a tmpl to a call_env_parsed_t + * + * @param[in] parsed to assign the tmpl to. + * @param[in] tmpl to assign. + */ +void call_env_parsed_set_tmpl(call_env_parsed_t *parsed, tmpl_t *tmpl) +{ + fr_assert_msg(parsed->rule->pair.parsed.type == CALL_ENV_PARSE_TYPE_TMPL, "Rule must indicate parsed output is a tmpl_t"); + parsed->data.tmpl = tmpl; +}; + +/** Assign data to a call_env_parsed_t + * + * @param[in] parsed to assign the tmpl to. + * @param[in] data to assign. + */ +void call_env_parsed_set_data(call_env_parsed_t *parsed, void *data) +{ + fr_assert_msg(parsed->rule->pair.parsed.type == CALL_ENV_PARSE_TYPE_VOID, "Rule must indicate parsed output is a void *"); + parsed->data.ptr = data; +}; + /** Given a call_env_method, parse all call_env_pair_t in the context of a specific call to an xlat or module method * * @param[in] ctx to allocate the call_env_t in. diff --git a/src/lib/unlang/call_env.h b/src/lib/unlang/call_env.h index 5107dd349c99..d2dd9e562eb3 100644 --- a/src/lib/unlang/call_env.h +++ b/src/lib/unlang/call_env.h @@ -50,11 +50,19 @@ typedef enum { CALL_ENV_INVALID = -2 } call_env_result_t; +/** What type of structure is produced by the parsing phase + */ typedef enum { - CALL_ENV_TYPE_VALUE_BOX = 1, - CALL_ENV_TYPE_VALUE_BOX_LIST, - CALL_ENV_TYPE_TMPL_ONLY -} call_env_dst_t; + CALL_ENV_PARSE_TYPE_TMPL = 1, //!< Output of the parsing phase is a tmpl_t. + CALL_ENV_PARSE_TYPE_VOID //!< Output of the parsing phase is undefined (a custom structure). +} call_env_parse_type_t; + +/** What type of structure is produced by the evaluation phase + */ +typedef enum { + CALL_ENV_RESULT_TYPE_VALUE_BOX = 1, //!< Output of the evaluation phase is a single value box. + CALL_ENV_RESULT_TYPE_VALUE_BOX_LIST, //!< Output of the evaluation phase is a list of value boxes. +} call_env_result_type_t; DIAG_OFF(attributes) typedef enum CC_HINT(flag_enum) { @@ -69,8 +77,9 @@ typedef enum CC_HINT(flag_enum) { ///< where tmpls should always be parsed with a particular quoting ///< regardless of how they are in the config file. E.g. the `program` ///< option of `rlm_exec` should always be parsed as T_BACK_QUOTED_STRING. - CALL_ENV_FLAG_ATTRIBUTE = (1 << 6), //!< Tmpl must contain an attribute reference. - CALL_ENV_FLAG_SUBSECTION = (1 << 7) //!< This is a subsection. + CALL_ENV_FLAG_PARSE_ONLY = (1 << 6), //!< The result of parsing will not be evaluated at runtime. + CALL_ENV_FLAG_ATTRIBUTE = (1 << 7), //!< Tmpl must contain an attribute reference. + CALL_ENV_FLAG_SUBSECTION = (1 << 8) //!< This is a subsection. } call_env_flags_t; DIAG_ON(attributes) @@ -103,11 +112,43 @@ DIAG_ON(attributes) #define call_env_force_quote(_flags) ((_flags) & CALL_ENV_FLAG_FORCE_QUOTE) +#define call_env_parse_only(_flags) ((_flags) & CALL_ENV_FLAG_PARSE_ONLY) + #define call_env_attribute(_flags) ((_flags) & CALL_ENV_FLAG_ATTRIBUTE) #define call_env_is_subsection(_flags) ((_flags) & CALL_ENV_FLAG_SUBSECTION) /** @} */ +/** Callback for performing custom parsing of a #CONF_PAIR + * + * @param[in] ctx to allocate any data in. + * @param[out] out Where to write the result of parsing. + * @param[in] namespace we're operating in. + * @param[in] ci The #CONF_SECTION or #CONF_PAIR to parse. + * @param[in] rule Parse rules - How the #CONF_PAIR or #CONF_SECTION should be converted. + * @return + * - 0 on success. + * - -1 on failure. + */ +typedef int (*call_env_parse_pair_t)(TALLOC_CTX *ctx, void *out, fr_dict_t const *namespace, CONF_ITEM *ci, call_env_parser_t const *rule); + +/** Callback for performing custom parsing of a #CONF_SECTION + * + * The callback function is expected to call call_env_parsed_add to allocate a new + * call_env_parsed_t, and either call_env_parsed_set_tmpl, or call_env_parsed_set_data to populate + * the call env_parsed_t structure. + * + * @param[in] ctx to allocate any data in. + * @param[out] out Where to write the result of parsing. + * @param[in] namespace we're operating in. + * @param[in] ci The #CONF_SECTION or #CONF_PAIR to parse. + * @param[in] rule Parse rules - How the #CONF_PAIR or #CONF_SECTION should be converted. + * @return + * - 0 on success. + * - -1 on failure. + */ +typedef int (*call_env_parse_section_t)(TALLOC_CTX *ctx, call_env_parsed_head_t *out, fr_dict_t const *namespace, CONF_ITEM *ci, call_env_parser_t const *rule); + /** Per method call config * * Similar to a conf_parser_t used to hold details of conf pairs @@ -120,44 +161,40 @@ struct call_env_parser_s { char const *name; //!< Of conf pair to pass to tmpl_tokenizer. call_env_flags_t flags; //!< Flags controlling parser behaviour. - ssize_t parsed_offset; //!< Where to write the result of the parsing phase. - ///< This is usually a tmpl_t, but could be other things when a callback - ///< function is used to parse the CONF_SECTION or CONF_PAIR. - union { struct { - fr_type_t cast_type; //!< To cast boxes to. Also contains flags controlling parser behaviour. + fr_type_t cast_type; //!< To cast boxes to. Also contains flags controlling parser behaviour. + + call_env_result_type_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 offset; //!< Where to write the result of evaluating the tmpl_t produced in the parsing phase. + + char const *dflt; //!< Default string to pass to the tmpl_tokenizer if no CONF_PAIR found. + fr_token_t dflt_quote; //!< Quoting for the default string. - call_env_dst_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 result_offset; //!< Where to write the result of evaluating the tmpl_t produced in the parsing phase. + call_env_parse_pair_t func; //!< Callback for parsing a CONF_PAIR - char const *dflt; //!< Default string to pass to the tmpl_tokenizer if no CONF_PAIR found. - fr_token_t dflt_quote; //!< Quoting for the default string. + struct { + ssize_t offset; //!< Where to write the result of the parsing phase. + ///< This is usually a tmpl_t, but could be other things when a callback + ///< function is used to parse the CONF_SECTION or CONF_PAIR. + + call_env_parse_type_t type; //!< What type of output the parsing phase is expected to produce. + } parsed; } pair; struct { - char const *ident2; //!< Second identifier for a section - call_env_parser_t const *subcs; //!< Nested definitions for subsection. + char const *ident2; //!< Second identifier for a section + call_env_parser_t const *subcs; //!< Nested definitions for subsection. + + call_env_parse_section_t func; //!< Callback for parsing CONF_SECTION. } section; }; }; #define CALL_ENV_TERMINATOR { NULL } -struct call_env_parsed_s { - call_env_parsed_entry_t entry; //!< Entry in list of parsed call_env_parsers. - tmpl_t *tmpl; //!< Tmpl produced from parsing conf pair. - size_t count; //!< Number of CONF_PAIRs found, matching the #call_env_parser_t. - size_t multi_index; //!< Array index for this instance. - call_env_parser_t const *rule; //!< Used to produce this. - bool tmpl_only; //!< Don't evaluate before module / xlat call. - ///< Only the tmpl reference is needed. -}; - -FR_DLIST_FUNCS(call_env_parsed, call_env_parsed_t, entry) - /** Helper macro for populating the size/type fields of a #call_env_method_t from the output structure type */ #define FR_CALL_ENV_METHOD_OUT(_inst) \ @@ -177,6 +214,14 @@ struct call_env_s { call_env_method_t const *method; //!< The method this call env is for. }; +/** Where we're specifying a parsing phase output field, determine its type + */ +#define CALL_ENV_PARSE_TYPE(_s, _f) \ +_Generic((((_s *)NULL)->_f), \ + tmpl_t * : CALL_ENV_PARSE_TYPE_TMPL, \ + default : CALL_ENV_PARSE_TYPE_VOID \ +) + /** Derive whether tmpl can only emit a single box. */ #define FR_CALL_ENV_SINGLE(_s, _f, _c) \ @@ -184,7 +229,8 @@ _Generic((((_s *)NULL)->_f), \ fr_value_box_t : __builtin_choose_expr(_c, CALL_ENV_FLAG_NONE, CALL_ENV_FLAG_SINGLE), \ fr_value_box_t * : __builtin_choose_expr(_c, CALL_ENV_FLAG_NONE, CALL_ENV_FLAG_SINGLE), \ fr_value_box_list_t : CALL_ENV_FLAG_NONE, \ - fr_value_box_list_t * : CALL_ENV_FLAG_SINGLE \ + fr_value_box_list_t * : CALL_ENV_FLAG_SINGLE, \ + default : CALL_ENV_FLAG_NONE \ ) /** Derive whether multi conf pairs are allowed from target field type. @@ -208,10 +254,10 @@ __builtin_choose_expr(_c, (void)0, false))) */ #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 \ + fr_value_box_t : CALL_ENV_RESULT_TYPE_VALUE_BOX, \ + fr_value_box_t * : CALL_ENV_RESULT_TYPE_VALUE_BOX, \ + fr_value_box_list_t : CALL_ENV_RESULT_TYPE_VALUE_BOX_LIST, \ + fr_value_box_list_t * : CALL_ENV_RESULT_TYPE_VALUE_BOX_LIST \ ) #define FR_CALL_ENV_DST_SIZE(_s, _f) \ @@ -219,15 +265,17 @@ _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) \ + fr_value_box_list_t * : sizeof(fr_value_box_list_t), \ + default : 0 \ ) -#define FR_CALL_ENV_DST_TYPE_NAME(_s, _f) \ +#define FR_CALL_ENV_RESULT_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" \ + fr_value_box_list_t * : "fr_value_box_list_t", \ + default : NULL \ ) typedef void _mismatch_flags; //!< Dummy type used to indicate bad flags. @@ -238,43 +286,76 @@ typedef void _mismatch_flags; //!< Dummy type used to indicate bad flags. FR_CALL_ENV_MULTI(_struct, _field) |\ ((_flags) & ~CALL_ENV_FLAG_CONCAT)) \ +/** Specify a call_env_parser_t which writes out runtime results to the specified field + * + * @param[in] _name of the conf pair to parse. + * @param[in] _cast_type Cast any value boxes produced to this type. + * @param[in] _flags controlling parser behaviour. + * @param[in] _struct which contains the field to write the result of the evaluation phase to. + * @param[in] _field where to write the result. + */ #define FR_CALL_ENV_OFFSET(_name, _cast_type, _flags, _struct, _field) \ .name = _name, \ .flags = CALL_ENV_FLAGS(_cast_type, _flags, _struct, _field), \ - .parsed_offset = -1, \ .pair = { \ .cast_type = _cast_type, \ .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), \ - .result_offset = offsetof(_struct, _field), \ + .type_name = FR_CALL_ENV_RESULT_TYPE_NAME(_struct, _field), \ + .offset = offsetof(_struct, _field), \ + .parsed = { \ + .offset = -1, \ + .type = CALL_ENV_PARSE_TYPE_TMPL \ + } \ } -/** Version of the above which sets optional field for pointer to tmpl +/** Specify a call_env_parser_t which writes out runtime results and the result of the parsing phase to the fields specified + * + * @param[in] _name of the conf pair to parse. + * @param[in] _cast_type Cast any value boxes produced to this type. + * @param[in] _flags controlling parser behaviour. + * @param[in] _struct which contains the field to write the result of the evaluation phase to. + * @param[in] _field where to write the result. + * @param[in] _parse_field where to write the result of the parsing phase. + * This must be a field in the specified _struct. */ -#define FR_CALL_ENV_TMPL_OFFSET(_name, _cast_type, _flags, _struct, _field, _tmpl_field) \ +#define FR_CALL_ENV_PARSE_OFFSET(_name, _cast_type, _flags, _struct, _field, _parse_field) \ .name = _name, \ .flags = CALL_ENV_FLAGS(_cast_type, _flags, _struct, _field), \ - .parsed_offset = offsetof(_struct, _tmpl_field), \ .pair = { \ .cast_type = _cast_type, \ .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), \ - .result_offset = offsetof(_struct, _field), \ + .type_name = FR_CALL_ENV_RESULT_TYPE_NAME(_struct, _field), \ + .offset = offsetof(_struct, _field), \ + .parsed = { \ + .offset = offsetof(_struct, _parse_field), \ + .type = CALL_ENV_PARSE_TYPE(_struct, _parse_field) \ + } \ } -/** Version of the above which only sets the field for a pointer to the tmpl +/** Specify a call_env_parser_t which writes out the result of the parsing phase to the field specified + * + * @param[in] _name of the conf pair to parse. + * @param[in] _cast_type Sets the cast used by the tmpl. + * @param[in] _flags controlling parser behaviour. + * @param[in] _struct which contains the field to write the result of the evaluation phase to. + * @param[in] _parse_field where to write the result of the parsing phase. + * This must be a field in the specified _struct. */ -#define FR_CALL_ENV_TMPL_ONLY_OFFSET(_name, _cast_type, _flags, _struct, _tmpl_field) \ +#define FR_CALL_ENV_PARSE_ONLY_OFFSET(_name, _cast_type, _flags, _struct, _parse_field) \ .name = _name, \ - .flags = _flags, \ - .parsed_offset = offsetof(_struct, _tmpl_field), \ + .flags = (_flags) | CALL_ENV_FLAG_PARSE_ONLY, \ .pair = { \ .cast_type = _cast_type, \ - .type = CALL_ENV_TYPE_TMPL_ONLY \ + .parsed = { \ + .offset = offsetof(_struct, _parse_field), \ + .type = CALL_ENV_PARSE_TYPE(_struct, _parse_field) \ + } \ } +/** Specify a call_env_parser_t which defines a nested subsection + */ #define FR_CALL_ENV_SUBSECTION(_name, _ident2, _flags, _subcs ) \ .name = _name, \ .flags = CALL_ENV_FLAG_SUBSECTION | (_flags), \ @@ -283,8 +364,24 @@ typedef void _mismatch_flags; //!< Dummy type used to indicate bad flags. .subcs = _subcs, \ } +/** Specify a call_env_parser_t which parses a subsection using a callback function + */ +#define FR_CALL_ENV_SUBSECTION_FUNC(_name, _ident2, _flags, _func) \ + .name = _name, \ + .flags = CALL_ENV_FLAG_SUBSECTION | (_flags), \ + .section = { \ + .ident2 = _ident2, \ + .func = _func \ + } + unlang_action_t call_env_expand(TALLOC_CTX *ctx, request_t *request, call_env_result_t *result, void **env_data, call_env_t const *call_env); +call_env_parsed_t *call_env_parsed_add(TALLOC_CTX *ctx, call_env_parsed_head_t *head, call_env_parser_t const *rule); + +void call_env_parsed_set_tmpl(call_env_parsed_t *parsed, tmpl_t *tmpl); + +void call_env_parsed_set_data(call_env_parsed_t *parsed, void *data); + call_env_t *call_env_alloc(TALLOC_CTX *ctx, char const *name, call_env_method_t const *call_env_method, fr_dict_t const *namespace, CONF_SECTION *cs) CC_HINT(nonnull(3,4,5)); diff --git a/src/modules/rlm_cache/rlm_cache.c b/src/modules/rlm_cache/rlm_cache.c index 6793f11d674f..d4676b1077ed 100644 --- a/src/modules/rlm_cache/rlm_cache.c +++ b/src/modules/rlm_cache/rlm_cache.c @@ -38,6 +38,8 @@ RCSID("$Id$") extern module_rlm_t rlm_cache; +static int update_section_parse(TALLOC_CTX *ctx, call_env_parsed_head_t *out, fr_dict_t const *namespace, CONF_ITEM *ci, UNUSED call_env_parser_t const *rule); + static const conf_parser_t module_config[] = { { FR_CONF_OFFSET_TYPE_FLAGS("driver", FR_TYPE_VOID, 0, rlm_cache_t, driver_submodule), .dflt = "rbtree", .func = module_rlm_submodule_parse }, @@ -51,13 +53,15 @@ static const conf_parser_t module_config[] = { }; typedef struct { - fr_value_box_t *key; + fr_value_box_t *key; //!< To lookup the cache entry with. + map_list_t *maps; //!< Attribute map applied to cache entries. } cache_call_env_t; static const call_env_method_t cache_method_env = { FR_CALL_ENV_METHOD_OUT(cache_call_env_t), .env = (call_env_parser_t[]) { { FR_CALL_ENV_OFFSET("key", FR_TYPE_STRING, CALL_ENV_FLAG_REQUIRED | CALL_ENV_FLAG_CONCAT, cache_call_env_t, key) }, + { FR_CALL_ENV_SUBSECTION_FUNC("update", CF_IDENT_ANY, CALL_ENV_FLAG_REQUIRED, update_section_parse) }, CALL_ENV_TERMINATOR } }; @@ -313,7 +317,7 @@ static unlang_action_t cache_expire(rlm_rcode_t *p_result, */ static unlang_action_t cache_insert(rlm_rcode_t *p_result, rlm_cache_t const *inst, request_t *request, rlm_cache_handle_t **handle, - fr_value_box_t const *key, fr_time_delta_t ttl) + fr_value_box_t const *key, map_list_t const *maps, fr_time_delta_t ttl) { map_t const *map = NULL; map_t *c_map; @@ -354,7 +358,7 @@ static unlang_action_t cache_insert(rlm_rcode_t *p_result, * gathering fr_pair_ts to cache. */ pool = talloc_pool(NULL, 2048); - while ((map = map_list_next(&inst->maps, map))) { + while ((map = map_list_next(maps, map))) { fr_pair_list_t to_cache; fr_pair_list_init(&to_cache); @@ -528,21 +532,6 @@ static unlang_action_t cache_set_ttl(rlm_rcode_t *p_result, } } -/** Verify that a map in the cache section makes sense - * - */ -static int cache_verify(map_t *map, void *ctx) -{ - if (unlang_fixup_update(map, ctx) < 0) return -1; - - if (!tmpl_is_attr(map->lhs)) { - cf_log_err(map->ci, "Destination must be an attribute ref or a list"); - return -1; - } - - return 0; -} - /** Do caching checks * * Since we can update ANY VP list, we do exactly the same thing for all sections @@ -756,7 +745,7 @@ static unlang_action_t CC_HINT(nonnull) mod_cache_it(rlm_rcode_t *p_result, modu if (insert && (exists == 0)) { rlm_rcode_t tmp; - cache_insert(&tmp, inst, request, &handle, env->key, ttl); + cache_insert(&tmp, inst, request, &handle, env->key, env->maps, ttl); switch (tmp) { case RLM_MODULE_FAIL: rcode = RLM_MODULE_FAIL; @@ -1097,7 +1086,7 @@ static unlang_action_t CC_HINT(nonnull) mod_method_store(rlm_rcode_t *p_result, * setting the TTL, which precludes performing an * insert. */ - cache_insert(&rcode, inst, request, &handle, env->key, ttl); + cache_insert(&rcode, inst, request, &handle, env->key, env->maps, ttl); if (rcode == RLM_MODULE_OK) rcode = RLM_MODULE_UPDATED; finish: @@ -1238,30 +1227,31 @@ static int mod_detach(module_detach_ctx_t const *mctx) return 0; } -/** Create a new rlm_cache_instance +/** Verify that a map in the cache section makes sense * */ -static int mod_instantiate(module_inst_ctx_t const *mctx) +static int cache_verify(map_t *map, void *uctx) { - rlm_cache_t *inst = talloc_get_type_abort(mctx->inst->data, rlm_cache_t); - CONF_SECTION *conf = mctx->inst->conf; - CONF_SECTION *update; + if (unlang_fixup_update(map, uctx) < 0) return -1; - if (!fr_time_delta_ispos(inst->config.ttl)) { - cf_log_err(conf, "Must set 'ttl' to non-zero"); + if (!tmpl_is_attr(map->lhs)) { + cf_log_err(map->ci, "Destination must be an attribute ref or a list"); return -1; } - if (inst->config.epoch != 0) { - cf_log_err(conf, "Must not set 'epoch' in the configuration files"); - return -1; - } + return 0; +} - update = cf_section_find(conf, "update", CF_IDENT_ANY); - if (!update) { - cf_log_err(conf, "Must have an 'update' section in order to cache anything"); - return -1; - } +static int update_section_parse(TALLOC_CTX *ctx, call_env_parsed_head_t *out, fr_dict_t const *namespace, CONF_ITEM *ci, UNUSED call_env_parser_t const *rule) +{ + map_list_t *maps; + CONF_SECTION *update = cf_item_to_section(ci); + call_env_parsed_t *call_env_parsed; + + MEM(call_env_parsed = call_env_parsed_add(ctx, out, + &(call_env_parser_t){ FR_CALL_ENV_PARSE_ONLY_OFFSET("update", FR_TYPE_VOID, 0, cache_call_env_t, maps)})); + + MEM(maps = talloc_zero(call_env_parsed, map_list_t)); /* * Make sure the users don't screw up too badly. @@ -1269,22 +1259,45 @@ static int mod_instantiate(module_inst_ctx_t const *mctx) { tmpl_rules_t parse_rules = { .attr = { + .dict_def = namespace, .list_def = request_attr_request, .allow_wildcard = true, .allow_foreign = true /* Because we don't know where we'll be called */ } }; - map_list_init(&inst->maps); - if (map_afrom_cs(inst, &inst->maps, update, - &parse_rules, &parse_rules, cache_verify, inst, MAX_ATTRMAP) < 0) { + map_list_init(maps); + if (map_afrom_cs(ctx, maps, update, + &parse_rules, &parse_rules, cache_verify, NULL, MAX_ATTRMAP) < 0) { return -1; } } - if (map_list_empty(&inst->maps)) { - cf_log_err(conf, "Cache config must contain an update section, and " - "that section must not be empty"); + if (map_list_empty(maps)) { + cf_log_err(update, "Update section must not be empty"); + return -1; + } + + call_env_parsed_set_data(call_env_parsed, maps); + + return 0; +} + +/** Create a new rlm_cache_instance + * + */ +static int mod_instantiate(module_inst_ctx_t const *mctx) +{ + rlm_cache_t *inst = talloc_get_type_abort(mctx->inst->data, rlm_cache_t); + CONF_SECTION *conf = mctx->inst->conf; + + if (!fr_time_delta_ispos(inst->config.ttl)) { + cf_log_err(conf, "Must set 'ttl' to non-zero"); + return -1; + } + + if (inst->config.epoch != 0) { + cf_log_err(conf, "Must not set 'epoch' in the configuration files"); return -1; } diff --git a/src/modules/rlm_cache/rlm_cache.h b/src/modules/rlm_cache/rlm_cache.h index 3802cbc59a04..4ca7ac216f88 100644 --- a/src/modules/rlm_cache/rlm_cache.h +++ b/src/modules/rlm_cache/rlm_cache.h @@ -67,9 +67,6 @@ typedef struct { module_instance_t *driver_submodule; //!< Driver's instance data. rlm_cache_driver_t const *driver; //!< Driver's exported interface. - - map_list_t maps; //!< Attribute map applied to users. - //!< and profiles. } rlm_cache_t; typedef struct { diff --git a/src/modules/rlm_chap/rlm_chap.c b/src/modules/rlm_chap/rlm_chap.c index 1c929626c74d..166437cc2a7a 100644 --- a/src/modules/rlm_chap/rlm_chap.c +++ b/src/modules/rlm_chap/rlm_chap.c @@ -71,7 +71,7 @@ static const call_env_method_t chap_autz_method_env = { \ CALL_ENV_FLAG_ATTRIBUTE | CALL_ENV_FLAG_REQUIRED | CALL_ENV_FLAG_NULLABLE | CALL_ENV_FLAG_CONCAT, chap_autz_call_env_t, chap_password), .pair.dflt = "&Chap-Password", .pair.dflt_quote = T_BARE_WORD }, - { FR_CALL_ENV_TMPL_OFFSET("chap_challenge", FR_TYPE_OCTETS, + { FR_CALL_ENV_PARSE_OFFSET("chap_challenge", FR_TYPE_OCTETS, CALL_ENV_FLAG_ATTRIBUTE | CALL_ENV_FLAG_REQUIRED | CALL_ENV_FLAG_NULLABLE | CALL_ENV_FLAG_CONCAT, chap_autz_call_env_t, chap_challenge, chap_challenge_tmpl), .pair.dflt = "&Chap-Challenge", .pair.dflt_quote = T_BARE_WORD }, diff --git a/src/modules/rlm_exec/rlm_exec.c b/src/modules/rlm_exec/rlm_exec.c index 81c9fd92d782..ef2c01130f4f 100644 --- a/src/modules/rlm_exec/rlm_exec.c +++ b/src/modules/rlm_exec/rlm_exec.c @@ -72,7 +72,7 @@ typedef struct { static const call_env_method_t exec_method_env = { FR_CALL_ENV_METHOD_OUT(exec_call_env_t), .env = (call_env_parser_t[]){ - { FR_CALL_ENV_TMPL_ONLY_OFFSET("program", FR_TYPE_STRING, CALL_ENV_FLAG_FORCE_QUOTE, exec_call_env_t, program), .pair.dflt_quote = T_BACK_QUOTED_STRING }, + { FR_CALL_ENV_PARSE_ONLY_OFFSET("program", FR_TYPE_STRING, CALL_ENV_FLAG_FORCE_QUOTE, exec_call_env_t, program), .pair.dflt_quote = T_BACK_QUOTED_STRING }, CALL_ENV_TERMINATOR } }; diff --git a/src/modules/rlm_ldap/rlm_ldap.c b/src/modules/rlm_ldap/rlm_ldap.c index 386f2d1f71de..42050b1c8e67 100644 --- a/src/modules/rlm_ldap/rlm_ldap.c +++ b/src/modules/rlm_ldap/rlm_ldap.c @@ -179,7 +179,7 @@ static const call_env_method_t authenticate_method_env = { { FR_CALL_ENV_SUBSECTION("user", NULL, CALL_ENV_FLAG_REQUIRED, ((call_env_parser_t[]) { USER_CALL_ENV_COMMON(ldap_auth_call_env_t), - { FR_CALL_ENV_TMPL_OFFSET("password_attribute", FR_TYPE_STRING, + { FR_CALL_ENV_PARSE_OFFSET("password_attribute", FR_TYPE_STRING, CALL_ENV_FLAG_ATTRIBUTE | CALL_ENV_FLAG_REQUIRED | CALL_ENV_FLAG_NULLABLE | CALL_ENV_FLAG_CONCAT, ldap_auth_call_env_t, password, password_tmpl), .pair.dflt = "&User-Password", .pair.dflt_quote = T_BARE_WORD }, diff --git a/src/modules/rlm_mschap/rlm_mschap.c b/src/modules/rlm_mschap/rlm_mschap.c index 9691ec7e9af8..bd8596148726 100644 --- a/src/modules/rlm_mschap/rlm_mschap.c +++ b/src/modules/rlm_mschap/rlm_mschap.c @@ -143,15 +143,15 @@ static const call_env_method_t mschap_ ## _x ## _method_env = { \ } #define MSCHAP_COMMON_CALL_ENV(_x) \ -{ FR_CALL_ENV_TMPL_ONLY_OFFSET("chap_challenge", FR_TYPE_OCTETS, CALL_ENV_FLAG_ATTRIBUTE | CALL_ENV_FLAG_REQUIRED, mschap_ ## _x ## _call_env_t, chap_challenge), \ +{ FR_CALL_ENV_PARSE_ONLY_OFFSET("chap_challenge", FR_TYPE_OCTETS, CALL_ENV_FLAG_ATTRIBUTE | CALL_ENV_FLAG_REQUIRED, mschap_ ## _x ## _call_env_t, chap_challenge), \ .pair.dflt = "&Vendor-Specific.Microsoft.CHAP-Challenge", .pair.dflt_quote = T_BARE_WORD }, \ -{ FR_CALL_ENV_TMPL_ONLY_OFFSET("chap_response", FR_TYPE_OCTETS, CALL_ENV_FLAG_ATTRIBUTE | CALL_ENV_FLAG_REQUIRED, mschap_ ## _x ## _call_env_t, chap_response), \ +{ FR_CALL_ENV_PARSE_ONLY_OFFSET("chap_response", FR_TYPE_OCTETS, CALL_ENV_FLAG_ATTRIBUTE | CALL_ENV_FLAG_REQUIRED, mschap_ ## _x ## _call_env_t, chap_response), \ .pair.dflt = "&Vendor-Specific.Microsoft.CHAP-Response", .pair.dflt_quote = T_BARE_WORD }, \ -{ FR_CALL_ENV_TMPL_ONLY_OFFSET("chap2_response", FR_TYPE_OCTETS, CALL_ENV_FLAG_ATTRIBUTE | CALL_ENV_FLAG_REQUIRED, mschap_ ## _x ## _call_env_t, chap2_response), \ +{ FR_CALL_ENV_PARSE_ONLY_OFFSET("chap2_response", FR_TYPE_OCTETS, CALL_ENV_FLAG_ATTRIBUTE | CALL_ENV_FLAG_REQUIRED, mschap_ ## _x ## _call_env_t, chap2_response), \ .pair.dflt = "&Vendor-Specific.Microsoft.CHAP2-Response", .pair.dflt_quote = T_BARE_WORD } #define MSCHAP_OPT_CALL_ENV(_opt, _x) \ -{ FR_CALL_ENV_TMPL_ONLY_OFFSET(STRINGIFY(_opt), FR_TYPE_OCTETS, CALL_ENV_FLAG_ATTRIBUTE, mschap_ ## _x ## _call_env_t, _opt) } +{ FR_CALL_ENV_PARSE_ONLY_OFFSET(STRINGIFY(_opt), FR_TYPE_OCTETS, CALL_ENV_FLAG_ATTRIBUTE, mschap_ ## _x ## _call_env_t, _opt) } typedef struct { tmpl_t const *username; @@ -161,7 +161,7 @@ typedef struct { } mschap_xlat_call_env_t; static const call_env_parser_t xlat_call_env[] = { - { FR_CALL_ENV_TMPL_ONLY_OFFSET("username", FR_TYPE_STRING, CALL_ENV_FLAG_ATTRIBUTE | CALL_ENV_FLAG_REQUIRED, mschap_xlat_call_env_t, username), .pair.dflt = "&User-Name", .pair.dflt_quote = T_BARE_WORD }, + { FR_CALL_ENV_PARSE_ONLY_OFFSET("username", FR_TYPE_STRING, CALL_ENV_FLAG_ATTRIBUTE | CALL_ENV_FLAG_REQUIRED, mschap_xlat_call_env_t, username), .pair.dflt = "&User-Name", .pair.dflt_quote = T_BARE_WORD }, MSCHAP_COMMON_CALL_ENV(xlat), CALL_ENV_TERMINATOR }; @@ -169,7 +169,7 @@ static const call_env_parser_t xlat_call_env[] = { MSCHAP_CALL_ENV(xlat); static const call_env_parser_t auth_call_env[] = { - { FR_CALL_ENV_TMPL_ONLY_OFFSET("username", FR_TYPE_STRING, CALL_ENV_FLAG_ATTRIBUTE | CALL_ENV_FLAG_REQUIRED, mschap_auth_call_env_t, username), .pair.dflt = "&User-Name", .pair.dflt_quote = T_BARE_WORD }, + { FR_CALL_ENV_PARSE_ONLY_OFFSET("username", FR_TYPE_STRING, CALL_ENV_FLAG_ATTRIBUTE | CALL_ENV_FLAG_REQUIRED, mschap_auth_call_env_t, username), .pair.dflt = "&User-Name", .pair.dflt_quote = T_BARE_WORD }, MSCHAP_COMMON_CALL_ENV(auth), MSCHAP_OPT_CALL_ENV(chap2_success, auth), MSCHAP_OPT_CALL_ENV(chap_mppe_keys, auth), diff --git a/src/modules/rlm_pap/rlm_pap.c b/src/modules/rlm_pap/rlm_pap.c index 215d815d0073..827675a08bbe 100644 --- a/src/modules/rlm_pap/rlm_pap.c +++ b/src/modules/rlm_pap/rlm_pap.c @@ -91,7 +91,7 @@ static const call_env_method_t pap_method_env = { .inst_size = sizeof(pap_call_env_t), .inst_type = "pap_call_env_t", .env = (call_env_parser_t[]) { - { FR_CALL_ENV_TMPL_OFFSET("password_attribute", FR_TYPE_STRING, + { FR_CALL_ENV_PARSE_OFFSET("password_attribute", FR_TYPE_STRING, CALL_ENV_FLAG_ATTRIBUTE | CALL_ENV_FLAG_REQUIRED | CALL_ENV_FLAG_NULLABLE | CALL_ENV_FLAG_CONCAT, pap_call_env_t, password, password_tmpl), .pair.dflt = "&User-Password", .pair.dflt_quote = T_BARE_WORD }, CALL_ENV_TERMINATOR diff --git a/src/modules/rlm_redis_ippool/rlm_redis_ippool.c b/src/modules/rlm_redis_ippool/rlm_redis_ippool.c index ac864315a6ed..8e499b09b5b9 100644 --- a/src/modules/rlm_redis_ippool/rlm_redis_ippool.c +++ b/src/modules/rlm_redis_ippool/rlm_redis_ippool.c @@ -194,10 +194,10 @@ static const call_env_method_t redis_ippool_alloc_method_env = { { FR_CALL_ENV_OFFSET("lease_time", FR_TYPE_UINT32, CALL_ENV_FLAG_REQUIRED, redis_ippool_alloc_call_env_t, lease_time) }, { FR_CALL_ENV_OFFSET("requested_address", FR_TYPE_STRING, CALL_ENV_FLAG_REQUIRED | CALL_ENV_FLAG_NULLABLE, redis_ippool_alloc_call_env_t, requested_address ), .pair.dflt = "%{%{Requested-IP-Address} || %{Net.Src.IP}}", .pair.dflt_quote = T_DOUBLE_QUOTED_STRING }, - { FR_CALL_ENV_TMPL_ONLY_OFFSET("allocated_address_attr", FR_TYPE_VOID, CALL_ENV_FLAG_ATTRIBUTE | CALL_ENV_FLAG_REQUIRED, redis_ippool_alloc_call_env_t, allocated_address_attr) }, - { FR_CALL_ENV_TMPL_ONLY_OFFSET("range_attr", FR_TYPE_VOID, CALL_ENV_FLAG_ATTRIBUTE | CALL_ENV_FLAG_REQUIRED, redis_ippool_alloc_call_env_t, range_attr), + { FR_CALL_ENV_PARSE_ONLY_OFFSET("allocated_address_attr", FR_TYPE_VOID, CALL_ENV_FLAG_ATTRIBUTE | CALL_ENV_FLAG_REQUIRED, redis_ippool_alloc_call_env_t, allocated_address_attr) }, + { FR_CALL_ENV_PARSE_ONLY_OFFSET("range_attr", FR_TYPE_VOID, CALL_ENV_FLAG_ATTRIBUTE | CALL_ENV_FLAG_REQUIRED, redis_ippool_alloc_call_env_t, range_attr), .pair.dflt = "&reply.IP-Pool.Range", .pair.dflt_quote = T_BARE_WORD }, - { FR_CALL_ENV_TMPL_ONLY_OFFSET("expiry_attr", FR_TYPE_VOID, CALL_ENV_FLAG_ATTRIBUTE, redis_ippool_alloc_call_env_t, expiry_attr) }, + { FR_CALL_ENV_PARSE_ONLY_OFFSET("expiry_attr", FR_TYPE_VOID, CALL_ENV_FLAG_ATTRIBUTE, redis_ippool_alloc_call_env_t, expiry_attr) }, CALL_ENV_TERMINATOR } }; @@ -212,10 +212,10 @@ static const call_env_method_t redis_ippool_update_method_env = { { FR_CALL_ENV_OFFSET("lease_time", FR_TYPE_UINT32, CALL_ENV_FLAG_REQUIRED, redis_ippool_update_call_env_t, lease_time) }, { FR_CALL_ENV_OFFSET("requested_address", FR_TYPE_STRING, CALL_ENV_FLAG_REQUIRED | CALL_ENV_FLAG_NULLABLE, redis_ippool_update_call_env_t, requested_address), .pair.dflt = "%{%{Requested-IP-Address} || %{Net.Src.IP}}", .pair.dflt_quote = T_DOUBLE_QUOTED_STRING }, - { FR_CALL_ENV_TMPL_ONLY_OFFSET("allocated_address_attr", FR_TYPE_VOID, CALL_ENV_FLAG_ATTRIBUTE | CALL_ENV_FLAG_REQUIRED, redis_ippool_update_call_env_t, allocated_address_attr) }, - { FR_CALL_ENV_TMPL_ONLY_OFFSET("range_attr", FR_TYPE_VOID, CALL_ENV_FLAG_ATTRIBUTE | CALL_ENV_FLAG_REQUIRED, redis_ippool_update_call_env_t, range_attr), + { FR_CALL_ENV_PARSE_ONLY_OFFSET("allocated_address_attr", FR_TYPE_VOID, CALL_ENV_FLAG_ATTRIBUTE | CALL_ENV_FLAG_REQUIRED, redis_ippool_update_call_env_t, allocated_address_attr) }, + { FR_CALL_ENV_PARSE_ONLY_OFFSET("range_attr", FR_TYPE_VOID, CALL_ENV_FLAG_ATTRIBUTE | CALL_ENV_FLAG_REQUIRED, redis_ippool_update_call_env_t, range_attr), .pair.dflt = "&reply.IP-Pool.Range", .pair.dflt_quote = T_BARE_WORD }, - { FR_CALL_ENV_TMPL_ONLY_OFFSET("expiry_attr", FR_TYPE_VOID, CALL_ENV_FLAG_ATTRIBUTE, redis_ippool_update_call_env_t, expiry_attr) }, + { FR_CALL_ENV_PARSE_ONLY_OFFSET("expiry_attr", FR_TYPE_VOID, CALL_ENV_FLAG_ATTRIBUTE, redis_ippool_update_call_env_t, expiry_attr) }, CALL_ENV_TERMINATOR } }; diff --git a/src/modules/rlm_smtp/rlm_smtp.c b/src/modules/rlm_smtp/rlm_smtp.c index 18b862d2327a..8fc2b74c23be 100644 --- a/src/modules/rlm_smtp/rlm_smtp.c +++ b/src/modules/rlm_smtp/rlm_smtp.c @@ -1181,7 +1181,7 @@ static int mod_thread_detach(module_thread_inst_ctx_t const *mctx) static const call_env_method_t method_env = { FR_CALL_ENV_METHOD_OUT(rlm_smtp_env_t), .env = (call_env_parser_t[]) { - { FR_CALL_ENV_TMPL_OFFSET("username", FR_TYPE_STRING, CALL_ENV_FLAG_NULLABLE | CALL_ENV_FLAG_CONCAT, rlm_smtp_env_t, username, username_tmpl), + { FR_CALL_ENV_PARSE_OFFSET("username", FR_TYPE_STRING, CALL_ENV_FLAG_NULLABLE | CALL_ENV_FLAG_CONCAT, rlm_smtp_env_t, username, username_tmpl), .pair.dflt_quote = T_DOUBLE_QUOTED_STRING }, { FR_CALL_ENV_OFFSET("password", FR_TYPE_STRING, CALL_ENV_FLAG_NULLABLE | CALL_ENV_FLAG_CONCAT, rlm_smtp_env_t, password), .pair.dflt_quote = T_DOUBLE_QUOTED_STRING }, CALL_ENV_TERMINATOR