From: Arran Cudbard-Bell Date: Fri, 7 Jun 2024 00:30:52 +0000 (-0400) Subject: Use new module method resolution function X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=2ecede2beac9d13b37af91fb0e1c05f3edcd76f9;p=thirdparty%2Ffreeradius-server.git Use new module method resolution function This supports static and dynamic modules Pass in call_env_ctx_t to parsing callbacks instead of the section name, module instance data directly --- diff --git a/src/lib/server/module_rlm.c b/src/lib/server/module_rlm.c index c5282605065..d2fc96d964c 100644 --- a/src/lib/server/module_rlm.c +++ b/src/lib/server/module_rlm.c @@ -21,10 +21,11 @@ * @brief Defines functions for rlm module (re-)initialisation. * * @copyright 2003,2006,2016 The FreeRADIUS server project - * @copyright 2016 Arran Cudbard-Bell (a.cudbardb@freeradius.org) + * @copyright 2016,2024 Arran Cudbard-Bell (a.cudbardb@freeradius.org) * @copyright 2000 Alan DeKok (aland@freeradius.org) * @copyright 2000 Alan Curry (pacman@world.std.com) */ + RCSID("$Id$") #include @@ -35,16 +36,27 @@ RCSID("$Id$") #include #include #include +#include +#include #include #include #include #include +#include +#include +#include +#include +#include +#include #include + #include #include +#include + /** Lookup virtual module by name */ static fr_rb_tree_t *module_rlm_virtual_name_tree; @@ -242,6 +254,41 @@ int module_rlm_sibling_section_find(CONF_SECTION **out, CONF_SECTION *module, ch return 1; } +xlat_t *module_rlm_xlat_register(TALLOC_CTX *ctx, module_inst_ctx_t const *mctx, + char const *name, xlat_func_t func, fr_type_t return_type) +{ + module_instance_t *mi = mctx->mi; + module_rlm_instance_t *mri = talloc_get_type_abort(mi->uctx, module_rlm_instance_t); + module_rlm_xlat_t *mrx; + xlat_t *x; + char inst_name[256]; + + fr_assert_msg(name != mctx->mi->name, "`name` must not be the same as the module " + "instance name. Pass a NULL `name` arg if this is required"); + + if (!name) { + name = mctx->mi->name; + } else { + if ((size_t)snprintf(inst_name, sizeof(inst_name), "%s.%s", mctx->mi->name, name) >= sizeof(inst_name)) { + ERROR("%s: Instance name too long", __FUNCTION__); + return NULL; + } + name = inst_name; + } + + x = xlat_func_register(ctx, name, func, return_type); + if (unlikely(x == NULL)) return NULL; + + xlat_mctx_set(x, mctx); + + MEM(mrx = talloc(mi, module_rlm_xlat_t)); + mrx->xlat = x; + + fr_rb_insert(&mri->xlats, mrx); + + return x; +} + /** Initialise a module specific connection pool * * @see fr_pool_init @@ -426,361 +473,347 @@ bool module_rlm_section_type_set(request_t *request, fr_dict_attr_t const *type_ } } -/** Find an existing module instance and verify it implements the specified method - * - * Extracts the method from the module name where the format is @verbatim . @endverbatim - * and ensures the module implements the specified method. +/** Iterate over an array of named module methods, looking for matches * - * @param[out] method the method function we will call - * @param[out] method_env the module_call_env to evaluate when compiling the method. - * @param[out] name1 name1 of the method being called - * @param[out] name2 name2 of the method being called - * @param[in] name The name of the module we're attempting to find, possibly concatenated with the method + * @param[in] bindings A terminated array of module method bindings. + * pre-sorted using #section_name_cmp with name2 + * sublists populated. + * @param[in] section name1 of the method being called can be one of the following: + * - An itenfier. + * - CF_IDENT_ANY if the method is a wildcard. + * name2 of the method being called can be one of the following: + * - An itenfier. + * - NULL to match section names with only a name1. + * - CF_IDENT_ANY if the method is a wildcard. * @return - * - The module instance on success. - * - NULL on not found - * - * If the module exists but the method doesn't exist, then `method` is set to NULL. + * - The module_method_name_t on success. + * - NULL on not found. */ -module_instance_t *module_rlm_by_name_and_method(module_method_t *method, call_env_method_t const **method_env, - char const **name1, char const **name2, - virtual_server_t const *vs, char const *name) +static CC_HINT(nonnull) +module_method_binding_t const *module_binding_find(module_method_binding_t const *bindings, section_name_t const *section) { - char *p, *q, *inst_name; - size_t len; - int j; - module_instance_t *mi; - module_method_binding_t const *methods; - char const *method_name1, *method_name2; - module_rlm_t const *mrlm; - - if (method) *method = NULL; - - method_name1 = method_name2 = NULL; - if (name1) { - method_name1 = *name1; - *name1 = NULL; - } - if (name2) { - method_name2 = *name2; - *name2 = NULL; - } + module_method_binding_t const *p; /* - * Module names are allowed to contain '.' - * so we search for the bare module name first. + * This could potentially be improved by using a binary search + * but given the small number of items, reduced branches and + * sequential access just scanning the list, it's probably not + * worth it. */ - mi = module_rlm_static_by_name(NULL, name); - if (mi) { - section_name_t const **allowed_list; - - if (!method) return mi; + for (p = bindings; p->section; p++) { + switch (section_name_match(p->section, section)) { + case 1: /* match */ + return p; - mrlm = module_rlm_from_module(mi->exported); - - /* - * We're not searching for a named method, OR the - * module has no named methods. Try to return a - * method based on the component. - */ - if (!method_name1 || !mrlm->bindings) goto return_component; + case -1: /* name1 didn't match, skip to the end of the sub-list */ + p = fr_dlist_tail(&p->same_name1); + break; - /* - * Walk through the module, finding a matching - * method. - */ - for (j = 0; mrlm->bindings[j].section; j++) { - methods = &mrlm->bindings[j]; + case 0: /* name1 did match - see if we can find a matching name2 */ + { + fr_dlist_head_t const *same_name1 = &p->same_name1; - /* - * Wildcard match name1, we're - * done. - */ - if (methods->section->name1 == CF_IDENT_ANY) { - found: - *method = methods->method; - if (method_env) *method_env = methods->method_env; - if (name1) *name1 = method_name1; - if (name2) *name2 = method_name2; - return mi; + while ((p = fr_dlist_next(same_name1, p))) { + if (section_name2_match(p->section, section)) return p; } - - /* - * If name1 doesn't match, skip it. - */ - if (strcasecmp(methods->section->name1, method_name1) != 0) continue; - - /* - * The module can declare a - * wildcard for name2, in which - * case it's a match. - */ - if (methods->section->name2 == CF_IDENT_ANY) goto found; - - /* - * No name2 is also a match to no name2. - */ - if (!methods->section->name2 && !method_name2) goto found; - - /* - * Don't do strcmp on NULLs - */ - if (!methods->section->name2 || !method_name2) continue; - - if (strcasecmp(methods->section->name2, method_name2) == 0) goto found; + p = fr_dlist_tail(same_name1); } - - if (!vs) goto skip_section_method; - - /* - * No match for "recv Access-Request", or - * whatever else the section is. Let's see if - * the section has a list of allowed methods. - */ - allowed_list = virtual_server_section_methods(vs, method_name1, method_name2); - if (!allowed_list) goto return_component; - - /* - * Walk over allowed methods for this section, - * (implicitly ordered by priority), and see if - * the allowed method matches any of the module - * methods. This process lets us reference a - * module as "foo" in the configuration. If the - * module exports a "recv bar" method, and the - * virtual server has a "recv bar" processing - * section, then they should match. - * - * Unfortunately, this process is O(N*M). - * Luckily, we only do it if all else fails, so - * it's mostly OK. - * - * Note that the "allowed" list CANNOT include - * CF_IDENT_ANY. Only the module can do that. - * If the "allowed" list exported CF_IDENT_ANY, - * then any module method would match, which is - * bad. - */ - for (j = 0; allowed_list[j]; j++) { - int k; - section_name_t const *allowed = allowed_list[j]; - - for (k = 0; mrlm->bindings[k].section; k++) { - methods = &mrlm->bindings[k]; - - fr_assert(methods->section->name1 != CF_IDENT_ANY); /* should have been caught above */ - - if (strcasecmp(methods->section->name1, allowed->name1) != 0) continue; - - /* - * The module matches "recv *", - * call this method. - */ - if (methods->section->name2 == CF_IDENT_ANY) { - found_allowed: - *method = methods->method; - return mi; - } - - /* - * No name2 is also a match to no name2. - */ - if (!methods->section->name2 && !allowed->name2) goto found_allowed; - - /* - * Don't do strcasecmp on NULLs - */ - if (!methods->section->name2 || !allowed->name2) continue; - - if (strcasecmp(methods->section->name2, allowed->name2) == 0) goto found_allowed; - } + break; } - - return_component: - /* - * Didn't find a matching method. Just return - * the module. - */ - return mi; +#ifdef __clang_analyzer__ + /* Will never be NULL, worse case, p doesn't change*/ + if (!p) break; +#endif } -skip_section_method: + return NULL; +} - /* - * Find out if the instance name contains - * a method, if it doesn't, then the module - * doesn't exist. - */ - p = strchr(name, '.'); - if (!p) { - fr_strerror_printf("No such module '%s'", name); - return NULL; +/** Dump the available bindings for the module into the strerror stack + * + * @param[in] mmb bindings to push onto the strerror stack. + */ +static void module_rlm_methods_to_strerror(module_method_binding_t const *mmb) +{ + module_method_binding_t const *mmb_p; + + if (!mmb || !mmb[0].section) { + fr_strerror_const_push("Module provides no methods"); + return; } - /* - * The module name may have a '.' in it, AND it may have - * a method So we try to find out which is which. - */ - inst_name = talloc_strdup(NULL, name); - p = inst_name + (p - name); + fr_strerror_const_push("Available methods are:"); - /* - * Loop over the '.' portions, gradually looking up a - * longer string, in order to find the full module name. - */ - do { - *p = '\0'; + for (mmb_p = mmb; mmb_p->section; mmb_p++) { + char const *name1 = section_name_str(mmb_p->section->name1); + char const *name2 = section_name_str(mmb_p->section->name2); - mi = module_instance_by_name(rlm_modules_static, NULL, inst_name); - if (mi) break; + fr_strerror_printf_push(" %s%s%s", + name1, name2 ? "." : "", name2 ? name2 : ""); + } +} - /* - * Find the next '.' - */ - *p = '.'; - p = strchr(p + 1, '.'); - } while (p); +/** Find an existing module instance and verify it implements the specified method + * + * Extracts the method from the module name where the format is @verbatim [.[.]] @endverbatim + * and ensures the module implements the specified method. + * + * @param[in] ctx to allocate the dynamic module key tmpl from. + * @param[out] mmc_out the result from resolving the module method, + * plus the key tmpl for dynamic modules. + * This is not allocated from the ctx to save the runtime + * dereference. + * @param[in] vs Virtual server to search for alternative module names in. + * @param[in] section Section name containing the module call. + * @param[in] name The module method call i.e. module[][.] + * @param[in] t_rules for resolving the dynamic module key. + * @return + * - The module instance on success. + * - NULL on not found + * + * If the module exists but the method doesn't exist, then `method` is set to NULL. + */ +fr_slen_t module_rlm_by_name_and_method(TALLOC_CTX *ctx, module_method_call_t *mmc_out, + virtual_server_t const *vs, section_name_t const *section, fr_sbuff_t *name, + tmpl_rules_t const *t_rules) +{ + fr_sbuff_term_t const *dyn_tt = &FR_SBUFF_TERMS( + L(""), + L("\t"), + L("\n"), + L(" "), + L("[") + ); + + fr_sbuff_term_t const *elem_tt = &FR_SBUFF_TERMS( + L(""), + L("\t"), + L("\n"), + L(" "), + L(".") + ); + + fr_sbuff_t *elem1; + module_method_call_t *mmc; + module_method_call_t mmc_tmp; + module_method_binding_t const *mmb; + + fr_sbuff_marker_t meth_start; + + fr_slen_t slen; + fr_sbuff_t our_name = FR_SBUFF(name); + + mmc = mmc_out ? mmc_out : &mmc_tmp; + if (mmc_out) memset(mmc_out, 0, sizeof(*mmc_out)); /* - * No such module, we're done. + * Advance until the start of the dynamic selector + * (if it exists). */ - if (!mi) { - fr_strerror_printf("Failed to find module '%s'", inst_name); - talloc_free(inst_name); - return NULL; + if (fr_sbuff_adv_until(&our_name, SIZE_MAX, dyn_tt, '\0') == 0) { + fr_strerror_printf("Invalid module method name"); + return fr_sbuff_error(&our_name); } - mrlm = module_rlm_from_module(mi->exported); + FR_SBUFF_TALLOC_THREAD_LOCAL(&elem1, MODULE_INSTANCE_LEN_MAX, (MODULE_INSTANCE_LEN_MAX + 1) * 10); /* - * We have a module, but the caller doesn't care about - * method or names, so just return the module. + * If the method string contains a '[' + * + * Search for a dynamic module method, e.g. `elem1[]`. */ - if (!method || !method_name1 || !method_name2) goto finish; + if (fr_sbuff_is_char(&our_name, '[')) { + fr_sbuff_marker_t end, s_end; + fr_sbuff_marker(&end, &our_name); + + slen = tmpl_afrom_substr(ctx, &mmc->key, &our_name, T_BARE_WORD, NULL, t_rules); + if (slen < 0) { + fr_strerror_const_push("Invalid dynamic module selector expression"); + talloc_free(mmc); + return slen; + } - /* - * We MAY have two names. - */ - p++; - q = strchr(p, '.'); - /* - * We've found the module, but it has no named methods. - */ - if (!mrlm->bindings) { - *name1 = name + (p - inst_name); - *name2 = NULL; - goto finish; - } + if (!fr_sbuff_is_char(&our_name, ']')) { + fr_strerror_const_push("Missing terminating ']' for dynamic module selector"); + error: + talloc_free(mmc); + return fr_sbuff_error(&our_name); + } + fr_sbuff_marker(&s_end, &our_name); + fr_sbuff_set_to_start(&our_name); + slen = fr_sbuff_out_bstrncpy(elem1, &our_name, fr_sbuff_ahead(&end)); + if (slen < 0) { + fr_strerror_const("Module method string too long"); + goto error; + } + mmc->mi = module_instance_by_name(rlm_modules_dynamic, NULL, elem1->start); + if (!mmc->mi) { + fr_strerror_printf("No such dynamic module '%s'", elem1->start); + goto error; + } + mmc->rlm = module_rlm_from_module(mmc->mi->exported); + + fr_sbuff_set(&our_name, &s_end); + fr_sbuff_advance(&our_name, 1); /* Skip the ']' */ /* - * We have "module.METHOD", but METHOD doesn't match - * "authorize", "authenticate", etc. Let's see if it - * matches anything else. + * With elem1.elem2.elem3 + * + * Search for a static module matching one of the following: + * + * - elem1.elem2.elem3 + * - elem1.elem2 + * - elem1 */ - if (!q) { - for (j = 0; mrlm->bindings[j].section; j++) { - methods = &mrlm->bindings[j]; - - /* - * If we do not have the second $method, then ignore it! - */ - if (methods->section->name2 && (methods->section->name2 != CF_IDENT_ANY)) continue; - - /* - * Wildcard match name1, we're - * done. - */ - if (!methods->section->name1 || (methods->section->name1 == CF_IDENT_ANY)) goto found_name1; + } else { + char *p; - /* - * If name1 doesn't match, skip it. - */ - if (strcasecmp(methods->section->name1, p) != 0) continue; + fr_sbuff_set_to_start(&our_name); - found_name1: - /* - * We've matched "*", or "name1" or - * "name1 *". Return that. - */ - *name1 = name + (p - inst_name); - *name2 = NULL; - *method = methods->method; - if (method_env) *method_env = methods->method_env; - break; + slen = fr_sbuff_out_bstrncpy_until(elem1, &our_name, SIZE_MAX, dyn_tt, NULL); + if (slen == 0) { + fr_strerror_const("Invalid module name"); + goto error; + } + if (slen < 0) { + fr_strerror_const("Module method string too long"); + goto error; } /* - * Return the found module. + * Now we have a mutable buffer, we can start chopping + * it up to find the module. */ - goto finish; - } + for (;;) { + mmc->mi = (module_instance_t *)module_rlm_static_by_name(NULL, elem1->start); + if (mmc->mi) { + mmc->rlm = module_rlm_from_module(mmc->mi->exported); + break; /* Done */ + } - /* - * We CANNOT have '.' in method names. - */ - if (strchr(q + 1, '.') != 0) goto finish; + p = strrchr(elem1->start, '.'); + if (!p) break; /* No more '.' */ + *p = '\0'; /* Chop off the last '.' */ + } + + if (!mmc->mi) { + fr_strerror_printf("No such module '%pV'", fr_box_strvalue_len(our_name.start, slen)); + return -1; + } - len = q - p; + fr_sbuff_set_to_start(&our_name); + fr_sbuff_advance(&our_name, strlen(elem1->start)); /* Advance past the module name */ + if (fr_sbuff_is_char(&our_name, '.')) { + fr_sbuff_advance(&our_name, 1); /* Static module method, search directly */ + } else { + fr_sbuff_marker(&meth_start, &our_name); /* for the errors... */ + goto by_section; /* Get the method dynamically from the section*/ + } + } /* - * Trim the '.'. + * For both cases, the buffer should be pointing + * at the start of the method string. */ - if (*q == '.' && *(q + 1)) q++; + fr_sbuff_marker(&meth_start, &our_name); /* - * We have "module.METHOD1.METHOD2". + * If a module method was provided, search for it in the named + * methods provided by the module. + * + * The method name should be either: * - * Loop over the method names, seeing if we have a match. + * - name1 + * - name1.name2 */ - for (j = 0; mrlm->bindings[j].section; j++) { - methods = &mrlm->bindings[j]; + { + section_name_t method; + fr_sbuff_t *elem2; - /* - * If name1 doesn't match, skip it. - */ - if (strncasecmp(methods->section->name1, p, len) != 0) continue; + fr_sbuff_set_to_start(elem1); /* May have used this already for module lookups */ - /* - * It may have been a partial match, like "rec", - * instead of "recv". In which case check if it - * was a FULL match. - */ - if (strlen(methods->section->name1) != len) continue; + slen = fr_sbuff_out_bstrncpy_until(elem1, &our_name, SIZE_MAX, elem_tt, NULL); + if (slen < 0) { + fr_strerror_const("Module method string too long"); + return fr_sbuff_error(&our_name); + } + if (slen == 0) goto by_section; /* This works for both dynamic and static modules */ - /* - * The module can declare a - * wildcard for name2, in which - * case it's a match. - */ - if (!methods->section->name2 || (methods->section->name2 == CF_IDENT_ANY)) goto found_name2; + FR_SBUFF_TALLOC_THREAD_LOCAL(&elem2, MODULE_INSTANCE_LEN_MAX, MODULE_INSTANCE_LEN_MAX); - /* - * Don't do strcmp on NULLs - */ - if (!methods->section->name2) continue; + if (fr_sbuff_is_char(&our_name, '.')) { + fr_sbuff_advance(&our_name, 1); + if (fr_sbuff_out_bstrncpy_until(elem2, &our_name, SIZE_MAX, elem_tt, NULL) < 0) { + fr_strerror_const("Module method string too long"); + goto error; + } + } - if (strcasecmp(methods->section->name2, q) != 0) continue; + method = (section_name_t) { + .name1 = elem1->start, + .name2 = fr_sbuff_used(elem2) ? elem2->start : NULL + }; + + mmb = module_binding_find(mmc->rlm->bindings, &method); + if (!mmb) { + fr_strerror_printf("Module \"%s\" does not have method %s%s%s", + mmc->mi->name, + method.name1, + method.name2 ? "." : "", + method.name2 ? method.name2 : "" + ); + + module_rlm_methods_to_strerror(mmc->rlm->bindings); + return fr_sbuff_error(&meth_start); + } + mmc->mmb = *mmb; /* For locality of reference and fewer derefs */ + if (mmc_out) section_name_dup(ctx, &mmc->asked, &method); - found_name2: - /* - * Update name1/name2 with the methods - * that were found. - */ - *name1 = methods->section->name1; - *name2 = name + (q - inst_name); - *method = methods->method; - if (method_env) *method_env = methods->method_env; - goto finish; + return fr_sbuff_set(name, &our_name); } - *name1 = name + (p - inst_name); - *name2 = NULL; +by_section: + /* + * First look for the section name in the module's + * bindings. If that fails, look for the alt + * section names from the virtual server section. + * + * If that fails, we're done. + */ + mmb = module_binding_find(mmc->rlm->bindings, section); + if (!mmb) { + section_name_t const **alt_p = virtual_server_section_methods(vs, section); + if (alt_p) { + for (; *alt_p; alt_p++) { + mmb = module_binding_find(mmc->rlm->bindings, *alt_p); + if (mmb) { + if (mmc_out) section_name_dup(ctx, &mmc->asked, *alt_p); + break; + } + } + } + } else { + if (mmc_out) section_name_dup(ctx, &mmc->asked, section); + } + if (!mmb) { + fr_strerror_printf("Module \"%s\" has no method for section %s %s { ... }, i.e. %s%s%s", + mmc->mi->name, + section->name1, + section->name2 ? section->name2 : "", + section->name1, + section->name2 ? "." : "", + section->name2 ? section->name2 : "" + ); + module_rlm_methods_to_strerror(mmc->rlm->bindings); + + return fr_sbuff_error(&meth_start); + } + mmc->mmb = *mmb; /* For locality of reference and fewer derefs */ -finish: - talloc_free(inst_name); - return mi; + return fr_sbuff_set(name, &our_name); } -CONF_SECTION *module_rlm_by_name_virtual(char const *asked_name) +CONF_SECTION *module_rlm_virtual_by_name(char const *asked_name) { module_rlm_virtual_t *inst; @@ -793,6 +826,11 @@ CONF_SECTION *module_rlm_by_name_virtual(char const *asked_name) return inst->cs; } +module_instance_t *module_rlm_dynamic_by_name(module_instance_t const *parent, char const *asked_name) +{ + return module_instance_by_name(rlm_modules_dynamic, parent, asked_name); +} + module_instance_t *module_rlm_static_by_name(module_instance_t const *parent, char const *asked_name) { return module_instance_by_name(rlm_modules_static, parent, asked_name); @@ -809,7 +847,6 @@ static int module_rlm_bootstrap_virtual(CONF_SECTION *cs) { char const *name; bool all_same; - module_t const *last = NULL; CONF_ITEM *sub_ci = NULL; CONF_PAIR *cp; module_instance_t *mi; @@ -858,50 +895,45 @@ static int module_rlm_bootstrap_virtual(CONF_SECTION *cs) */ all_same = (strcmp(cf_section_name1(cs), "group") != 0); - /* - * Ensure that the modules we reference here exist. - */ - while ((sub_ci = cf_item_next(cs, sub_ci))) { - if (cf_item_is_pair(sub_ci)) { - cp = cf_item_to_pair(sub_ci); - if (cf_pair_value(cp)) { - cf_log_err(sub_ci, "Cannot set return codes in a %s block", cf_section_name1(cs)); - return -1; - } + { + module_t const *last = NULL; - /* - * Allow "foo.authorize" in subsections. - * - * Note that we don't care what the method is, just that it exists. - * - * This check is needed only because we - * want to know if we need to register a - * redundant xlat for the virtual module. - */ - mi = module_rlm_by_name_and_method(NULL, NULL, NULL, NULL, NULL, cf_pair_attr(cp)); - if (!mi) { - cf_log_err(sub_ci, "Module instance \"%s\" referenced in %s block, does not exist", - cf_pair_attr(cp), cf_section_name1(cs)); - return -1; - } + /* + * Ensure that the modules we reference here exist. + */ + while ((sub_ci = cf_item_next(cs, sub_ci))) { + if (cf_item_is_pair(sub_ci)) { + cp = cf_item_to_pair(sub_ci); + if (cf_pair_value(cp)) { + cf_log_err(sub_ci, "Cannot set return codes in a %s block", cf_section_name1(cs)); + return -1; + } + + mi = module_rlm_static_by_name(NULL, cf_pair_attr(cp)); + if (!mi) { + cf_log_perr(sub_ci, "Failed resolving module reference '%s' in %s block", + cf_pair_attr(cp), cf_section_name1(cs)); + return -1; + } - if (all_same) { - if (!last) { - last = mi->exported; - } else if (last != mi->exported) { - last = NULL; - all_same = false; + if (all_same) { + if (!last) { + last = mi->exported; + } else if (last != mi->exported) { + last = NULL; + all_same = false; + } } + } else { + all_same = false; } - } else { - all_same = false; - } - /* - * Don't check subsections for now. That check - * happens later in the unlang compiler. - */ - } /* loop over things in a virtual module section */ + /* + * Don't check subsections for now. That check + * happens later in the unlang compiler. + */ + } /* loop over things in a virtual module section */ + } inst = talloc_zero(cs, module_rlm_virtual_t); if (!inst) return -1; @@ -1095,6 +1127,16 @@ static int module_method_validate(module_instance_t *mi) return 0; } +/** Compare xlat functions registered to a module + */ +static int8_t module_rlm_xlat_cmp(void const *one, void const *two) +{ + module_rlm_xlat_t const *a = one; + module_rlm_xlat_t const *b = two; + + return xlat_func_cmp(a->xlat, b->xlat); +} + /** Allocate a rlm module instance * * These have extra space allocated to hold the dlist of associated xlats. @@ -1123,7 +1165,7 @@ module_instance_t *module_rlm_instance_alloc(module_list_t *ml, MEM(mri = talloc(mi, module_rlm_instance_t)); module_instance_uctx_set(mi, mri); - fr_rb_inline_init(&mri->xlats, module_rlm_xlat_t, node, xlat_func_cmp, NULL); + fr_rb_inline_init(&mri->xlats, module_rlm_xlat_t, node, module_rlm_xlat_cmp, NULL); return mi; } diff --git a/src/lib/server/module_rlm.h b/src/lib/server/module_rlm.h index 75b0e95cb3b..09d281103bb 100644 --- a/src/lib/server/module_rlm.h +++ b/src/lib/server/module_rlm.h @@ -63,6 +63,9 @@ typedef struct { ///< onto the stack for execution. So we need ///< to use the common type here. module_rlm_t const *rlm; //!< Cached module_rlm_t. + section_name_t asked; //!< The actual . used for the module call. + ///< This was either the override the user specified, + ///< or the name of the section. module_method_binding_t mmb; //!< Method we're calling. tmpl_t *key; //!< Dynamic key, only set for dynamic modules. } module_method_call_t; @@ -122,13 +125,12 @@ 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, call_env_method_t const ** method_env, - char const **name1, char const **name2, - virtual_server_t const *vs, char const *asked_name); +fr_slen_t module_rlm_by_name_and_method(TALLOC_CTX *ctx, module_method_call_t *mmc_out, + virtual_server_t const *vs, section_name_t const *section, fr_sbuff_t *name, + tmpl_rules_t const *t_rules) CC_HINT(nonnull(5)); module_instance_t *module_rlm_dynamic_by_name(module_instance_t const *parent, char const *name); -CONF_SECTION *module_rlm_by_name_virtual(char const *asked_name); module_instance_t *module_rlm_static_by_name(module_instance_t const *parent, char const *name); CONF_SECTION *module_rlm_virtual_by_name(char const *name); diff --git a/src/lib/server/section.h b/src/lib/server/section.h index ac10afd2b06..7c8c1ae778c 100644 --- a/src/lib/server/section.h +++ b/src/lib/server/section.h @@ -91,6 +91,26 @@ name2: return section_name2_match(a, b); } +/** Return a printable string for the section name + * + * @param[in] name Section name. + */ +static inline char const *section_name_str(char const *name) +{ + if (name == NULL) return "NULL"; + if (name == CF_IDENT_ANY) return "*"; + return name; +} + +static inline void section_name_dup(TALLOC_CTX *ctx, section_name_t *dst, section_name_t const *src) +{ + dst->name1 = src->name1; + dst->name2 = src->name2; + + if (dst->name1 && (dst->name1 != CF_IDENT_ANY)) dst->name1 = talloc_typed_strdup(ctx, src->name1); + if (dst->name2 && (dst->name2 != CF_IDENT_ANY)) dst->name2 = talloc_typed_strdup(ctx, src->name2); +} + int8_t section_name_cmp(void const *one, void const *two); #ifdef __cplusplus diff --git a/src/lib/server/virtual_servers.c b/src/lib/server/virtual_servers.c index d51403d093d..708a7ef1a70 100644 --- a/src/lib/server/virtual_servers.c +++ b/src/lib/server/virtual_servers.c @@ -24,8 +24,6 @@ * @copyright 2000 Alan DeKok (aland@freeradius.org) * @copyright 2000 Alan Curry (pacman@world.std.com) */ - - RCSID("$Id$") #include @@ -38,6 +36,7 @@ RCSID("$Id$") #include #include #include +#include #include #include @@ -110,8 +109,6 @@ static CONF_SECTION *virtual_server_root; static fr_rb_tree_t *listen_addr_root = NULL; -static int8_t server_section_name_cmp(void const *one, void const *two); - static int namespace_on_read(TALLOC_CTX *ctx, void *out, UNUSED void *parent, CONF_ITEM *ci, conf_parser_t const *rule); static int server_on_read(TALLOC_CTX *ctx, void *out, UNUSED void *parent, CONF_ITEM *ci, UNUSED conf_parser_t const *rule); @@ -498,6 +495,14 @@ static int listen_parse(UNUSED TALLOC_CTX *ctx, void *out, UNUSED void *parent, return 0; } +static int8_t virtual_server_compile_name_cmp(void const *a, void const *b) +{ + virtual_server_compile_t const *sa = a; + virtual_server_compile_t const *sb = b; + + return section_name_cmp(sa->section, sb->section); +} + /** Callback to validate the server section * * @param[in] ctx to allocate data in. @@ -523,7 +528,7 @@ static int server_parse(UNUSED TALLOC_CTX *ctx, void *out, UNUSED void *parent, return -1; } - MEM(server->sections = fr_rb_alloc(server, server_section_name_cmp, NULL)); + MEM(server->sections = fr_rb_alloc(server, virtual_server_compile_name_cmp, NULL)); server->server_cs = server_cs; /* @@ -1010,24 +1015,6 @@ int virtual_server_compile_sections(virtual_server_t const *vs, tmpl_rules_t con return found; } -static int8_t server_section_name_cmp(void const *one, void const *two) -{ - virtual_server_compile_t const *a = one; - virtual_server_compile_t const *b = two; - int ret; - - ret = strcmp(a->section->name1, b->section->name1); - ret = CMP(ret, 0); - if (ret != 0) return ret; - - if (a->section->name2 == b->section->name2) return 0; - if ((a->section->name2 == CF_IDENT_ANY) && (b->section->name2 != CF_IDENT_ANY)) return -1; - if ((a->section->name2 != CF_IDENT_ANY) && (b->section->name2 == CF_IDENT_ANY)) return +1; - - ret = strcmp(a->section->name2, b->section->name2); - return CMP(ret, 0); -} - /** Register name1 / name2 as allowed processing sections * * This function is called from the virtual server bootstrap routine, @@ -1078,7 +1065,7 @@ int virtual_server_section_register(virtual_server_t *vs, virtual_server_compile /** Find the component for a section * */ -section_name_t const **virtual_server_section_methods(virtual_server_t const *vs, char const *name1, char const *name2) +section_name_t const **virtual_server_section_methods(virtual_server_t const *vs, section_name_t const *section) { virtual_server_compile_t *entry; @@ -1086,10 +1073,10 @@ section_name_t const **virtual_server_section_methods(virtual_server_t const *vs * Look up the specific name first. That way we can * define both "accounting on", and "accounting *". */ - if (name2 != CF_IDENT_ANY) { + if (section->name2 != CF_IDENT_ANY) { entry = fr_rb_find(vs->sections, &(virtual_server_compile_t) { - .section = SECTION_NAME(name1, name2) + .section = section }); if (entry) return entry->methods; } @@ -1099,7 +1086,7 @@ section_name_t const **virtual_server_section_methods(virtual_server_t const *vs */ entry = fr_rb_find(vs->sections, &(virtual_server_compile_t) { - .section = SECTION_NAME(name1, CF_IDENT_ANY) + .section = SECTION_NAME(section->name1, CF_IDENT_ANY) }); if (!entry) return NULL; diff --git a/src/lib/server/virtual_servers.h b/src/lib/server/virtual_servers.h index 7c9d570c6bc..224c9a9c8ef 100644 --- a/src/lib/server/virtual_servers.h +++ b/src/lib/server/virtual_servers.h @@ -112,9 +112,9 @@ typedef struct { int virtual_server_section_register(virtual_server_t *vs, virtual_server_compile_t const *entry) CC_HINT(nonnull); -int virtual_server_compile_sections(virtual_server_t const *vs, tmpl_rules_t const *rules) CC_HINT(nonnull); +section_name_t const **virtual_server_section_methods(virtual_server_t const *vs, section_name_t const *section) CC_HINT(nonnull); -section_name_t const **virtual_server_section_methods(virtual_server_t const *vs, char const *name1, char const *name2) CC_HINT(nonnull(1)); +int virtual_server_compile_sections(virtual_server_t const *vs, tmpl_rules_t const *rules) CC_HINT(nonnull); unlang_action_t virtual_server_push(request_t *request, CONF_SECTION *server_cs, bool top_frame) CC_HINT(nonnull); diff --git a/src/lib/unlang/call_env.c b/src/lib/unlang/call_env.c index dd133e4e4fc..4cc75f4c0b4 100644 --- a/src/lib/unlang/call_env.c +++ b/src/lib/unlang/call_env.c @@ -29,6 +29,7 @@ RCSID("$Id$") #include #include #include +#include #include #include #include @@ -53,6 +54,12 @@ struct call_env_parsed_s { }; FR_DLIST_FUNCS(call_env_parsed, call_env_parsed_t, entry) +#if defined(DEBUG_CALL_ENV) +# define CALL_ENV_DEBUG(_ci, fmt, ...) cf_log_debug(_ci, fmt, ##__VA_ARGS__) +#else +# define CALL_ENV_DEBUG(_ci, ...) +#endif + /** Parse the result of call_env tmpl expansion */ static inline CC_HINT(always_inline) @@ -358,15 +365,14 @@ int call_env_parsed_valid(call_env_parsed_t const *parsed, CONF_ITEM const *ci, * @param[out] out Where to write the result of parsing. * @param[in] t_rules we're parsing attributes with. Contains the default dictionary and nested 'caller' tmpl_rules_t. * @param[in] ci The #CONF_SECTION or #CONF_PAIR to parse. - * @param[in] data module / xlat instance data of the module / xlat allocating this call_env + * @param[in] cec information about the call. * @param[in] rule Parse rules - How the #CONF_PAIR or #CONF_SECTION should be converted. * @return * - 0 on success. * - -1 on failure. */ int call_env_parse_pair(TALLOC_CTX *ctx, void *out, tmpl_rules_t const *t_rules, CONF_ITEM *ci, - UNUSED char const *section_name1, UNUSED char const *section_name2, - UNUSED void const *data, UNUSED call_env_parser_t const *rule) + UNUSED call_env_ctx_t const *cec, UNUSED call_env_parser_t const *rule) { CONF_PAIR const *to_parse = cf_item_to_pair(ci); tmpl_t *parsed_tmpl; @@ -394,48 +400,53 @@ int call_env_parse_pair(TALLOC_CTX *ctx, void *out, tmpl_rules_t const *t_rules, * @param[in] name Module name for error messages. * @param[in] t_rules controlling how the call env is parsed. * @param[in] cs Module config. - * @param[in] section_name1 Name 1 from calling section for module calls - * @param[in] section_name2 Name 2 from calling section for module calls - * @param[in] data module / xlat instance data of the module / xlat allocating this call_env + * @param[in] cec information about the call. * @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, tmpl_rules_t const *t_rules, - CONF_SECTION const *cs, char const *section_name1, char const *section_name2, - void const *data, call_env_parser_t const *rule) { + CONF_SECTION const *cs, + call_env_ctx_t const *cec, call_env_parser_t const *rule) { CONF_PAIR const *cp, *next; call_env_parsed_t *call_env_parsed = NULL; ssize_t count, multi_index; + call_env_parser_t const *rule_p = rule; + + while (rule_p->name) { + CALL_ENV_DEBUG(cs, "%s: Parsing call env data for %s", name, section_name_str(rule_p->name)); - while (rule->name) { - if (call_env_is_subsection(rule->flags)) { + if (call_env_is_subsection(rule_p->flags)) { CONF_SECTION const *subcs; - subcs = cf_section_find(cs, rule->name, rule->section.ident2); - if (!subcs && !call_env_parse_missing(rule->flags)) { - if (!call_env_required(rule->flags)) goto next; - cf_log_err(cs, "Module %s missing required section \"%s\"", name, rule->name); + subcs = cf_section_find(cs, rule_p->name, rule_p->section.name2); + if (!subcs && !call_env_parse_missing(rule_p->flags)) { + if (!call_env_required(rule_p->flags)) goto next; + cf_log_err(cs, "Module %s missing required section \"%s\"", name, rule_p->name); return -1; } /* * Hand off to custom parsing function if there is one... */ - if (rule->section.func) { + if (rule_p->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, t_rules, cf_section_to_item(subcs), section_name1, - section_name2, data, rule) < 0) { - cf_log_perr(cs, "Failed parsing configuration section %s", rule->name); + CALL_ENV_DEBUG(cs, "%s: Calling subsection callback %p", name, rule_p->section.func); + + if (rule_p->section.func(ctx, parsed, t_rules, cf_section_to_item(subcs), cec, rule_p) < 0) { + cf_log_perr(cs, "Failed parsing configuration section %s", rule_p->name); talloc_free(call_env_parsed); return -1; } + CALL_ENV_DEBUG(subcs, "%s: Callback returned %u parsed call envs", name, + call_env_parsed_num_elements(parsed)); + /* * We _could_ fix up count and multi_index on behalf of * the callback, but there's no guarantee that all call_env_parsed_t @@ -443,25 +454,28 @@ static int call_env_parse(TALLOC_CTX *ctx, call_env_parsed_head_t *parsed, char */ call_env_parsed = last; while ((call_env_parsed = call_env_parsed_next(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); + CALL_ENV_DEBUG(subcs, "%s: Checking parsed env", name, rule_p->section.func); + if (call_env_parsed_valid(call_env_parsed, cf_section_to_item(subcs), rule_p) < 0) { + cf_log_err(cf_section_to_item(subcs), "Invalid data produced by %s", rule_p->name); return -1; } } goto next; } - if (call_env_parse(ctx, parsed, name, t_rules, subcs, section_name1, section_name2, - data, rule->section.subcs) < 0) return -1; + if (call_env_parse(ctx, parsed, name, t_rules, subcs, cec, rule_p->section.subcs) < 0) { + CALL_ENV_DEBUG(cs, "%s: Recursive call failed", name, rule_p->name); + return -1; + } goto next; } - cp = cf_pair_find(cs, rule->name); + cp = cf_pair_find(cs, rule_p->name); - if (!cp && !rule->pair.dflt) { - if (!call_env_required(rule->flags)) goto next; + if (!cp && !rule_p->pair.dflt) { + if (!call_env_required(rule_p->flags)) goto next; - cf_log_err(cs, "Missing required config item '%s'", rule->name); + cf_log_err(cs, "Missing required config item '%s'", rule_p->name); return -1; } @@ -469,32 +483,32 @@ 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(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); + if (!call_env_multi(rule_p->flags) && ((next = cf_pair_find_next(cs, cp, rule_p->name)))) { + cf_log_err(cf_pair_to_item(next), "Invalid duplicate configuration item '%s'", rule_p->name); return -1; } - count = cf_pair_count(cs, rule->name); + count = cf_pair_count(cs, rule_p->name); if (count == 0) count = 1; for (multi_index = 0; multi_index < count; multi_index++) { CONF_PAIR *tmp_cp = NULL; CONF_PAIR const *to_parse; tmpl_rules_t our_rules = {}; - fr_type_t type = rule->pair.cast_type; - call_env_parse_pair_t func = rule->pair.func ? rule->pair.func : call_env_parse_pair; + fr_type_t type = rule_p->pair.cast_type; + call_env_parse_pair_t func = rule_p->pair.func ? rule_p->pair.func : call_env_parse_pair; if (t_rules) { our_rules.parent = t_rules->parent; our_rules.attr.dict_def = t_rules->attr.dict_def; - our_rules.escape = rule->pair.escape; /* Escape rules will now get embedded in the tmpl_t and used at evaluation */ + our_rules.escape = rule_p->pair.escape; /* Escape rules will now get embedded in the tmpl_t and used at evaluation */ } our_rules.attr.list_def = request_attr_request; our_rules.cast = ((type == FR_TYPE_VOID) ? FR_TYPE_NULL : type); - our_rules.literals_safe_for = rule->pair.literals_safe_for; + our_rules.literals_safe_for = rule_p->pair.literals_safe_for; - call_env_parsed = call_env_parsed_alloc(ctx, rule); + call_env_parsed = call_env_parsed_alloc(ctx, rule_p); call_env_parsed->count = count; call_env_parsed->multi_index = multi_index; @@ -505,18 +519,18 @@ static int call_env_parse(TALLOC_CTX *ctx, call_env_parsed_head_t *parsed, char * we can't do that here. */ if (cp) { - if (call_env_force_quote(rule->flags)) { + if (call_env_force_quote(rule_p->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)); + call_env_force_quote(rule_p->flags) ? rule_p->pair.dflt_quote : cf_pair_value_quote(cp)); } else { to_parse = cp; } } else { to_parse = tmp_cp = cf_pair_alloc(NULL, - rule->name, rule->pair.dflt, T_OP_EQ, - T_BARE_WORD, rule->pair.dflt_quote); + rule_p->name, rule_p->pair.dflt, T_OP_EQ, + T_BARE_WORD, rule_p->pair.dflt_quote); } /* @@ -524,9 +538,9 @@ static int call_env_parse(TALLOC_CTX *ctx, call_env_parsed_head_t *parsed, char * would, or produce a custom structure, which will be copied into the * result structure. */ - if (unlikely(func(ctx, &call_env_parsed->data, &our_rules, cf_pair_to_item(to_parse), section_name1, section_name2, data, rule) < 0)) { + if (unlikely(func(ctx, &call_env_parsed->data, &our_rules, cf_pair_to_item(to_parse), cec, rule_p) < 0)) { error: - cf_log_perr(to_parse, "Failed to parse configuration item '%s = %s'", rule->name, cf_pair_value(to_parse)); + cf_log_perr(to_parse, "Failed to parse configuration item '%s = %s'", rule_p->name, cf_pair_value(to_parse)); talloc_free(call_env_parsed); talloc_free(tmp_cp); return -1; @@ -539,17 +553,19 @@ static int call_env_parse(TALLOC_CTX *ctx, call_env_parsed_head_t *parsed, char /* * Ensure only valid data is produced. */ - if (call_env_parsed_valid(call_env_parsed, cf_pair_to_item(to_parse), rule) < 0) goto error; + if (call_env_parsed_valid(call_env_parsed, cf_pair_to_item(to_parse), rule_p) < 0) goto error; call_env_parsed_insert_tail(parsed, call_env_parsed); next_pair: talloc_free(tmp_cp); - cp = cf_pair_find_next(cs, cp, rule->name); + cp = cf_pair_find_next(cs, cp, rule_p->name); } next: - rule++; + rule_p++; } + CALL_ENV_DEBUG(cs, "Returning afer processing %u rules", (unsigned int)(rule_p - rule)); + return 0; } @@ -572,7 +588,7 @@ static size_t call_env_count(size_t *names_len, CONF_SECTION const *cs, call_env while (call_env->name) { if (call_env_is_subsection(call_env->flags)) { CONF_SECTION const *subcs; - subcs = cf_section_find(cs, call_env->name, call_env->section.ident2); + subcs = cf_section_find(cs, call_env->name, call_env->section.name2); if (!subcs) goto next; /* @@ -710,14 +726,13 @@ void call_env_parsed_free(call_env_parsed_head_t *parsed, call_env_parsed_t *ptr * @param[in] call_env_method containing the call_env_pair_t to evaluate against the specified CONF_SECTION. * @param[in] t_rules that control how call_env_pair_t are parsed. * @param[in] cs to parse in the context of the call. - * @param[in] data module / xlat instance data of the module / xlat allocating this call_env + * @param[in] cec information about how the call is being made. * @return * - A new call_env_t on success. * - NULL on failure. */ call_env_t *call_env_alloc(TALLOC_CTX *ctx, char const *name, call_env_method_t const *call_env_method, - tmpl_rules_t const *t_rules, CONF_SECTION *cs, char const *section_name1, - char const *section_name2, void const *data) + tmpl_rules_t const *t_rules, CONF_SECTION *cs, call_env_ctx_t const *cec) { unsigned int count; size_t names_len; @@ -747,8 +762,7 @@ call_env_t *call_env_alloc(TALLOC_CTX *ctx, char const *name, call_env_method_t MEM(call_env = talloc_pooled_object(ctx, call_env_t, count * 4, (sizeof(call_env_parser_t) + sizeof(tmpl_t)) * count + names_len * 2)); call_env->method = call_env_method; call_env_parsed_init(&call_env->parsed); - if (call_env_parse(call_env, &call_env->parsed, name, t_rules, cs, section_name1, section_name2, - data, call_env_method->env) < 0) { + if (call_env_parse(call_env, &call_env->parsed, name, t_rules, cs, cec, call_env_method->env) < 0) { talloc_free(call_env); return NULL; } diff --git a/src/lib/unlang/call_env.h b/src/lib/unlang/call_env.h index 068b25f899c..c65a79bd868 100644 --- a/src/lib/unlang/call_env.h +++ b/src/lib/unlang/call_env.h @@ -34,6 +34,7 @@ extern "C" { typedef struct call_env_parser_s call_env_parser_t; typedef struct call_env_parsed_s call_env_parsed_t; typedef struct call_env_method_s call_env_method_t; +typedef struct call_env_ctx_s call_env_ctx_t; typedef struct call_env_s call_env_t; FR_DLIST_TYPES(call_env_parsed) @@ -43,6 +44,7 @@ FR_DLIST_TYPEDEFS(call_env_parsed, call_env_parsed_head_t, call_env_parsed_entry #include #include #include +#include #include #include @@ -135,13 +137,13 @@ DIAG_ON(attributes) * @param[out] out Where to write the result of parsing. * @param[in] t_rules we're parsing attributes with. Contains the default dictionary and nested 'caller' tmpl_rules_t. * @param[in] ci The #CONF_SECTION or #CONF_PAIR to parse. - * @param[in] data module / xlat instance data of the module / xlat allocating this call_env + * @param[in] cec information about how the call env is being used. * @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, tmpl_rules_t const *t_rules, CONF_ITEM *ci, char const *section_name1, char const *section_name2, void const *data, call_env_parser_t const *rule); +typedef int (*call_env_parse_pair_t)(TALLOC_CTX *ctx, void *out, tmpl_rules_t const *t_rules, CONF_ITEM *ci, call_env_ctx_t const *cec, call_env_parser_t const *rule); /** Callback for performing custom parsing of a #CONF_SECTION * @@ -153,12 +155,14 @@ typedef int (*call_env_parse_pair_t)(TALLOC_CTX *ctx, void *out, tmpl_rules_t co * @param[out] out Where to write the result of parsing. * @param[in] t_rules we're parsing attributes with. Contains the default dictionary and nested 'caller' tmpl_rules_t. * @param[in] ci The #CONF_SECTION or #CONF_PAIR to parse. + * @param[in] cec information about how the call env is being used. * @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, tmpl_rules_t const *t_rules, CONF_ITEM *ci, char const *section_name1, char const *section_name2, void const *data, call_env_parser_t const *rule); +typedef int (*call_env_parse_section_t)(TALLOC_CTX *ctx, call_env_parsed_head_t *out, tmpl_rules_t const *t_rules, CONF_ITEM *ci, + call_env_ctx_t const *cec, call_env_parser_t const *rule); /** Per method call config * @@ -199,7 +203,7 @@ struct call_env_parser_s { } pair; struct { - char const *ident2; //!< Second identifier for a section + char const *name2; //!< 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. @@ -209,6 +213,21 @@ struct call_env_parser_s { void const *uctx; //!< User context for callback functions. }; +typedef enum { + CALL_ENV_CTX_TYPE_MODULE = 1, //!< The callenv is registered to a module method. + CALL_ENV_CTX_TYPE_XLAT //!< The callenv is registered to an xlat. +} call_env_ctx_type_t; + +struct call_env_ctx_s { + call_env_ctx_type_t type; //!< Type of callenv ctx. + + module_instance_t const *mi; //!< Module instance that the callenv is registered to. + ///< Available for both module methods, and xlats. + + section_name_t const *asked; //!< The actual name1/name2 that resolved to a + ///< module_method_binding_t. +}; + #define CALL_ENV_TERMINATOR { NULL } /** Helper macro for populating the size/type fields of a #call_env_method_t from the output structure type @@ -375,21 +394,21 @@ typedef void _mismatch_flags; //!< Dummy type used to indicate bad flags. /** Specify a call_env_parser_t which defines a nested subsection */ -#define FR_CALL_ENV_SUBSECTION(_name, _ident2, _flags, _subcs ) \ +#define FR_CALL_ENV_SUBSECTION(_name, _name2, _flags, _subcs ) \ .name = _name, \ .flags = CALL_ENV_FLAG_SUBSECTION | (_flags), \ .section = { \ - .ident2 = _ident2, \ + .name2 = _name2, \ .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) \ +#define FR_CALL_ENV_SUBSECTION_FUNC(_name, _name2, _flags, _func) \ .name = _name, \ .flags = CALL_ENV_FLAG_SUBSECTION | (_flags), \ .section = { \ - .ident2 = _ident2, \ + .name2 = _name2, \ .func = _func \ } @@ -402,8 +421,8 @@ unlang_action_t call_env_expand(TALLOC_CTX *ctx, request_t *request, call_env_re /** @name Functions that implement standard parsing behaviour which can be called by callbacks * @{ */ -int call_env_parse_pair(TALLOC_CTX *ctx, void *out, tmpl_rules_t const *t_rules, CONF_ITEM *ci, char const *section_name1, - char const *section_name2, void const *data, call_env_parser_t const *rule); +int call_env_parse_pair(TALLOC_CTX *ctx, void *out, tmpl_rules_t const *t_rules, CONF_ITEM *ci, + call_env_ctx_t const *cec, call_env_parser_t const *rule); /** @} */ /** @name Functions to be used by the section callbacks to add parsed data. @@ -426,8 +445,7 @@ void call_env_parsed_free(call_env_parsed_head_t *parsed, call_env_parsed_t *ptr * @{ */ call_env_t *call_env_alloc(TALLOC_CTX *ctx, char const *name, call_env_method_t const *call_env_method, - tmpl_rules_t const *rules, CONF_SECTION *cs, char const *section_name1, - char const *section_name2, void const *data) CC_HINT(nonnull(3,4,5)); + tmpl_rules_t const *rules, CONF_SECTION *cs, call_env_ctx_t const *cec) CC_HINT(nonnull(3,4,5)); /** @} */ #ifdef __cplusplus diff --git a/src/lib/unlang/compile.c b/src/lib/unlang/compile.c index e1137a0cf5b..629f0812a82 100644 --- a/src/lib/unlang/compile.c +++ b/src/lib/unlang/compile.c @@ -312,9 +312,9 @@ static void unlang_dump(unlang_t *instruction, int depth) case UNLANG_TYPE_MODULE: { - unlang_module_t *single = unlang_generic_to_module(c); + unlang_module_t *m = unlang_generic_to_module(c); - DEBUG("%.*s%s", depth, unlang_spaces, single->mi->name); + DEBUG("%.*s%s", depth, unlang_spaces, m->mmc.mi->name); } break; @@ -4361,7 +4361,7 @@ static CONF_SECTION *virtual_module_find_cs(CONF_ITEM *ci, * * Return it to the caller, with the updated method. */ - subcs = module_rlm_by_name_virtual(virtual_name); + subcs = module_rlm_virtual_by_name(virtual_name); if (subcs) goto check_for_loop; /* @@ -4420,30 +4420,29 @@ check_for_loop: return subcs; } -static unlang_t *compile_module(unlang_t *parent, unlang_compile_t *unlang_ctx, - CONF_ITEM *ci, module_instance_t *inst, module_method_t method, - call_env_method_t const *method_env, char const *realname) +static unlang_t *compile_module(unlang_t *parent, unlang_compile_t *unlang_ctx, CONF_ITEM *ci, char const *name) { - unlang_t *c; - unlang_module_t *single; - - /* - * Check if the module in question has the necessary - * component. - */ - if (!method) { - cf_log_err(ci, "Failed compiling %s - no method matching calling section found", inst->name); + unlang_t *c; + unlang_module_t *m; + fr_slen_t slen; + + MEM(m = talloc_zero(parent, unlang_module_t)); + slen = module_rlm_by_name_and_method(m, &m->mmc, + unlang_ctx->vs, + &(section_name_t){ .name1 = unlang_ctx->section_name1, .name2 = unlang_ctx->section_name2 }, + &FR_SBUFF_IN(name, strlen(name)), + unlang_ctx->rules); + if (slen < 0) { + cf_log_perr(ci, "Failed compiling module call"); + talloc_free(m); return NULL; } - MEM(single = talloc_zero(parent, unlang_module_t)); - single->mi = inst; - single->method = method; - c = unlang_module_to_generic(single); + c = unlang_module_to_generic(m); c->parent = parent; c->next = NULL; - c->name = talloc_typed_strdup(c, realname); + c->name = talloc_typed_strdup(c, name); c->debug_name = c->name; c->type = UNLANG_TYPE_MODULE; c->ci = ci; @@ -4451,7 +4450,7 @@ static unlang_t *compile_module(unlang_t *parent, unlang_compile_t *unlang_ctx, /* * Set the default actions for this module. */ - c->actions = inst->actions; + c->actions = m->mmc.mi->actions; /* * Add in the default actions for this section. @@ -4461,22 +4460,27 @@ static unlang_t *compile_module(unlang_t *parent, unlang_compile_t *unlang_ctx, /* * Parse the method environment for this module / method */ - if (method_env) { + if (m->mmc.mmb.method_env) { + call_env_method_t const *method_env = m->mmc.mmb.method_env; + fr_assert_msg(method_env->inst_size, "Method environment for module %s, method %s %s declared, " "but no inst_size set", - inst->name, unlang_ctx->section_name1, unlang_ctx->section_name2); + m->mmc.mi->name, unlang_ctx->section_name1, unlang_ctx->section_name2); if (!unlang_ctx->rules) { - cf_log_err(ci, "Failed compiling %s - no rules", inst->name); + cf_log_err(ci, "Failed compiling %s - no rules", m->mmc.mi->name); goto error; } - single->call_env = call_env_alloc(single, single->self.name, method_env, - unlang_ctx->rules, inst->conf, - unlang_ctx->section_name1, unlang_ctx->section_name2, - single->mi->data); - if (!single->call_env) { + m->call_env = call_env_alloc(m, m->self.name, method_env, + unlang_ctx->rules, m->mmc.mi->conf, + &(call_env_ctx_t){ + .type = CALL_ENV_CTX_TYPE_MODULE, + .mi = m->mmc.mi, + .asked = &m->mmc.asked + }); + if (!m->call_env) { error: - talloc_free(c); + talloc_free(m); return NULL; } } @@ -4487,7 +4491,7 @@ static unlang_t *compile_module(unlang_t *parent, unlang_compile_t *unlang_ctx, */ if (cf_item_is_section(ci) && !unlang_compile_actions(&c->actions, cf_item_to_section(ci), - (inst->exported->flags & MODULE_TYPE_RETRY) != 0)) goto error; + (m->mmc.mi->exported->flags & MODULE_TYPE_RETRY) != 0)) goto error; return c; } @@ -4533,15 +4537,13 @@ static int unlang_pair_keywords_len = NUM_ELEMENTS(unlang_pair_keywords); static unlang_t *compile_item(unlang_t *parent, unlang_compile_t *unlang_ctx, CONF_ITEM *ci) { char const *name, *p; - module_instance_t *inst; CONF_SECTION *cs, *subcs, *modules; char const *realname; unlang_compile_t unlang_ctx2; - module_method_t method; bool policy; unlang_op_compile_t compile; unlang_t *c; - call_env_method_t const *method_env = NULL; + bool ignore_notfound = false; if (cf_item_is_section(ci)) { cs = cf_item_to_section(ci); @@ -4695,14 +4697,21 @@ check_for_module: * Not a function. It must be a real module. */ modules = cf_section_find(cf_root(ci), "modules", NULL); - if (!modules) goto fail; + if (!modules) { + cf_log_err(ci, "Failed compiling \"%s\" as a module or policy as no modules are enabled", name); + cf_log_err(ci, "Please verify that modules { ... } section is present in the server configuration", name); + return NULL; + } realname = name; /* * Try to load the optional module. */ - if (realname[0] == '-') realname++; + if (*realname == '-') { + ignore_notfound = true; + realname++; + } /* * Set the child compilation context BEFORE parsing the @@ -4711,38 +4720,15 @@ check_for_module: * name2, etc. */ UPDATE_CTX2; - inst = module_rlm_by_name_and_method(&method, &method_env, - &unlang_ctx2.section_name1, &unlang_ctx2.section_name2, - unlang_ctx2.vs, realname); - if (inst) { - c = compile_module(parent, &unlang_ctx2, ci, inst, method, method_env, realname); - goto allocate_number; - } + c = compile_module(parent, &unlang_ctx2, ci, realname); + if (c) goto allocate_number; - /* - * We were asked to MAYBE load it and it - * doesn't exist. Return a soft error. - */ - if (realname != name) { - cf_log_warn(ci, "Ignoring \"%s\" as the \"%s\" module is not enabled.", name, realname); + if (ignore_notfound) { + cf_log_warn(ci, "Ignoring \"%s\" as the \"%s\" module is not enabled, " + "or the method does not exist", name, realname); return UNLANG_IGNORE; } - /* - * The module exists, but it does not have the - * named method. - */ - if (!unlang_ctx2.section_name1) { - cf_log_err(ci, "Failed compiling %s - %s", name, fr_strerror()); - return NULL; - } - - /* - * Can't de-reference it to anything. Ugh. - */ -fail: - cf_log_err(ci, "Failed to find \"%s\" as a module or policy.", name); - cf_log_err(ci, "Please verify that the configuration exists in mods-enabled/%s.", name); return NULL; } diff --git a/src/lib/unlang/module.c b/src/lib/unlang/module.c index ef256b622c2..3a46c63c12d 100644 --- a/src/lib/unlang/module.c +++ b/src/lib/unlang/module.c @@ -45,7 +45,7 @@ typedef struct { unlang_module_fd_event_t fd_read; //!< Function to call when FD is readable. unlang_module_fd_event_t fd_write; //!< Function to call when FD is writable. unlang_module_fd_event_t fd_error; //!< Function to call when FD has errored. - module_instance_t *mi; //!< Module instance to pass to callbacks. + module_instance_t const *mi; //!< Module instance to pass to callbacks. ///< Use mi->data to get instance data. void *thread; //!< Thread specific module instance. void *env_data; //!< Per call environment data. @@ -130,12 +130,12 @@ int unlang_module_timeout_add(request_t *request, unlang_module_timeout_t callba unlang_stack_t *stack = request->stack; unlang_stack_frame_t *frame = &stack->frame[stack->depth]; unlang_module_event_t *ev; - unlang_module_t *mc; + unlang_module_t *m; unlang_frame_state_module_t *state = talloc_get_type_abort(frame->state, unlang_frame_state_module_t); fr_assert(stack->depth > 0); fr_assert(frame->instruction->type == UNLANG_TYPE_MODULE); - mc = unlang_generic_to_module(frame->instruction); + m = unlang_generic_to_module(frame->instruction); ev = talloc(request, unlang_module_event_t); if (!ev) return -1; @@ -144,7 +144,7 @@ int unlang_module_timeout_add(request_t *request, unlang_module_timeout_t callba .request = request, .fd = -1, .timeout = callback, - .mi = mc->mi, + .mi = m->mmc.mi, .thread = state->thread, .env_data = state->env_data, .rctx = rctx @@ -250,14 +250,14 @@ int unlang_module_fd_add(request_t *request, unlang_stack_t *stack = request->stack; unlang_stack_frame_t *frame = &stack->frame[stack->depth]; unlang_module_event_t *ev; - unlang_module_t *mc; + unlang_module_t *m; unlang_frame_state_module_t *state = talloc_get_type_abort(frame->state, unlang_frame_state_module_t); fr_assert(stack->depth > 0); fr_assert(frame->instruction->type == UNLANG_TYPE_MODULE); - mc = unlang_generic_to_module(frame->instruction); + m = unlang_generic_to_module(frame->instruction); ev = talloc_zero(request, unlang_module_event_t); if (!ev) return -1; @@ -267,7 +267,7 @@ int unlang_module_fd_add(request_t *request, ev->fd_read = read; ev->fd_write = write; ev->fd_error = error; - ev->mi = mc->mi; + ev->mi = m->mmc.mi; ev->thread = state->thread; ev->env_data = state->env_data; ev->rctx = rctx; @@ -355,8 +355,12 @@ int unlang_module_push(rlm_rcode_t *p_result, request_t *request, .retry = RETRY_INIT, }, }, - .mi = mi, - .method = method + .mmc = { + .mi = mi, + .mmb = { + .method = method + } + } }; /* @@ -519,13 +523,13 @@ unlang_action_t unlang_module_yield_to_section(rlm_rcode_t *p_result, unlang_module_signal_t signal, fr_signal_t sigmask, void *rctx) { if (!subcs) { - unlang_stack_t *stack = request->stack; - unlang_stack_frame_t *frame = &stack->frame[stack->depth]; - unlang_module_t *mc; + unlang_stack_t *stack = request->stack; + unlang_stack_frame_t *frame = &stack->frame[stack->depth]; + unlang_module_t *m; unlang_frame_state_module_t *state; fr_assert(frame->instruction->type == UNLANG_TYPE_MODULE); - mc = unlang_generic_to_module(frame->instruction); + m = unlang_generic_to_module(frame->instruction); /* * Be transparent to the resume function. @@ -536,7 +540,7 @@ unlang_action_t unlang_module_yield_to_section(rlm_rcode_t *p_result, state = talloc_get_type_abort(frame->state, unlang_frame_state_module_t); return resume(p_result, - MODULE_CTX(mc->mi, module_thread(mc->mi)->data, + MODULE_CTX(m->mmc.mi, module_thread(m->mmc.mi)->data, state->env_data, rctx), request); } @@ -625,7 +629,7 @@ static inline CC_HINT(always_inline) void safe_unlock(module_instance_t *mi) static void unlang_module_signal(request_t *request, unlang_stack_frame_t *frame, fr_signal_t action) { unlang_frame_state_module_t *state = talloc_get_type_abort(frame->state, unlang_frame_state_module_t); - unlang_module_t *mc = unlang_generic_to_module(frame->instruction); + unlang_module_t *m = unlang_generic_to_module(frame->instruction); char const *caller; if (!state->signal) return; @@ -634,10 +638,10 @@ static void unlang_module_signal(request_t *request, unlang_stack_frame_t *frame * Async calls can't push anything onto the unlang stack, so we just use a local "caller" here. */ caller = request->module; - request->module = mc->mi->name; - safe_lock(mc->mi); - if (!(action & state->sigmask)) state->signal(MODULE_CTX(mc->mi, state->thread->data, state->env_data, state->rctx), request, action); - safe_unlock(mc->mi); + request->module = m->mmc.mi->name; + safe_lock(m->mmc.mi); + if (!(action & state->sigmask)) state->signal(MODULE_CTX(m->mmc.mi, state->thread->data, state->env_data, state->rctx), request, action); + safe_unlock(m->mmc.mi); request->module = caller; /* @@ -696,8 +700,8 @@ static unlang_action_t unlang_module_resume_done(rlm_rcode_t *p_result, request_ static unlang_action_t unlang_module_resume(rlm_rcode_t *p_result, request_t *request, unlang_stack_frame_t *frame) { unlang_frame_state_module_t *state = talloc_get_type_abort(frame->state, unlang_frame_state_module_t); - unlang_module_t *mc = unlang_generic_to_module(frame->instruction); - module_method_t resume; + unlang_module_t *m = unlang_generic_to_module(frame->instruction); + module_method_t resume; unlang_action_t ua; /* @@ -724,16 +728,16 @@ static unlang_action_t unlang_module_resume(rlm_rcode_t *p_result, request_t *re /* * Lock is noop unless instance->mutex is set. */ - safe_lock(mc->mi); - ua = resume(&state->rcode, MODULE_CTX(mc->mi, state->thread->data, + safe_lock(m->mmc.mi); + ua = resume(&state->rcode, MODULE_CTX(m->mmc.mi, state->thread->data, state->env_data, state->rctx), request); - safe_unlock(mc->mi); + safe_unlock(m->mmc.mi); if (request->master_state == REQUEST_STOP_PROCESSING) ua = UNLANG_ACTION_STOP_PROCESSING; switch (ua) { case UNLANG_ACTION_STOP_PROCESSING: - RWARN("Module %s or worker signalled to stop processing request", mc->mi->module->exported->name); + RWARN("Module %s or worker signalled to stop processing request", m->mmc.mi->name); if (state->p_result) *state->p_result = state->rcode; state->thread->active_callers--; *p_result = state->rcode; @@ -874,7 +878,7 @@ static void unlang_module_event_retry_handler(UNUSED fr_event_list_t *el, fr_tim static unlang_action_t unlang_module(rlm_rcode_t *p_result, request_t *request, unlang_stack_frame_t *frame) { - unlang_module_t *mc; + unlang_module_t *m; unlang_frame_state_module_t *state = talloc_get_type_abort(frame->state, unlang_frame_state_module_t); unlang_action_t ua; fr_time_t now = fr_time_wrap(0); @@ -890,26 +894,26 @@ static unlang_action_t unlang_module(rlm_rcode_t *p_result, request_t *request, * Process a stand-alone child, and fall through * to dealing with it's parent. */ - mc = unlang_generic_to_module(frame->instruction); - fr_assert(mc); + m = unlang_generic_to_module(frame->instruction); + fr_assert(m); RDEBUG4("[%i] %s - %s (%s)", stack_depth_current(request), __FUNCTION__, - mc->mi->module->exported->name, mc->mi->name); + m->mmc.mi->module->exported->name, m->mmc.mi->name); state->p_result = NULL; /* * Return administratively configured return code */ - if (mc->mi->force) { - state->rcode = mc->mi->code; + if (m->mmc.mi->force) { + state->rcode = m->mmc.mi->code; ua = UNLANG_ACTION_CALCULATE_RESULT; goto done; } - if (mc->call_env) { + if (m->mmc.mmb.method_env) { if (!state->env_data) { - ua = call_env_expand(state, request, &state->env_result, &state->env_data, mc->call_env); + ua = call_env_expand(state, request, &state->env_result, &state->env_data, m->call_env); switch (ua) { case UNLANG_ACTION_FAIL: goto fail; @@ -932,7 +936,7 @@ static unlang_action_t unlang_module(rlm_rcode_t *p_result, request_t *request, /* * Grab the thread/module specific data if any exists. */ - state->thread = module_thread(mc->mi); + state->thread = module_thread(m->mmc.mi); fr_assert(state->thread != NULL); /* @@ -951,12 +955,12 @@ static unlang_action_t unlang_module(rlm_rcode_t *p_result, request_t *request, */ if (fr_time_delta_ispos(frame->instruction->actions.retry.irt)) now = fr_time(); - request->module = mc->mi->name; - safe_lock(mc->mi); /* Noop unless instance->mutex set */ - ua = mc->method(&state->rcode, - MODULE_CTX(mc->mi, state->thread->data, state->env_data, NULL), - request); - safe_unlock(mc->mi); + request->module = m->mmc.mi->name; + safe_lock(m->mmc.mi); /* Noop unless instance->mutex set */ + ua = m->mmc.mmb.method(&state->rcode, + MODULE_CTX(m->mmc.mi, state->thread->data, state->env_data, NULL), + request); + safe_unlock(m->mmc.mi); if (request->master_state == REQUEST_STOP_PROCESSING) ua = UNLANG_ACTION_STOP_PROCESSING; @@ -966,7 +970,7 @@ static unlang_action_t unlang_module(rlm_rcode_t *p_result, request_t *request, * must have been blocked. */ case UNLANG_ACTION_STOP_PROCESSING: - RWARN("Module %s became unblocked", mc->mi->name); + RWARN("Module %s became unblocked", m->mmc.mi->name); if (state->p_result) *state->p_result = state->rcode; *p_result = state->rcode; request->module = state->previous_module; diff --git a/src/lib/unlang/xlat_inst.c b/src/lib/unlang/xlat_inst.c index 33bcef8cf38..8ee0c51352a 100644 --- a/src/lib/unlang/xlat_inst.c +++ b/src/lib/unlang/xlat_inst.c @@ -23,6 +23,7 @@ * @copyright 2018-2021 Arran Cudbard-Bell (a.cudbardb@freeradius.org) * @copyright 2018 The FreeRADIUS server project */ +#include "lib/unlang/call_env.h" RCSID("$Id$") #include @@ -272,7 +273,11 @@ static xlat_inst_t *xlat_inst_alloc(xlat_exp_t *node) .dict_def = call->dict, .list_def = request_attr_request } - }, call->func->mctx->mi->conf, NULL, NULL, call->func->mctx->mi->data); + }, call->func->mctx->mi->conf, + &(call_env_ctx_t){ + .type = CALL_ENV_CTX_TYPE_XLAT, + .mi = call->func->mctx->mi + }); if (!xi->call_env) { talloc_free(xi); return NULL; diff --git a/src/lib/unlang/xlat_redundant.c b/src/lib/unlang/xlat_redundant.c index cfb3455edc6..7652a637766 100644 --- a/src/lib/unlang/xlat_redundant.c +++ b/src/lib/unlang/xlat_redundant.c @@ -22,14 +22,25 @@ * * @copyright 2023 Arran Cudbard-Bell (a.cudbardb@freeradius.org) */ - RCSID("$Id$") +#include + #include #include #include #include + +#include +#include +#include +#include + +#include #include +#include +#include + /* * Internal redundant handler for xlats @@ -50,7 +61,7 @@ typedef enum { typedef struct { fr_dlist_t entry; //!< Entry in the redundant function list. - xlat_t *func; //!< Resolved xlat function. + xlat_t const *func; //!< Resolved xlat function. } xlat_redundant_func_t; typedef struct { @@ -193,7 +204,7 @@ static xlat_action_t xlat_redundant(TALLOC_CTX *ctx, fr_dcursor_t *out, * @param[in] args Arguments to the function. Will be copied, * and freed when the new xlat node is freed. */ -static xlat_exp_t *xlat_exp_func_alloc(TALLOC_CTX *ctx, xlat_t *func, xlat_exp_head_t const *args) +static xlat_exp_t *xlat_exp_func_alloc(TALLOC_CTX *ctx, xlat_t const *func, xlat_exp_head_t const *args) { xlat_exp_t *node; @@ -311,6 +322,16 @@ static xlat_arg_parser_t const xlat_redundant_args[] = { XLAT_ARG_PARSER_TERMINATOR }; +static inline CC_HINT(always_inline) +void xlat_redundant_add_xlat(xlat_redundant_t *xr, xlat_t const *x) +{ + xlat_redundant_func_t *xrf; + + MEM(xrf = talloc_zero(xr, xlat_redundant_func_t)); + xrf->func = x; + fr_dlist_insert_tail(&xr->funcs, xrf); +} + /** Registers a redundant xlat * * These xlats wrap the xlat methods of the modules in a redundant section, @@ -330,18 +351,18 @@ int xlat_register_redundant(CONF_SECTION *cs) }; static size_t xlat_redundant_type_table_len = NUM_ELEMENTS(xlat_redundant_type_table); - char const *name1, *name2; + char const *name1; xlat_redundant_type_t xr_type; xlat_redundant_t *xr; - xlat_func_flags_t flags = XLAT_FUNC_FLAG_NONE; - bool can_be_pure = false; + xlat_func_flags_t default_flags; xlat_arg_parser_t const *args = NULL; fr_type_t return_type = FR_TYPE_NULL; - bool first = true; - xlat_t *xlat; CONF_ITEM *ci = NULL; + int children = 0, i; + module_instance_t **mri_arr; /* Temporary array of module instances */ + module_instance_t **mri_p; name1 = cf_section_name1(cs); xr_type = fr_table_value_by_str(xlat_redundant_type_table, name1, XLAT_REDUNDANT_INVALID); @@ -351,110 +372,199 @@ int xlat_register_redundant(CONF_SECTION *cs) return -1; case XLAT_REDUNDANT: - can_be_pure = true; /* Can be pure */ + default_flags = XLAT_FUNC_FLAG_PURE; /* Can be pure */ break; case XLAT_LOAD_BALANCE: - can_be_pure = false; /* Can never be pure because of random selection */ + default_flags = XLAT_FUNC_FLAG_NONE; /* Can never be pure because of random selection */ break; case XLAT_REDUNDANT_LOAD_BALANCE: - can_be_pure = false; /* Can never be pure because of random selection */ + default_flags = XLAT_FUNC_FLAG_NONE; /* Can never be pure because of random selection */ break; } - name2 = cf_section_name2(cs); - if (xlat_func_find(name2, talloc_array_length(name2) - 1)) { - cf_log_err(cs, "An expansion is already registered for this name"); - return -1; - } + /* + * Count the children + */ + while ((ci = cf_item_next(cs, ci))) { + if (!cf_item_is_pair(ci)) continue; - MEM(xr = talloc_zero(cs, xlat_redundant_t)); - xr->type = xr_type; - xr->cs = cs; - fr_dlist_talloc_init(&xr->funcs, xlat_redundant_func_t, entry); + children++; + } /* - * Count the number of children for load-balance, and - * also find out a little bit more about the old xlats. + * There must be at least one child. * - * These are just preemptive checks, the majority of - * the work is done when a redundant xlat is - * instantiated. There we create an xlat node for - * each of the children of the section. + * It's useful to allow a redundant section with + * only one child, for debugging. */ - while ((ci = cf_item_next(cs, ci))) { - xlat_redundant_func_t *xrf; - char const *mod_func_name; - xlat_t *mod_func; + if (children == 0) { + cf_log_err(cs, "%s %s { ... } section must contain at least one module", + cf_section_name1(cs), cf_section_name2(cs)); + return -1; + } + + /* + * Resolve all the modules in the redundant section. + */ + MEM(mri_arr = talloc_array(NULL, module_instance_t *, children)); + for (ci = cf_item_next(cs, NULL), i = 0; + ci; + ci = cf_item_next(cs, ci), i++) { + char const *name; if (!cf_item_is_pair(ci)) continue; - mod_func_name = cf_pair_attr(cf_item_to_pair(ci)); + name = cf_pair_attr(cf_item_to_pair(ci)); - /* - * This is ok, it just means the module - * doesn't have an xlat method. - * - * If there are ordering issues we could - * move this check to the instantiation - * function. - */ - mod_func = xlat_func_find(mod_func_name, talloc_array_length(mod_func_name) - 1); - if (!mod_func) { - talloc_free(xr); - return 1; + mri_arr[i] = module_rlm_static_by_name(NULL, name); + if (!mri_arr[i]) { + cf_log_err(ci, "Module '%s' not found. Referenced in %s %s { ... } section", + name, cf_section_name1(cs), cf_section_name2(cs)); + error: + talloc_free(mri_arr); + return -1; } + } - if (!args) { - args = mod_func->args; - } else { - fr_assert(args == mod_func->args); - } + /* + * Iterate over the xlats registered for the + * first module, verifying that the other + * module instances have all registered the + * similarly named xlat functions. + * + * We ignore any xlat functions that aren't + * available in all the modules. + */ + { + fr_rb_iter_inorder_t iter; + fr_sbuff_t *name; + fr_sbuff_marker_t name_start; + xlat_t *xlat; + module_rlm_xlat_t const *mrx; + module_rlm_instance_t *mri; + + FR_SBUFF_TALLOC_THREAD_LOCAL(&name, 128, SIZE_MAX); /* - * Degrade to a void return type if - * we have mixed types in a redundant - * section. + * Prepopulate the name buffer with . + * as every function wil be registered with this + * prefix. */ - if (!first) { - if (mod_func->return_type != return_type) return_type = FR_TYPE_VOID; - } else { - return_type = mod_func->return_type; - first = false; + if ((fr_sbuff_in_bstrcpy_buffer(name, cf_section_name2(cs)) <= 0) || + (fr_sbuff_in_char(name, '.') <= 0)) { + cf_log_perr(cs, "Name too long"); + return -1; } - MEM(xrf = talloc_zero(xr, xlat_redundant_func_t)); - xrf->func = mod_func; - fr_dlist_insert_tail(&xr->funcs, xrf); + fr_sbuff_marker(&name_start, name); + + mri = talloc_get_type_abort(mri_arr[0]->uctx, module_rlm_instance_t); /* - * Figure out pure status. If any of - * the children are un-pure then the - * whole redundant xlat is un-pure, - * same with async. + * Iterate over the xlats registered to the first + * module instance, searching for them in all the + * other module instances. */ - if (can_be_pure && mod_func->flags.pure) flags |= XLAT_FUNC_FLAG_PURE; - } + for (mrx = fr_rb_iter_init_inorder(&iter, &mri->xlats); + mrx; + mrx = fr_rb_iter_next_inorder(&iter)) { + xlat_func_flags_t flags = default_flags; + char const *name_p; + + (void)talloc_get_type(mrx, module_rlm_xlat_t); + + /* + * Allocate the redundant xlat structure + * (somehwat prematurely) with the expectation + * that the virtually every module of the same + * type will have registered the same xlat + * functions. + */ + MEM(xr = talloc_zero(NULL, xlat_redundant_t)); + xr->type = xr_type; + xr->cs = cs; + fr_dlist_talloc_init(&xr->funcs, xlat_redundant_func_t, entry); + + xlat_redundant_add_xlat(xr, mrx->xlat); + + /* + * We're only interested in xlat functions + * that are available in all the module + * instances. + */ + for (mri_p = mri_arr + 1; mri_p < mri_arr + children; mri_p++) { + module_rlm_xlat_t *found; + + found = talloc_get_type_abort(fr_rb_find(&mri->xlats, mrx), module_rlm_xlat_t); + if (!found) continue; + + xlat_redundant_add_xlat(xr, mrx->xlat); + + if (!found->xlat->flags.pure) flags &= ~XLAT_FUNC_FLAG_PURE; + } - /* - * At least one module xlat has to exist. - */ - if (!fr_dlist_num_elements(&xr->funcs)) { - talloc_free(xr); - return 1; - } + /* + * Warn, but allow about redundant/failover expansions + * that are neither redundant, no failover. + * + * It's sometime useful to comment out multiples during + * testing. + */ + if (fr_dlist_num_elements(&xr->funcs) == 1) { + cf_log_debug(cs, "%s expansion has no alternates, only %s", + fr_table_str_by_value(xlat_redundant_type_table, xr->type, ""), + mrx->xlat->name); - xlat = xlat_func_register(NULL, name2, xlat_redundant, return_type); - if (unlikely(xlat == NULL)) { - ERROR("Registering xlat for %s section failed", - fr_table_str_by_value(xlat_redundant_type_table, xr->type, "")); - talloc_free(xr); - return -1; + } + /* + * Where the xlat name is in the format . + * then the redundant xlat will be .. + * + * Where the xlat has no '.', it's likely just the module + * name, in which case we just use . + */ + name_p = strchr(mrx->xlat->name, '.'); + if (name_p) { + name_p++; + fr_sbuff_set(name, &name_start); /* Reset the aggregation buffer to the '.' */ + if (fr_sbuff_in_bstrncpy(name, name_p, strlen(name_p)) < 0) { + cf_log_perr(cs, "Name too long"); + goto error; + } + name_p = fr_sbuff_start(name); + } else { + name_p = cf_section_name2(cs); + } + + /* + * Register the new redundant xlat, and hang it off of + * the first module instance in the section. + * + * This isn't great, but at least the xlat should + * get unregistered at about the right time. + */ + xlat = xlat_func_register(mri_arr[0], name_p, xlat_redundant, return_type); + if (unlikely(xlat == NULL)) { + cf_log_err(cs, "Registering expansion for %s section failed", + fr_table_str_by_value(xlat_redundant_type_table, xr->type, "")); + talloc_free(xr); + return -1; + } + talloc_steal(xlat, xr); /* redundant xlat should own its own config */ + + cf_log_debug(cs, "Registered %s expansion \"%s\" with %u alternates", + fr_table_str_by_value(xlat_redundant_type_table, xr->type, ""), + xlat->name, fr_dlist_num_elements(&xr->funcs)); + + xlat_func_flags_set(xlat, flags); + xlat_func_instantiate_set(xlat, xlat_redundant_instantiate, xlat_redundant_inst_t, NULL, xr); + if (args) xlat_func_args_set(xlat, xlat_redundant_args); + } } - xlat_func_flags_set(xlat, flags); - xlat_func_instantiate_set(xlat, xlat_redundant_instantiate, xlat_redundant_inst_t, NULL, xr); - if (args) xlat_func_args_set(xlat, xlat_redundant_args); + + talloc_free(mri_arr); return 0; } diff --git a/src/modules/rlm_cache/rlm_cache.c b/src/modules/rlm_cache/rlm_cache.c index ccf92aec442..1e9171bb746 100644 --- a/src/modules/rlm_cache/rlm_cache.c +++ b/src/modules/rlm_cache/rlm_cache.c @@ -43,8 +43,8 @@ RCSID("$Id$") extern module_rlm_t rlm_cache; int submodule_parse(TALLOC_CTX *ctx, void *out, void *parent, CONF_ITEM *ci, conf_parser_t const *rule); -static int cache_key_parse(TALLOC_CTX *ctx, void *out, tmpl_rules_t const *t_rules, CONF_ITEM *ci, char const *section_name1, char const *section_name2, void const *data, call_env_parser_t const *rule); -static int cache_update_section_parse(TALLOC_CTX *ctx, call_env_parsed_head_t *out, tmpl_rules_t const *t_rules, CONF_ITEM *ci, char const *section_name1, char const *section_name2, void const *data, call_env_parser_t const *rule); +static int cache_key_parse(TALLOC_CTX *ctx, void *out, tmpl_rules_t const *t_rules, CONF_ITEM *ci, call_env_ctx_t const *cec, call_env_parser_t const *rule); +static int cache_update_section_parse(TALLOC_CTX *ctx, call_env_parsed_head_t *out, tmpl_rules_t const *t_rules, CONF_ITEM *ci, call_env_ctx_t const *cec, 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", @@ -118,10 +118,10 @@ int submodule_parse(TALLOC_CTX *ctx, void *out, void *parent, CONF_ITEM *ci, con } static int cache_key_parse(TALLOC_CTX *ctx, void *out, tmpl_rules_t const *t_rules, CONF_ITEM *ci, - char const *section_name1, char const *section_name2, void const *data, + call_env_ctx_t const *cec, call_env_parser_t const *rule) { - rlm_cache_t const *inst = talloc_get_type_abort_const(data, rlm_cache_t); + rlm_cache_t const *inst = talloc_get_type_abort_const(cec->mi->data, rlm_cache_t); call_env_parse_pair_t func = inst->driver->key_parse ? inst->driver->key_parse : call_env_parse_pair; tmpl_t *key_tmpl; fr_type_t cast; @@ -130,8 +130,7 @@ static int cache_key_parse(TALLOC_CTX *ctx, void *out, tmpl_rules_t const *t_rul * Call the custom key parse function, OR the standard call_env_parse_pair * function, depending on whether the driver calls a custom parsing function. */ - if (unlikely((ret = func(ctx, &key_tmpl, t_rules, ci, section_name1, section_name2, - inst->driver_submodule->data, rule)) < 0)) return ret; + if (unlikely((ret = func(ctx, &key_tmpl, t_rules, ci, inst->driver_submodule->data, rule)) < 0)) return ret; *((tmpl_t **)out) = key_tmpl; /* @@ -1418,8 +1417,8 @@ static int cache_verify(map_t *map, void *uctx) } static int cache_update_section_parse(TALLOC_CTX *ctx, call_env_parsed_head_t *out, tmpl_rules_t const *t_rules, - CONF_ITEM *ci, UNUSED char const *section_name1, UNUSED char const *section_name2, - UNUSED void const *data, UNUSED call_env_parser_t const *rule) + CONF_ITEM *ci, + UNUSED call_env_ctx_t const *cec, UNUSED call_env_parser_t const *rule) { CONF_SECTION *update = cf_item_to_section(ci); call_env_parsed_t *parsed; diff --git a/src/modules/rlm_detail/rlm_detail.c b/src/modules/rlm_detail/rlm_detail.c index 62c7e1c8e15..07bf835a47c 100644 --- a/src/modules/rlm_detail/rlm_detail.c +++ b/src/modules/rlm_detail/rlm_detail.c @@ -402,11 +402,11 @@ static unlang_action_t CC_HINT(nonnull) mod_post_auth(rlm_rcode_t *p_result, mod return detail_do(p_result, mctx, request, request->reply, &request->reply_pairs, false); } -static int call_env_filename_parse(TALLOC_CTX *ctx, void *out, tmpl_rules_t const *t_rules, CONF_ITEM *ci, - UNUSED char const *section_name1, UNUSED char const *section_name2, - void const *data, UNUSED call_env_parser_t const *rule) +static int call_env_filename_parse(TALLOC_CTX *ctx, void *out, tmpl_rules_t const *t_rules, + CONF_ITEM *ci, + call_env_ctx_t const *cec, UNUSED call_env_parser_t const *rule) { - rlm_detail_t const *inst = talloc_get_type_abort_const(data, rlm_detail_t); + rlm_detail_t const *inst = talloc_get_type_abort_const(cec->mi->data, rlm_detail_t); tmpl_t *parsed; CONF_PAIR const *to_parse = cf_item_to_pair(ci); tmpl_rules_t our_rules; @@ -427,8 +427,8 @@ static int call_env_filename_parse(TALLOC_CTX *ctx, void *out, tmpl_rules_t cons } static int call_env_suppress_parse(TALLOC_CTX *ctx, call_env_parsed_head_t *out, tmpl_rules_t const *t_rules, - CONF_ITEM *ci, UNUSED char const *section_name1, UNUSED char const *section_name2, - UNUSED void const *data, UNUSED call_env_parser_t const *rule) + CONF_ITEM *ci, + UNUSED call_env_ctx_t const *cec, UNUSED call_env_parser_t const *rule) { CONF_SECTION const *cs = cf_item_to_section(ci); CONF_SECTION const *parent = cf_item_to_section(cf_parent(ci)); diff --git a/src/modules/rlm_files/rlm_files.c b/src/modules/rlm_files/rlm_files.c index 79cd5ee539b..8efe9806712 100644 --- a/src/modules/rlm_files/rlm_files.c +++ b/src/modules/rlm_files/rlm_files.c @@ -627,13 +627,12 @@ static unlang_action_t CC_HINT(nonnull) mod_files(rlm_rcode_t *p_result, module_ * */ static int call_env_parse(TALLOC_CTX *ctx, void *out, tmpl_rules_t const *t_rules, CONF_ITEM *ci, - UNUSED char const *section_name1, UNUSED char const *section_name2, - void const *data, UNUSED call_env_parser_t const *rule) + call_env_ctx_t const *cec, UNUSED call_env_parser_t const *rule) { - rlm_files_t const *inst = talloc_get_type_abort_const(data, rlm_files_t); - CONF_PAIR const *to_parse = cf_item_to_pair(ci); - rlm_files_data_t *files_data; - fr_type_t keytype; + rlm_files_t const *inst = talloc_get_type_abort_const(cec->mi->data, rlm_files_t); + CONF_PAIR const *to_parse = cf_item_to_pair(ci); + rlm_files_data_t *files_data; + fr_type_t keytype; MEM(files_data = talloc_zero(ctx, rlm_files_data_t)); diff --git a/src/modules/rlm_ldap/rlm_ldap.c b/src/modules/rlm_ldap/rlm_ldap.c index 2319938300a..242f39d387d 100644 --- a/src/modules/rlm_ldap/rlm_ldap.c +++ b/src/modules/rlm_ldap/rlm_ldap.c @@ -77,9 +77,9 @@ typedef struct { map_list_t *profile_map; //!< List of maps to apply to the profile. } ldap_xlat_profile_call_env_t; -static int ldap_update_section_parse(TALLOC_CTX *ctx, call_env_parsed_head_t *out, tmpl_rules_t const *t_rules, CONF_ITEM *ci, char const *section_name1, char const *section_name2, void const *data, call_env_parser_t const *rule); +static int ldap_update_section_parse(TALLOC_CTX *ctx, call_env_parsed_head_t *out, tmpl_rules_t const *t_rules, CONF_ITEM *ci, call_env_ctx_t const *cec, call_env_parser_t const *rule); -static int ldap_group_filter_parse(TALLOC_CTX *ctx, void *out, tmpl_rules_t const *t_rules, CONF_ITEM *ci, UNUSED char const *section_name1, UNUSED char const *section_name2, void const *data, UNUSED call_env_parser_t const *rule); +static int ldap_group_filter_parse(TALLOC_CTX *ctx, void *out, tmpl_rules_t const *t_rules, CONF_ITEM *ci, call_env_ctx_t const *cec, UNUSED call_env_parser_t const *rule); static const call_env_parser_t sasl_call_env[] = { { FR_CALL_ENV_OFFSET("mech", FR_TYPE_STRING, CALL_ENV_FLAG_NONE, ldap_auth_call_env_t, user_sasl_mech) }, @@ -2230,8 +2230,8 @@ static int parse_sub_section(module_inst_ctx_t const *mctx, } static int ldap_update_section_parse(TALLOC_CTX *ctx, call_env_parsed_head_t *out, tmpl_rules_t const *t_rules, - CONF_ITEM *ci, UNUSED char const *section_name1, UNUSED char const *section_name2, - UNUSED void const *data, call_env_parser_t const *rule) + CONF_ITEM *ci, + UNUSED call_env_ctx_t const *cec, call_env_parser_t const *rule) { map_list_t *maps; CONF_SECTION *update = cf_item_to_section(ci); @@ -2309,12 +2309,11 @@ static int ldap_update_section_parse(TALLOC_CTX *ctx, call_env_parsed_head_t *ou } static int ldap_group_filter_parse(TALLOC_CTX *ctx, void *out, tmpl_rules_t const *t_rules, UNUSED CONF_ITEM *ci, - UNUSED char const *section_name1, UNUSED char const *section_name2, - void const *data, UNUSED call_env_parser_t const *rule) + call_env_ctx_t const *cec, UNUSED call_env_parser_t const *rule) { - rlm_ldap_t const *inst = talloc_get_type_abort_const(data, rlm_ldap_t); - char const *filters[] = { inst->group.obj_filter, inst->group.obj_membership_filter }; - tmpl_t *parsed; + rlm_ldap_t const *inst = talloc_get_type_abort_const(cec->mi->data, rlm_ldap_t); + char const *filters[] = { inst->group.obj_filter, inst->group.obj_membership_filter }; + tmpl_t *parsed; if (fr_ldap_filter_to_tmpl(ctx, t_rules, filters, NUM_ELEMENTS(filters), &parsed) < 0) return -1; diff --git a/src/modules/rlm_linelog/rlm_linelog.c b/src/modules/rlm_linelog/rlm_linelog.c index 7207f935233..c421f67f868 100644 --- a/src/modules/rlm_linelog/rlm_linelog.c +++ b/src/modules/rlm_linelog/rlm_linelog.c @@ -65,8 +65,7 @@ RCSID("$Id$") static int linelog_escape_func(fr_value_box_t *vb, UNUSED void *uctx); static int call_env_filename_parse(TALLOC_CTX *ctx, void *out, tmpl_rules_t const *t_rules, CONF_ITEM *ci, - UNUSED char const *section_name1, UNUSED char const *section_name2, - void const *data, UNUSED call_env_parser_t const *rule); + call_env_ctx_t const *cec, UNUSED call_env_parser_t const *rule); typedef enum { LINELOG_DST_INVALID = 0, LINELOG_DST_FILE, //!< Log to a file. @@ -876,13 +875,12 @@ build_vector: * Custom call env parser for filenames - sets the correct escaping function */ static int call_env_filename_parse(TALLOC_CTX *ctx, void *out, tmpl_rules_t const *t_rules, CONF_ITEM *ci, - UNUSED char const *section_name1, UNUSED char const *section_name2, - void const *data, UNUSED call_env_parser_t const *rule) + call_env_ctx_t const *cec, UNUSED call_env_parser_t const *rule) { - rlm_linelog_t const *inst = talloc_get_type_abort_const(data, rlm_linelog_t); - tmpl_t *parsed; - CONF_PAIR const *to_parse = cf_item_to_pair(ci); - tmpl_rules_t our_rules; + rlm_linelog_t const *inst = talloc_get_type_abort_const(cec->mi->data, rlm_linelog_t); + tmpl_t *parsed; + CONF_PAIR const *to_parse = cf_item_to_pair(ci); + tmpl_rules_t our_rules; /* * If we're not logging to a file destination, do nothing diff --git a/src/modules/rlm_smtp/rlm_smtp.c b/src/modules/rlm_smtp/rlm_smtp.c index 3fa81f000f3..8dcf707aa3b 100644 --- a/src/modules/rlm_smtp/rlm_smtp.c +++ b/src/modules/rlm_smtp/rlm_smtp.c @@ -942,8 +942,8 @@ static int mod_thread_detach(module_thread_inst_ctx_t const *mctx) * */ static int smtp_header_section_parse(TALLOC_CTX *ctx, call_env_parsed_head_t *out, tmpl_rules_t const *t_rules, - CONF_ITEM *ci, UNUSED char const *section_name1, UNUSED char const *section_name2, - UNUSED void const *data, UNUSED call_env_parser_t const *rule) + CONF_ITEM *ci, + UNUSED call_env_ctx_t const *cec, UNUSED call_env_parser_t const *rule) { CONF_SECTION const *cs = cf_item_to_section(ci); CONF_ITEM const *item = NULL; diff --git a/src/modules/rlm_sql/rlm_sql.c b/src/modules/rlm_sql/rlm_sql.c index 2bc7858942d..fff93a80022 100644 --- a/src/modules/rlm_sql/rlm_sql.c +++ b/src/modules/rlm_sql/rlm_sql.c @@ -120,10 +120,10 @@ static const call_env_method_t authorize_method_env = { }; static int logfile_call_env_parse(TALLOC_CTX *ctx, call_env_parsed_head_t *out, tmpl_rules_t const *t_rules, CONF_ITEM *cc, - char const *section_name1, char const *section_name2, void const *data, call_env_parser_t const *rule); + call_env_ctx_t const *cec, call_env_parser_t const *rule); static int query_call_env_parse(TALLOC_CTX *ctx, call_env_parsed_head_t *out, tmpl_rules_t const *t_rules, CONF_ITEM *cc, - char const *section_name1, char const *section_name2, void const *data, call_env_parser_t const *rule); + call_env_ctx_t const *cec, call_env_parser_t const *rule); typedef struct { fr_value_box_t filename; @@ -1848,8 +1848,8 @@ static unlang_action_t CC_HINT(nonnull) mod_sql_redundant(rlm_rcode_t *p_result, } static int logfile_call_env_parse(TALLOC_CTX *ctx, call_env_parsed_head_t *out, tmpl_rules_t const *t_rules, - CONF_ITEM *ci, char const *section_name1, char const *section_name2, - UNUSED void const *data, UNUSED call_env_parser_t const *rule) + CONF_ITEM *ci, + UNUSED call_env_ctx_t const *cec, UNUSED call_env_parser_t const *rule) { CONF_SECTION const *subcs = NULL, *subsubcs = NULL; CONF_PAIR const *to_parse = NULL; @@ -1858,6 +1858,8 @@ static int logfile_call_env_parse(TALLOC_CTX *ctx, call_env_parsed_head_t *out, tmpl_rules_t our_rules; char *section2, *p; + fr_assert(cec->type == CALL_ENV_CTX_TYPE_MODULE); + /* * The call env subsection which calls this has CF_IDENT_ANY as its name * which results in finding the first child section of the module config. @@ -1875,10 +1877,10 @@ static int logfile_call_env_parse(TALLOC_CTX *ctx, call_env_parsed_head_t *out, * falling back to * { logfile } */ - subcs = cf_section_find(cf_item_to_section(ci), section_name1, CF_IDENT_ANY); + subcs = cf_section_find(cf_item_to_section(ci), cec->asked->name1, CF_IDENT_ANY); if (subcs) { - if (section_name2) { - section2 = talloc_strdup(NULL, section_name2); + if (cec->asked->name2) { + section2 = talloc_strdup(NULL, cec->asked->name2); p = section2; while (*p != '\0') { *(p) = tolower((uint8_t)*p); @@ -1923,10 +1925,10 @@ static int logfile_call_env_parse(TALLOC_CTX *ctx, call_env_parsed_head_t *out, } static int query_call_env_parse(TALLOC_CTX *ctx, call_env_parsed_head_t *out, tmpl_rules_t const *t_rules, - CONF_ITEM *ci, UNUSED char const *section_name1, char const *section_name2, - void const *data, UNUSED call_env_parser_t const *rule) + CONF_ITEM *ci, + call_env_ctx_t const *cec, UNUSED call_env_parser_t const *rule) { - rlm_sql_t const *inst = talloc_get_type_abort_const(data, rlm_sql_t); + rlm_sql_t const *inst = talloc_get_type_abort_const(cec->mi->data, rlm_sql_t); CONF_SECTION const *subcs = NULL; CONF_PAIR const *to_parse = NULL; tmpl_t *parsed_tmpl; @@ -1935,7 +1937,7 @@ static int query_call_env_parse(TALLOC_CTX *ctx, call_env_parsed_head_t *out, tm char *section2, *p; ssize_t count, slen, multi_index = 0; - if (!section_name2) return -1; + fr_assert(cec->type == CALL_ENV_CTX_TYPE_MODULE); /* * Find the instance(s) of "query" to parse @@ -1943,15 +1945,19 @@ static int query_call_env_parse(TALLOC_CTX *ctx, call_env_parsed_head_t *out, tm * If the module call is from `accounting Start` then it should be * { accounting { start { query } } } */ - section2 = talloc_strdup(NULL, section_name2); + section2 = talloc_strdup(NULL, section_name_str(cec->asked->name2)); p = section2; while (*p != '\0') { *(p) = tolower((uint8_t)*p); p++; } subcs = cf_section_find(cf_item_to_section(ci), section2, CF_IDENT_ANY); + if (!subcs) { + cf_log_debug(ci, "No query found for \"%s\", this query will be disabled...", section2); + talloc_free(section2); + return 0; + } talloc_free(section2); - if (!subcs) return 0; /* * Use module specific escape functions @@ -1966,11 +1972,14 @@ static int query_call_env_parse(TALLOC_CTX *ctx, call_env_parsed_head_t *out, tm while ((to_parse = cf_pair_find_next(subcs, to_parse, "query"))) { MEM(parsed_env = call_env_parsed_add(ctx, out, - &(call_env_parser_t){ FR_CALL_ENV_PARSE_ONLY_OFFSET("query", FR_TYPE_STRING, CALL_ENV_FLAG_CONCAT | CALL_ENV_FLAG_MULTI, sql_redundant_call_env_t, query)})); + &(call_env_parser_t){ + FR_CALL_ENV_PARSE_ONLY_OFFSET("query", FR_TYPE_STRING, CALL_ENV_FLAG_CONCAT | CALL_ENV_FLAG_MULTI, + sql_redundant_call_env_t, query) + })); slen = tmpl_afrom_substr(parsed_env, &parsed_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, &our_rules); + &FR_SBUFF_IN(cf_pair_value(to_parse), talloc_array_length(cf_pair_value(to_parse)) - 1), + cf_pair_value_quote(to_parse), NULL, &our_rules); if (slen <= 0) { cf_canonicalize_error(to_parse, slen, "Failed parsing query", cf_pair_value(to_parse)); error: @@ -2215,8 +2224,8 @@ static int mod_bootstrap(module_inst_ctx_t const *mctx) * The xlat escape function needs access to inst - so * argument parser details need to be defined here */ - sql_xlat_arg = talloc_zero_array(xlat, xlat_arg_parser_t, 2); - uctx = talloc_zero(sql_xlat_arg, rlm_sql_escape_uctx_t); + MEM(sql_xlat_arg = talloc_zero_array(xlat, xlat_arg_parser_t, 2)); + MEM(uctx = talloc_zero(sql_xlat_arg, rlm_sql_escape_uctx_t)); *uctx = (rlm_sql_escape_uctx_t){ .sql = inst, .handle = NULL }; sql_xlat_arg[0] = (xlat_arg_parser_t){ .type = FR_TYPE_STRING, diff --git a/src/modules/rlm_sqlcounter/rlm_sqlcounter.c b/src/modules/rlm_sqlcounter/rlm_sqlcounter.c index 83ce70c732d..1bc7cc21b3b 100644 --- a/src/modules/rlm_sqlcounter/rlm_sqlcounter.c +++ b/src/modules/rlm_sqlcounter/rlm_sqlcounter.c @@ -443,10 +443,9 @@ static unlang_action_t CC_HINT(nonnull) mod_authorize(rlm_rcode_t *p_result, mod /** Custom call_env parser to tokenize the SQL query xlat used for counter retrieval */ static int call_env_query_parse(TALLOC_CTX *ctx, void *out, tmpl_rules_t const *t_rules, CONF_ITEM *ci, - UNUSED char const *section_name1, UNUSED char const *section_name2, - void const *data, UNUSED call_env_parser_t const *rule) + call_env_ctx_t const *cec, UNUSED call_env_parser_t const *rule) { - rlm_sqlcounter_t const *inst = talloc_get_type_abort_const(data, rlm_sqlcounter_t); + rlm_sqlcounter_t const *inst = talloc_get_type_abort_const(cec->mi->data, rlm_sqlcounter_t); CONF_PAIR const *to_parse = cf_item_to_pair(ci); char *query; xlat_exp_head_t *ex; diff --git a/src/modules/rlm_winbind/rlm_winbind.c b/src/modules/rlm_winbind/rlm_winbind.c index 688982fd485..8d1fb7990f1 100644 --- a/src/modules/rlm_winbind/rlm_winbind.c +++ b/src/modules/rlm_winbind/rlm_winbind.c @@ -24,11 +24,11 @@ * @copyright 2016 The FreeRADIUS server project * @copyright 2016 Matthew Newton (matthew@newtoncomputing.co.uk) */ - RCSID("$Id$") #include #include +#include #include #include @@ -438,8 +438,7 @@ static const call_env_method_t winbind_autz_method_env = { }; static int domain_call_env_parse(TALLOC_CTX *ctx, void *out, tmpl_rules_t const *t_rules, CONF_ITEM *ci, - UNUSED char const *section_name1, UNUSED char const *section_name2, - UNUSED void const *data, UNUSED call_env_parser_t const *rule) + UNUSED call_env_ctx_t const *cec, UNUSED call_env_parser_t const *rule) { CONF_PAIR const *to_parse = cf_item_to_pair(ci); tmpl_t *parsed_tmpl = NULL; diff --git a/src/tests/keywords/radius.conf b/src/tests/keywords/radius.conf index 037e4ce7999..346b8fdc9e6 100644 --- a/src/tests/keywords/radius.conf +++ b/src/tests/keywords/radius.conf @@ -99,8 +99,8 @@ $TEMPLATE template_test } redundant redundant_test { - test1.passthrough - test2.passthrough + test1 + test2 } } diff --git a/src/tests/keywords/xlat-redundant b/src/tests/keywords/xlat-redundant index a042226f3ed..5d84ae7b911 100644 --- a/src/tests/keywords/xlat-redundant +++ b/src/tests/keywords/xlat-redundant @@ -13,7 +13,7 @@ if (!(%concat(%test2.passthrough(foo, bar), '|') == "foo|bar")) { # # The config has a "redundant" block for test1 and test2. # -if (!(%concat(%redundant_test(foo, bar), '|') == "foo|bar")) { +if (!(%concat(%redundant_test.passthrough(foo, bar), '|') == "foo|bar")) { test_fail }