.allow_unresolved = cc->tmpl_rules.attr.allow_unresolved
},
.xlat = cc->tmpl_rules.xlat,
- });
+ },
+ 0);
if (dec_len <= 0) {
fr_strerror_printf_push_head("ERROR offset %d", (int) -dec_len);
};
- slen = xlat_tokenize(xlat_ctx, &head, &line, &p_rules, &t_rules);
+ slen = xlat_tokenize(xlat_ctx, &head, &line, &p_rules, &t_rules, 0);
if (slen <= 0) {
talloc_free(xlat_ctx);
fr_sbuff_in_sprintf(&out, "ERROR offset %d '%s'", (int) -slen, fr_strerror());
EXIT_WITH_FAILURE;
}
- if (map_proc_register(NULL, "test-fail", mod_map_proc, map_proc_verify, 0) < 0) {
+ if (map_proc_register(NULL, "test-fail", mod_map_proc, map_proc_verify, 0, 0) < 0) {
EXIT_WITH_FAILURE;
}
.allow_unresolved = false,
.allow_foreign = (dict == NULL)
},
- });
+ }, 0);
if (slen < 0) {
char *spaces, *text;
* @copyright 2015 Arran Cudbard-bell (a.cudbardb@freeradius.org)
*/
+
RCSID("$Id$")
#include <freeradius-devel/server/base.h>
#include <freeradius-devel/server/map_proc_priv.h>
#include <freeradius-devel/util/debug.h>
+#include <freeradius-devel/util/value.h>
static fr_rb_tree_t *map_proc_root = NULL;
* @param[in] evaluate Module's map processor function.
* @param[in] instantiate function (optional).
* @param[in] inst_size of talloc chunk to allocate for instance data (optional).
+ * @param[in] literals_safe_for What safe_for value to assign to literals.
* @return
* - 0 on success.
* - -1 on failure.
*/
int map_proc_register(void *mod_inst, char const *name,
map_proc_func_t evaluate,
- map_proc_instantiate_t instantiate, size_t inst_size)
+ map_proc_instantiate_t instantiate, size_t inst_size, fr_value_box_safe_for_t literals_safe_for)
{
map_proc_t *proc;
proc->evaluate = evaluate;
proc->instantiate = instantiate;
proc->inst_size = inst_size;
+ proc->literals_safe_for = literals_safe_for;
return 0;
}
#include <freeradius-devel/server/cf_util.h>
#include <freeradius-devel/server/tmpl.h>
#include <freeradius-devel/server/map.h>
+#include <freeradius-devel/util/value.h>
#ifdef __cplusplus
extern "C" {
void map_proc_free(void);
int map_proc_register(void *mod_inst, char const *name,
map_proc_func_t evaluate,
- map_proc_instantiate_t instantiate, size_t inst_size);
+ map_proc_instantiate_t instantiate, size_t inst_size, fr_value_box_safe_for_t safe_for);
map_proc_inst_t *map_proc_instantiate(TALLOC_CTX *ctx, map_proc_t const *proc,
CONF_SECTION *cs, tmpl_t const *src, map_list_t const *maps);
#include <freeradius-devel/server/modpriv.h>
#include <freeradius-devel/server/map_proc.h>
#include <freeradius-devel/util/debug.h>
+#include <freeradius-devel/util/value.h>
#ifdef __cplusplus
extern "C" {
map_proc_func_t evaluate; //!< Module's map processor function.
map_proc_instantiate_t instantiate; //!< Callback to create new instance struct.
size_t inst_size; //!< Size of map_proc instance data to allocate.
+ fr_value_box_safe_for_t literals_safe_for; //!< Safe for values to be set for literals in the map source.
};
/** Map processor instance
bool new_functions; //!< new function syntax
};
-struct tmpl_literal_rules_s {
- fr_value_box_safe_for_t safe_for; //!< What should literals be marked up safe for
-};
-
/** Optional arguments passed to vp_tmpl functions
*
*/
struct tmpl_rules_s {
- tmpl_rules_t const *parent; //!< for parent / child relationships
-
- tmpl_attr_rules_t attr; //!< Rules/data for parsing attribute references.
- tmpl_xlat_rules_t xlat; //!< Rules/data for parsing xlats.
-
- fr_dict_attr_t const *enumv; //!< Enumeration attribute used to resolve enum values.
+ tmpl_rules_t const *parent; //!< for parent / child relationships
- fr_type_t cast; //!< Whether there was an explicit cast.
- ///< Used to determine if barewords or other values
- ///< should be converted to an internal data type.
+ tmpl_attr_rules_t attr; //!< Rules/data for parsing attribute references.
+ tmpl_xlat_rules_t xlat; //!< Rules/data for parsing xlats.
- bool at_runtime; //!< Produce an ephemeral/runtime tmpl.
- ///< Instantiated xlats are not added to the global
- ///< trees, regexes are not JIT'd.
+ fr_dict_attr_t const *enumv; //!< Enumeration attribute used to resolve enum values.
- tmpl_escape_t escape; //!< How escaping should be handled during evaluation.
+ fr_type_t cast; //!< Whether there was an explicit cast.
+ ///< Used to determine if barewords or other values
+ ///< should be converted to an internal data type.
- tmpl_literal_rules_t literal; //!< Rules for parsing literals.
+ bool at_runtime; //!< Produce an ephemeral/runtime tmpl.
+ ///< Instantiated xlats are not added to the global
+ ///< trees, regexes are not JIT'd.
+ fr_value_box_safe_for_t literals_safe_for; //!< safe_for value assigned to literal values in
+ ///< xlats, execs, and data.
+ tmpl_escape_t escape; //!< How escaping should be handled during evaluation.
};
/** Similar to tmpl_rules_t, but used to specify parameters that may change during subsequent resolution passes
#include <freeradius-devel/util/misc.h>
#include <freeradius-devel/util/sbuff.h>
+#include <freeradius-devel/util/value.h>
#include <ctype.h>
talloc_free(vpt);
FR_SBUFF_ERROR_RETURN(&our_in);
}
+ fr_value_box_mark_safe_for(&tmp, t_rules->literals_safe_for);
tmpl_init(vpt, TMPL_TYPE_DATA, quote, fr_sbuff_start(&our_in), fr_sbuff_used(&our_in), t_rules);
xlat_exp_head_t *head = NULL;
vpt = tmpl_alloc_null(ctx);
- slen = xlat_tokenize(vpt, &head, &our_in, p_rules, t_rules);
+ slen = xlat_tokenize(vpt, &head, &our_in, p_rules, t_rules, t_rules->literals_safe_for);
if (slen <= 0) FR_SBUFF_ERROR_RETURN(&our_in);
if (xlat_needs_resolving(head)) UNRESOLVED_SET(&type);
my_t_rules.cast = my_t_rules.enumv->type;
- return tmpl_afrom_value_substr(ctx, out, in, quote,
- &my_t_rules, true, p_rules);
+ return tmpl_afrom_value_substr(ctx, out, in, quote, &my_t_rules, true, p_rules);
}
/*
vpt = tmpl_alloc_null(ctx);
- slen = xlat_tokenize(vpt, &head, &our_in, p_rules, t_rules);
+ slen = xlat_tokenize(vpt, &head, &our_in, p_rules, t_rules, t_rules->literals_safe_for);
if (slen < 0) FR_SBUFF_ERROR_RETURN(&our_in);
/*
* Ensure any xlats produced are bootstrapped
* so that their instance data will be created.
*/
- if (xlat_finalize(head, t_rules) < 0) {
+ if (xlat_finalize(head, t_rules->xlat.runtime_el) < 0) {
fr_strerror_const("Failed to bootstrap xlat");
FR_SBUFF_ERROR_RETURN(&our_in);
}
vpt = tmpl_alloc_null(ctx);
- slen = xlat_tokenize(vpt, &head, &our_in, p_rules, t_rules);
+ slen = xlat_tokenize(vpt, &head, &our_in, p_rules, t_rules, t_rules->literals_safe_for);
if (slen < 0) FR_SBUFF_ERROR_RETURN(&our_in);
/*
}
}
- if (xlat_finalize(trigger->xlat, &(tmpl_rules_t ) {
- .xlat = {
- .runtime_el = intp ? unlang_interpret_event_list(request) : main_loop_event_list(),
- },
- .at_runtime = true
- }) < 0) {
+ if (xlat_finalize(trigger->xlat, intp ? unlang_interpret_event_list(request) : main_loop_event_list()) < 0) {
fr_strerror_const("Failed performing ephemeral instantiation for xlat");
talloc_free(request);
return -1;
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;
call_env_parsed = call_env_parsed_alloc(ctx, rule);
call_env_parsed->count = count;
*
* @copyright 2023 Network RADIUS SAS (legal@networkradius.com)
*/
+#include "lib/util/value.h"
RCSIDH(call_env_h, "$Id$")
#ifdef __cplusplus
call_env_parse_type_t type; //!< What type of output the parsing phase is expected to produce.
} parsed;
+ fr_value_box_safe_for_t literals_safe_for; //!< What safe_for value to assign any literals that are arguments to the tmpl_t.
tmpl_escape_t escape; //!< Escape method to use when evaluating tmpl_t.
} pair;
cf_log_err(cs, "Failed to find map processor '%s'", name2);
return NULL;
}
+ t_rules.literals_safe_for = proc->literals_safe_for;
g = group_allocate(parent, cs, &map_ext);
if (!g) return NULL;
fr_sbuff_parse_rules_t const *p_rules, tmpl_rules_t const *t_rules, bool comma, bool allow_attr);
fr_slen_t xlat_tokenize(TALLOC_CTX *ctx, xlat_exp_head_t **head, fr_sbuff_t *in,
- fr_sbuff_parse_rules_t const *p_rules, tmpl_rules_t const *t_rules);
+ fr_sbuff_parse_rules_t const *p_rules, tmpl_rules_t const *t_rules,
+ fr_value_box_safe_for_t literals_safe_for);
fr_slen_t xlat_print(fr_sbuff_t *in, xlat_exp_head_t const *node, fr_sbuff_escape_rules_t const *e_rules);
int xlat_instance_register_func(xlat_exp_t *node);
-int xlat_finalize(xlat_exp_head_t *head, tmpl_rules_t const *t_rules); /* xlat_instance_register() or xlat_instantiate_ephemeral() */
+int xlat_finalize(xlat_exp_head_t *head, fr_event_list_t *runtime_el); /* xlat_instance_register() or xlat_instantiate_ephemeral() */
void xlat_instances_free(void);
.runtime_el = unlang_interpret_event_list(request),
},
.at_runtime = true
- }) < 0) {
+ }, 0) < 0) {
RPEDEBUG("Failed parsing expansion");
error:
talloc_free(rctx);
.runtime_el = unlang_interpret_event_list(request),
},
.at_runtime = true,
- });
+ }, 0);
if (len == 0) {
if (*out) {
**out = '\0';
* Add nodes that need to be bootstrapped to
* the registry.
*/
- if (xlat_finalize(head, t_rules) < 0) {
+ if (xlat_finalize(head, t_rules->xlat.runtime_el) < 0) {
talloc_free(head);
return -1;
}
}
fr_slen_t xlat_tokenize_expression(TALLOC_CTX *ctx, xlat_exp_head_t **out, fr_sbuff_t *in,
- fr_sbuff_parse_rules_t const *p_rules, tmpl_rules_t const *t_rules)
+ fr_sbuff_parse_rules_t const *p_rules, tmpl_rules_t const *t_rules)
{
return xlat_tokenize_expression_internal(ctx, out, in, p_rules, t_rules, false);
}
fr_slen_t xlat_tokenize_condition(TALLOC_CTX *ctx, xlat_exp_head_t **out, fr_sbuff_t *in,
- fr_sbuff_parse_rules_t const *p_rules, tmpl_rules_t const *t_rules)
+ fr_sbuff_parse_rules_t const *p_rules, tmpl_rules_t const *t_rules)
{
return xlat_tokenize_expression_internal(ctx, out, in, p_rules, t_rules, true);
}
/** Bootstrap static xlats, or instantiate ephemeral ones.
*
- * @param[in] head of xlat tree to create instance data for.
- * @param[in] t_rules parsing rules with #fr_event_list_t
+ * @param[in] head of xlat tree to create instance data for.
+ * @param[in] runtime_el determines whether we do ephemeral or static instantiation.
+ * If NULL, we perform static instantiation, otherwise
+ * will perform ephemeral instantiation passing the el to
+ * the instantiation functions.
*/
-int xlat_finalize(xlat_exp_head_t *head, tmpl_rules_t const *t_rules)
+int xlat_finalize(xlat_exp_head_t *head, fr_event_list_t *runtime_el)
{
- if (!t_rules || !t_rules->at_runtime) {
- fr_assert(!t_rules || !t_rules->xlat.runtime_el);
+ if (!runtime_el) {
return xlat_instance_register(head);
}
- return xlat_instantiate_ephemeral(head, t_rules->xlat.runtime_el);
+ return xlat_instantiate_ephemeral(head, runtime_el);
}
/** Walk over all registered instance data and free them explicitly
* @copyright 2000,2006 The FreeRADIUS server project
*/
+#include "lib/util/event.h"
#include "lib/util/value.h"
RCSID("$Id$")
* - -1 on failure.
*/
static int xlat_tokenize_input(xlat_exp_head_t *head, fr_sbuff_t *in,
- fr_sbuff_parse_rules_t const *p_rules, tmpl_rules_t const *t_rules, fr_value_box_safe_for_t safe_for)
+ fr_sbuff_parse_rules_t const *p_rules, tmpl_rules_t const *t_rules,
+ fr_value_box_safe_for_t safe_for)
{
xlat_exp_t *node = NULL;
fr_slen_t slen;
/** Tokenize an xlat expansion
*
- * @param[in] ctx to allocate dynamic buffers in.
- * @param[out] out the head of the xlat list / tree structure.
- * @param[in] in the format string to expand.
- * @param[in] p_rules controlling how the string containing the xlat
- * expansions should be parsed.
- * @param[in] t_rules controlling how attribute references are parsed.
+ * @param[in] ctx to allocate dynamic buffers in.
+ * @param[out] out the head of the xlat list / tree structure.
+ * @param[in] in the format string to expand.
+ * @param[in] p_rules controlling how the string containing the xlat
+ * expansions should be parsed.
+ * @param[in] t_rules controlling how attribute references are parsed.
+ * Do NOT alter this function to take tmpl_rules_t
+ * as this provides another value for literals_safe_for
+ * and this gets very confusing.
+ * @param[in] literals_safe_for the safe_for value to assign to any literals occurring at the
+ * top level of the expansion.
* @return
* - >0 on success.
* - 0 and *head == NULL - Parse failure on first char.
* - < 0 the negative offset of the parse failure.
*/
fr_slen_t xlat_tokenize(TALLOC_CTX *ctx, xlat_exp_head_t **out, fr_sbuff_t *in,
- fr_sbuff_parse_rules_t const *p_rules, tmpl_rules_t const *t_rules)
+ fr_sbuff_parse_rules_t const *p_rules, tmpl_rules_t const *t_rules,
+ fr_value_box_safe_for_t literals_safe_for)
{
fr_sbuff_t our_in = FR_SBUFF(in);
xlat_exp_head_t *head;
- MEM(head = xlat_exp_head_alloc(ctx));
- if (t_rules) {
- fr_assert(!t_rules->at_runtime || t_rules->xlat.runtime_el); /* if it's at runtime, we need an event list */
- }
+ MEM(head = xlat_exp_head_alloc(ctx));
fr_strerror_clear(); /* Clear error buffer */
- if (xlat_tokenize_input(head, &our_in, p_rules, t_rules, t_rules ? t_rules->literal.safe_for : 0) < 0) {
+ if (xlat_tokenize_input(head, &our_in, p_rules, t_rules, literals_safe_for) < 0) {
talloc_free(head);
FR_SBUFF_ERROR_RETURN(&our_in);
}
* Add nodes that need to be bootstrapped to
* the registry.
*/
- if (xlat_finalize(head, t_rules) < 0) {
+ if (xlat_finalize(head, t_rules->xlat.runtime_el) < 0) {
talloc_free(head);
return 0;
}
*
* @copyright 2021 The FreeRADIUS server project
*/
+#include "lib/util/sbuff.h"
+#include "lib/util/table.h"
+#include "lib/util/value.h"
RCSID("$Id$")
#include "uri.h"
*
* @note This function has a signature compatible with fr_uri_escape_func_t.
*
+ * @note This function may modify the type of boxes, as all boxes in the list are
+ * cast to strings before parsing.
+ *
* @param[in,out] uri_vb to escape
* @param[in] uctx A fr_uri_escape_ctx_t containing the initial fr_uri_part_t
* and the uctx to pass to the escaping function.
fr_sbuff_t sbuff;
uint8_t adv;
- if (uri_vb->tainted && !ctx->uri_part->tainted_allowed) {
- fr_strerror_printf_push("Tainted value not allowed for %s", ctx->uri_part->name);
- return -1;
- }
-
/*
* Ensure boxes are strings before attempting to escape.
*/
/*
* Tainted boxes can only belong to a single part of the URI
*/
- if (uri_vb->tainted) {
+ if ((ctx->uri_part->safe_for > 0) && !fr_value_box_is_safe_for(uri_vb, ctx->uri_part->safe_for)) {
if (ctx->uri_part->func) {
/*
* Escaping often ends up breaking the vb's list pointers
fr_strerror_printf_push("Unable to escape tainted input %pV", uri_vb);
return -1;
}
+ fr_value_box_mark_safe_for(uri_vb, ctx->uri_part->safe_for);
uri_vb->entry = entry;
+ } else {
+ fr_strerror_printf_push("Unsafe input \"%pV\" not allowed in URI part %s", uri_vb, ctx->uri_part->name);
+ return -1;
}
return 0;
}
* definition in uri_parts. Tainted values, where allowed, are escaped
* using the function specified for the uri part.
*
+ * @note This function may modify the type of boxes, as all boxes in the list are
+ * cast to strings before parsing.
+ *
* @param uri to parse. A list of string type value boxes containing
* fragments of a URI.
* @param uri_parts definition of URI structure. Should point to the start
return 0;
}
+
+/** Searches for a matching scheme in the table of schemes, using a list of value boxes representing the URI
+ *
+ * @note Unlikel
+ *
+ * @param uri to parse. A list of string type value boxes containing
+ * fragments of a URI.
+ * @param schemes Table of schemes to search.
+ * @param schemes_len Number of schemes in the table.
+ * @param def Default scheme to use if none is found.
+ * @return The matching scheme, or def if none is found.
+ */
+int fr_uri_has_scheme(fr_value_box_list_t *uri, fr_table_num_sorted_t const *schemes, size_t schemes_len, int def)
+{
+ char scheme_buff[20]; /* hopefully no schemes over 20 bytes */
+ fr_sbuff_t sbuff = FR_SBUFF_OUT(scheme_buff, sizeof(scheme_buff));
+
+ /*
+ * Fill the scheme buffer with at most sizeof(scheme_buff) - 1 bytes of string data.
+ */
+ fr_value_box_list_foreach_safe(uri, vb) {
+ fr_value_box_t tmp;
+ int ret;
+
+ if (unlikely(vb->type != FR_TYPE_STRING)) {
+ if (unlikely(fr_value_box_cast(NULL, &tmp, FR_TYPE_STRING, vb->enumv, vb) < 0)) {
+ fr_strerror_printf_push("Unable to cast %pV to a string", vb);
+ return 0;
+ }
+ ret = fr_sbuff_in_bstrncpy(&sbuff, tmp.vb_strvalue,
+ fr_sbuff_remaining(&sbuff) > tmp.vb_length ? tmp.vb_length : fr_sbuff_remaining(&sbuff));
+ fr_value_box_clear_value(&tmp);
+ } else {
+ ret = fr_sbuff_in_bstrncpy(&sbuff, vb->vb_strvalue,
+ fr_sbuff_remaining(&sbuff) > vb->vb_length ? vb->vb_length : fr_sbuff_remaining(&sbuff));
+ }
+
+ if (unlikely(ret < 0)) return -1;
+ }}
+
+ /*
+ * Ensure the first box is a valid scheme
+ */
+ return fr_table_value_by_longest_prefix(NULL, schemes, fr_sbuff_start(&sbuff), fr_sbuff_used(&sbuff), def);
+}
*
*/
typedef struct {
- char const *name; //!< Name of this part of the URI
- fr_sbuff_term_t const *terminals; //!< Characters that mark the end of this part.
- uint8_t const part_adv[UINT8_MAX + 1]; //!< How many parts to advance for a specific terminal
- size_t extra_skip; //!< How many additional characters to skip after
- ///< the terminal
- bool tainted_allowed; //!< Do we accept tainted values for this part
- fr_uri_escape_func_t func; //!< Function to use to escape tainted values
+ char const *name; //!< Name of this part of the URI
+ fr_sbuff_term_t const *terminals; //!< Characters that mark the end of this part.
+ uint8_t const part_adv[UINT8_MAX + 1]; //!< How many parts to advance for a specific terminal
+ size_t extra_skip; //!< How many additional characters to skip after
+ ///< the terminal
+ fr_value_box_safe_for_t safe_for; //!< What type of value is safe for this part
+ fr_uri_escape_func_t func; //!< Function to use to escape tainted values
} fr_uri_part_t;
/** uctx to pass to fr_uri_escape
void *uctx; //!< to pass to fr_uri_escape_func_t.
} fr_uri_escape_ctx_t;
-#define XLAT_URI_PART_TERMINATOR { .name = NULL, .terminals = NULL, .tainted_allowed = false, .func = NULL }
+#define XLAT_URI_PART_TERMINATOR { .name = NULL, .terminals = NULL, .safe_for = 0, .func = NULL }
int fr_uri_escape_list(fr_value_box_list_t *uri, fr_uri_part_t const *uri_parts, void *uctx);
int fr_uri_escape(fr_value_box_t *uri_vb, void *uctx);
+int fr_uri_has_scheme(fr_value_box_list_t *uri, fr_table_num_sorted_t const *schemes, size_t schemes_len, int def);
+
#ifdef __cplusplus
}
#endif
if (unlikely((xlat = xlat_func_register(NULL, "client", xlat_client, FR_TYPE_STRING)) == NULL)) return -1;
xlat_func_args_set(xlat, xlat_client_args);
- map_proc_register(NULL, "client", map_proc_client, NULL, 0);
+ map_proc_register(NULL, "client", map_proc_client, NULL, 0, 0);
return 0;
}
/*
* And register the `map csv <key> { ... }` function.
*/
- map_proc_register(inst, mctx->inst->name, mod_map_proc, csv_maps_verify, 0);
+ map_proc_register(inst, mctx->inst->name, mod_map_proc, csv_maps_verify, 0, 0);
return 0;
}
-
/** Instantiate the module
*
* Creates a new instance of the module reading parameters from a configuration section.
fr_json_format_verify(format, true);
if (map_proc_register(inst, "json", mod_map_proc,
- mod_map_proc_instantiate, sizeof(rlm_json_jpath_cache_t)) < 0) return -1;
+ mod_map_proc_instantiate, sizeof(rlm_json_jpath_cache_t), 0) < 0) return -1;
return 0;
}
USES_APPLE_DEPRECATED_API
#include <freeradius-devel/util/debug.h>
+#include <freeradius-devel/util/table.h>
#include <freeradius-devel/util/uri.h>
+#include <freeradius-devel/util/value.h>
#include <freeradius-devel/ldap/conf.h>
#include <freeradius-devel/ldap/base.h>
fr_ldap_map_exp_t expanded;
} ldap_map_ctx_t;
+typedef enum {
+ LDAP_SCHEME_UNIX = 0,
+ LDAP_SCHEME_TCP,
+ LDAP_SCHEME_TCP_SSL
+} ldap_schemes_t;
+
+static fr_table_num_sorted_t const ldap_uri_scheme_table[] = {
+ { L("ldap://"), LDAP_SCHEME_UNIX },
+ { L("ldapi://"), LDAP_SCHEME_TCP },
+ { L("ldaps://"), LDAP_SCHEME_TCP_SSL },
+};
+static size_t ldap_uri_scheme_table_len = NUM_ELEMENTS(ldap_uri_scheme_table);
+
+/** This is the common function that actually ends up doing all the URI escaping
+ */
+#define LDAP_URI_SAFE_FOR (fr_value_box_safe_for_t)fr_ldap_escape_func
+
static xlat_arg_parser_t const ldap_escape_xlat_arg[] = {
- { .required = true, .concat = true, .type = FR_TYPE_STRING },
+ { .required = true, .concat = true, .type = FR_TYPE_STRING, .safe_for = LDAP_URI_SAFE_FOR },
XLAT_ARG_PARSER_TERMINATOR
};
*/
fr_sbuff_trim_talloc(&sbuff, len);
fr_value_box_strdup_shallow(vb, NULL, fr_sbuff_buff(&sbuff), in_vb->tainted);
- fr_value_box_mark_safe_for(vb, ldap_escape_xlat);
fr_dcursor_append(out, vb);
return XLAT_ACTION_DONE;
/** Escape function for a part of an LDAP URI
*
*/
-static int uri_part_escape(fr_value_box_t *vb, UNUSED void *uctx)
+static int ldap_uri_part_escape(fr_value_box_t *vb, UNUSED void *uctx)
{
fr_sbuff_t sbuff;
fr_sbuff_uctx_talloc_t sbuff_ctx;
size_t len;
- /*
- * If it's already safe, don't do anything.
- */
- if (fr_value_box_is_safe_for(vb, uri_part_escape)) return 0;
-
/*
* Maximum space needed for output would be 3 times the input if every
* char needed escaping
fr_sbuff_trim_talloc(&sbuff, len);
fr_value_box_clear_value(vb);
fr_value_box_strdup_shallow(vb, NULL, fr_sbuff_buff(&sbuff), vb->tainted);
- fr_value_box_mark_safe_for(vb, uri_part_escape);
return 0;
}
fr_trunk_request_signal_cancel(query->treq);
}
-
+/*
+ * If a part doesn't have an escaping function, parsing will fail unless the input
+ * was marked up with a safe_for value by the ldap arg parsing, i.e. was a literal
+ * input argument to the xlat.
+ *
+ * This is equivalent to the old "tainted_allowed" flag.
+ */
static fr_uri_part_t const ldap_uri_parts[] = {
- { .name = "scheme", .terminals = &FR_SBUFF_TERMS(L(":")), .part_adv = { [':'] = 1 },
- .tainted_allowed = false, .extra_skip = 2 },
- { .name = "host", .terminals = &FR_SBUFF_TERMS(L(":"), L("/")), .part_adv = { [':'] = 1, ['/'] = 2 },
- .tainted_allowed = false },
- { .name = "port", .terminals = &FR_SBUFF_TERMS(L("/")), .part_adv = { ['/'] = 1 },
- .tainted_allowed = false },
- { .name = "dn", .terminals = &FR_SBUFF_TERMS(L("?")), .part_adv = { ['?'] = 1 },
- .tainted_allowed = true, .func = uri_part_escape },
- { .name = "attrs", .terminals = &FR_SBUFF_TERMS(L("?")), .part_adv = { ['?'] = 1 },
- .tainted_allowed = false },
- { .name = "scope", .terminals = &FR_SBUFF_TERMS(L("?")), .part_adv = { ['?'] = 1 },
- .tainted_allowed = true, .func = uri_part_escape },
- { .name = "filter", .terminals = &FR_SBUFF_TERMS(L("?")), .part_adv = { ['?'] = 1},
- .tainted_allowed = true, .func = uri_part_escape },
- { .name = "exts", .tainted_allowed = true, .func = uri_part_escape },
+ { .name = "scheme", .safe_for = LDAP_URI_SAFE_FOR, .terminals = &FR_SBUFF_TERMS(L(":")), .part_adv = { [':'] = 1 }, .extra_skip = 2 },
+ { .name = "host", .safe_for = LDAP_URI_SAFE_FOR, .terminals = &FR_SBUFF_TERMS(L(":"), L("/")), .part_adv = { [':'] = 1, ['/'] = 2 } },
+ { .name = "port", .safe_for = LDAP_URI_SAFE_FOR, .terminals = &FR_SBUFF_TERMS(L("/")), .part_adv = { ['/'] = 1 } },
+ { .name = "dn", .safe_for = LDAP_URI_SAFE_FOR, .terminals = &FR_SBUFF_TERMS(L("?")), .part_adv = { ['?'] = 1 }, .func = ldap_uri_part_escape },
+ { .name = "attrs", .safe_for = LDAP_URI_SAFE_FOR, .terminals = &FR_SBUFF_TERMS(L("?")), .part_adv = { ['?'] = 1 }},
+ { .name = "scope", .safe_for = LDAP_URI_SAFE_FOR, .terminals = &FR_SBUFF_TERMS(L("?")), .part_adv = { ['?'] = 1 }, .func = ldap_uri_part_escape },
+ { .name = "filter", .safe_for = LDAP_URI_SAFE_FOR, .terminals = &FR_SBUFF_TERMS(L("?")), .part_adv = { ['?'] = 1}, .func = ldap_uri_part_escape },
+ { .name = "exts", .safe_for = LDAP_URI_SAFE_FOR, .func = ldap_uri_part_escape },
+ XLAT_URI_PART_TERMINATOR
+};
+
+static fr_uri_part_t const ldap_dn_parts[] = {
+ { .name = "dn", .safe_for = LDAP_URI_SAFE_FOR , .func = ldap_uri_part_escape },
XLAT_URI_PART_TERMINATOR
};
static xlat_arg_parser_t const ldap_xlat_arg[] = {
- { .required = true, .type = FR_TYPE_STRING },
+ { .required = true, .type = FR_TYPE_STRING, .safe_for = LDAP_URI_SAFE_FOR },
XLAT_ARG_PARSER_TERMINATOR
};
}
static xlat_arg_parser_t const ldap_memberof_xlat_arg[] = {
- { .required = true, .concat = true, .type = FR_TYPE_STRING },
+ { .required = true, .concat = true, .type = FR_TYPE_STRING, .safe_for = LDAP_URI_SAFE_FOR },
XLAT_ARG_PARSER_TERMINATOR
};
char const *filter;
int scope;
+ bool is_dn;
+
XLAT_ARGS(in, &uri_components);
- if (fr_uri_escape_list(&uri_components->vb_group, ldap_uri_parts, NULL) < 0) {
- RPERROR("Failed to escape LDAP URI");
- return XLAT_ACTION_FAIL;
+ is_dn = (fr_uri_has_scheme(&uri_components->vb_group, ldap_uri_scheme_table, ldap_uri_scheme_table_len, -1) < 0);
+
+ /*
+ * Apply different escaping rules based on whether the first
+ * arg lookgs like a URI or a DN.
+ */
+ if (is_dn) {
+ if (fr_uri_escape_list(&uri_components->vb_group, ldap_dn_parts, NULL) < 0) {
+ RPERROR("Failed to escape LDAP DN");
+ return XLAT_ACTION_FAIL;
+ }
+ } else {
+ if (fr_uri_escape_list(&uri_components->vb_group, ldap_uri_parts, NULL) < 0) {
+ RPERROR("Failed to escape LDAP URI");
+ return XLAT_ACTION_FAIL;
+ }
}
/*
* Smush everything into the first URI box
*/
uri = fr_value_box_list_head(&uri_components->vb_group);
-
if (fr_value_box_list_concat_in_place(uri, uri, &uri_components->vb_group,
FR_TYPE_STRING, FR_VALUE_BOX_LIST_FREE, true, SIZE_MAX) < 0) {
REDEBUG("Failed concattenating input");
MEM(xlat_ctx = talloc_zero(unlang_interpret_frame_talloc_ctx(request), ldap_xlat_profile_ctx_t));
talloc_set_destructor(xlat_ctx, ldap_xlat_profile_ctx_free);
- if (!ldap_is_ldap_url(uri->vb_strvalue)) {
+ if (is_dn) {
host_url = handle_config->server;
dn = talloc_typed_strdup_buffer(xlat_ctx, uri->vb_strvalue);
filter = env_data->profile_filter.vb_strvalue;
ldap_map_ctx_t *map_ctx;
char *host_url, *host = NULL;
- if (fr_uri_escape_list(url, ldap_uri_parts, NULL) < 0) {
- RPERROR("Failed to escape LDAP URI");
- RETURN_MODULE_FAIL;
- }
-
+ /* FIXME - We need a way of markup up literal */
+/*
+ * if (fr_uri_escape_list(url, ldap_uri_parts, NULL) < 0) {
+ * RPERROR("Failed to escape LDAP URI");
+ * RETURN_MODULE_FAIL;
+ * }
+ */
url_head = fr_value_box_list_head(url);
if (!url_head) {
REDEBUG("LDAP URL cannot be (null)");
xlat_func_args_set(xlat, ldap_xlat_arg);
xlat_func_call_env_set(xlat, &xlat_profile_method_env);
- map_proc_register(inst, mctx->inst->name, mod_map_proc, ldap_map_verify, 0);
+ map_proc_register(inst, mctx->inst->name, mod_map_proc, ldap_map_verify, 0, LDAP_URI_SAFE_FOR);
return 0;
}
if (unlikely(!(xlat = xlat_func_register(NULL, "ldap.escape", ldap_escape_xlat, FR_TYPE_STRING)))) return -1;
xlat_func_mono_set(xlat, ldap_escape_xlat_arg);
xlat_func_flags_set(xlat, XLAT_FUNC_FLAG_PURE);
+ xlat_func_safe_for_set(xlat, LDAP_URI_SAFE_FOR); /* Used for all LDAP escaping */
+
if (unlikely(!(xlat = xlat_func_register(NULL, "ldap.unescape", ldap_unescape_xlat, FR_TYPE_STRING)))) return -1;
xlat_func_mono_set(xlat, ldap_escape_xlat_arg);
xlat_func_flags_set(xlat, XLAT_FUNC_FLAG_PURE);
#include "rest.h"
-static int uri_part_escape(fr_value_box_t *vb, void *uctx);
-static void *uri_part_escape_uctx_alloc(UNUSED request_t *request, void const *uctx);
+static int rest_uri_part_escape(fr_value_box_t *vb, void *uctx);
+static void *rest_uri_part_escape_uctx_alloc(UNUSED request_t *request, void const *uctx);
+
+#define REST_URI_SAFE_FOR (fr_value_box_safe_for_t)fr_curl_xlat_uri_escape
static fr_uri_part_t const rest_uri_parts[] = {
- { .name = "scheme", .terminals = &FR_SBUFF_TERMS(L(":")), .part_adv = { [':'] = 1 },
- .tainted_allowed = false, .extra_skip = 2 },
- { .name = "host", .terminals = &FR_SBUFF_TERMS(L(":"), L("/")), .part_adv = { [':'] = 1, ['/'] = 2 },
- .tainted_allowed = true, .func = uri_part_escape },
- { .name = "port", .terminals = &FR_SBUFF_TERMS(L("/")), .part_adv = { ['/'] = 1 },
- .tainted_allowed = false },
- { .name = "method", .terminals = &FR_SBUFF_TERMS(L("?")), .part_adv = { ['?'] = 1 },
- .tainted_allowed = true, .func = uri_part_escape },
- { .name = "param", .tainted_allowed = true, .func = uri_part_escape },
+ { .name = "scheme", .safe_for = REST_URI_SAFE_FOR, .terminals = &FR_SBUFF_TERMS(L(":")), .part_adv = { [':'] = 1 }, .extra_skip = 2 },
+ { .name = "host", .safe_for = REST_URI_SAFE_FOR, .terminals = &FR_SBUFF_TERMS(L(":"), L("/")), .part_adv = { [':'] = 1, ['/'] = 2 }, .func = rest_uri_part_escape },
+ { .name = "port", .safe_for = REST_URI_SAFE_FOR, .terminals = &FR_SBUFF_TERMS(L("/")), .part_adv = { ['/'] = 1 } },
+ { .name = "method", .safe_for = REST_URI_SAFE_FOR, .terminals = &FR_SBUFF_TERMS(L("?")), .part_adv = { ['?'] = 1 }, .func = rest_uri_part_escape },
+ { .name = "param", .safe_for = REST_URI_SAFE_FOR, .func = rest_uri_part_escape },
XLAT_URI_PART_TERMINATOR
};
.mode = TMPL_ESCAPE_PRE_CONCAT, \
.uctx = { \
.func = { \
- .alloc = uri_part_escape_uctx_alloc, \
+ .alloc = rest_uri_part_escape_uctx_alloc, \
.uctx = rest_uri_parts \
} , \
.type = TMPL_ESCAPE_UCTX_ALLOC_FUNC\
}, \
- .safe_for = (fr_value_box_safe_for_t)fr_curl_xlat_uri_escape \
- }}, /* Do not concat */ \
+ .safe_for = REST_URI_SAFE_FOR \
+ }, \
+ .pair.literals_safe_for = REST_URI_SAFE_FOR}, /* Do not concat */ \
REST_CALL_ENV_REQUEST_COMMON(_dflt_username, _dflt_password) \
CALL_ENV_TERMINATOR \
})) }, \
return 0;
}
-static int _uri_part_escape_uctx_free(void *uctx)
+static int _rest_uri_part_escape_uctx_free(void *uctx)
{
return talloc_free(uctx);
}
* @param[in] uctx pointer to the start of the uri_parts array.
* @return A new fr_uri_escape_ctx_t.
*/
-static void *uri_part_escape_uctx_alloc(UNUSED request_t *request, void const *uctx)
+static void *rest_uri_part_escape_uctx_alloc(UNUSED request_t *request, void const *uctx)
{
static _Thread_local fr_uri_escape_ctx_t *t_ctx;
fr_uri_escape_ctx_t *ctx;
MEM(ctx = talloc_zero(NULL, fr_uri_escape_ctx_t));
- fr_atexit_thread_local(t_ctx, _uri_part_escape_uctx_free, ctx);
+ fr_atexit_thread_local(t_ctx, _rest_uri_part_escape_uctx_free, ctx);
} else {
memset(t_ctx, 0, sizeof(*t_ctx));
}
* - 0 on success
* - -1 on failure
*/
-static int uri_part_escape(fr_value_box_t *vb, UNUSED void *uctx)
+static int rest_uri_part_escape(fr_value_box_t *vb, UNUSED void *uctx)
{
char *escaped;
}
static xlat_arg_parser_t const rest_xlat_args[] = {
- { .required = true, .type = FR_TYPE_STRING },
+ { .required = true, .safe_for = REST_URI_SAFE_FOR, .type = FR_TYPE_STRING },
{ .variadic = XLAT_ARG_VARIADIC_EMPTY_KEEP, .type = FR_TYPE_STRING },
XLAT_ARG_PARSER_TERMINATOR
};
fr_json_version_print();
#endif
+ {
+ xlat_t *xlat;
+
+ xlat = xlat_func_register(NULL, "rest.escape", fr_curl_xlat_uri_escape, FR_TYPE_STRING);
+ if (unlikely(!xlat)) {
+ ERROR("Failed registering \"rest.escape\" xlat");
+ return -1;
+ }
+ xlat_func_args_set(xlat, fr_curl_xlat_uri_args);
+ xlat_func_safe_for_set(xlat, REST_URI_SAFE_FOR); /* Each instance of the uri_escape xlat has its own safe_for value */
+ xlat = xlat_func_register(NULL, "rest.unescape", fr_curl_xlat_uri_unescape, FR_TYPE_STRING);
+ if (unlikely(!xlat)) {
+ ERROR("Failed registering \"rest.unescape\" xlat");
+ return -1;
+ }
+ xlat_func_args_set(xlat, fr_curl_xlat_uri_args);
+ }
+
return 0;
}
+static void mod_unload(void)
+{
+ xlat_func_unregister("rest.escape");
+ xlat_func_unregister("rest.unescape");
+}
+
/*
* The module name should be the only globally exported symbol.
* That is, everything else should be 'static'.
.thread_inst_size = sizeof(rlm_rest_thread_t),
.config = module_config,
.onload = mod_load,
+ .unload = mod_unload,
.bootstrap = mod_bootstrap,
.instantiate = instantiate,
.thread_instantiate = mod_thread_instantiate,
/*
* Register the SQL map processor function
*/
- if (inst->driver->sql_fields) map_proc_register(inst, mctx->inst->name, mod_map_proc, sql_map_verify, 0);
+ if (inst->driver->sql_fields) map_proc_register(inst, mctx->inst->name, mod_map_proc, sql_map_verify, 0, (fr_value_box_safe_for_t)inst->driver);
return 0;
}
['%'] = '%',
['\\'] = '\\',
},
- }}, t_rules) < 0) {
+ }}, t_rules, 0) < 0) {
talloc_free(query);
return -1;
}
*/
our_rules.escape.uctx.func.uctx = sql;
our_rules.escape.safe_for = (fr_value_box_safe_for_t)sql->driver;
- our_rules.literal.safe_for = (fr_value_box_safe_for_t)sql->driver;
+ our_rules.literals_safe_for = (fr_value_box_safe_for_t)sql->driver;
if (tmpl_afrom_substr(ctx, &parsed_tmpl,
&FR_SBUFF_IN(cf_pair_value(to_parse), talloc_array_length(cf_pair_value(to_parse)) - 1),
if (strlen(cf_pair_value(to_parse)) > 0) {
if (tmpl_afrom_substr(ctx, &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, t_rules) < 0) return -1;
+ cf_pair_value_quote(to_parse), 0,
+ NULL, t_rules) < 0) return -1;
} else {
/*
* If the domain has not been specified, try and find
test_fail
}
+# Verify that DN based profiles allow dynamic expansions
+group {
+ string user
+
+ &user := 'suspended'
+
+ if (!%ldap.profile("ldap:///cn=%{user},ou=profiles,dc=example,dc=com")) {
+ test_fail
+ }
+
+ if (&reply.Reply-Message != 'User-Suspended') {
+ test_fail
+ }
+
+ &control := {}
+ &reply := {}
+
+ if (!%ldap.profile("cn=%{user},ou=profiles,dc=example,dc=com")) {
+ test_fail
+ }
+
+ if (&reply.Reply-Message != 'User-Suspended') {
+ test_fail
+ }
+
+ &control := {}
+ &reply := {}
+}
+
test_pass
&test_string := 'notfound'
# Retrieve a plain text file
-&result_string := %rest('GET', "http://%{server_host}:%{server_port}/test.txt")
+&result_string := %rest('GET', "http://%{server_host}:%rest.escape(%{server_port})/test.txt")
if (!(&REST-HTTP-Status-Code == 200)) {
test_fail
}
# Take host from incoming packet
-&result_string := %rest("http://%{Login-IP-Host}:%{server_port}/test.txt")
+&result_string := %rest("http://%{Login-IP-Host}:%rest.escape(%{server_port})/test.txt")
if (!(&REST-HTTP-Status-Code == 200) || !(&result_string == "Sample text response\n")) {
test_fail
# Port is not allowed from incoming packet
&result_string := %rest("http://%{server_host}:%{NAS-Port}/test.txt")
-if (!(&Module-Failure-Message == "Failed escaping URI: Tainted value not allowed for port") || &result_string) {
+if (!(&Module-Failure-Message == "Failed escaping URI: Unsafe input \"8080\" not allowed in URI part port") || &result_string) {
test_fail
}
# Check a "not found" gives a 404 status code
-&result_string := %rest('GET', "http://%{server_host}:%{server_port}/%{test_string}")
+&result_string := %rest('GET', "http://%{server_host}:%rest.escape(%{server_port})/%{test_string}")
if (!(&REST-HTTP-Status-Code == 404)) {
test_fail
}
# GET with URL parameters
-&test_string := %rest('GET', "http://%{server_host}:%{server_port}/user/%{User-Name}/mac/%{Called-Station-Id}")
+&test_string := %rest('GET', "http://%{server_host}:%rest.escape(%{server_port})/user/%{User-Name}/mac/%{Called-Station-Id}")
if (!(&REST-HTTP-Status-Code == 200)) {
test_fail
&control.User-Name := 'dummy'
# Directly use json map and prepend the returned value
-map json %rest('GET', "http://%{server_host}:%{server_port}/user/%{User-Name}/mac/%{Called-Station-Id}") {
+map json %rest('GET', "http://%{server_host}:%rest.escape(%{server_port})/user/%{User-Name}/mac/%{Called-Station-Id}") {
&control.User-Name ^= '$.control\.User-Name.value'
}
&test_string := %json.encode('&request.NAS-IP-Address')
# POST to https with JSON body data
-&result_string := %rest('POST', "https://%{server_host}:%{server_ssl_port}/user/%{User-Name}/mac/%{Called-Station-Id}?section=accounting", %{test_string})
+&result_string := %rest('POST', "https://%{server_host}:%rest.escape(%{server_ssl_port})/user/%{User-Name}/mac/%{Called-Station-Id}?section=accounting", %{test_string})
if (!(&REST-HTTP-Status-Code == 200)) {
test_fail
&result_string := "NAS=%{NAS-IP-Address}&user=%{User-Name}"
# POST to https with POST body data
-&result_string := %rest('POST', "https://%{server_host}:%{server_ssl_port}/post/test?section=dummy", %{result_string})
+&result_string := %rest('POST', "https://%{server_host}:%rest.escape(%{server_ssl_port})/post/test?section=dummy", %{result_string})
if (!(&REST-HTTP-Status-Code == 200)) {
test_fail
string arguments
string body
- map json %rest('POST', "http://%{server_host}:%{server_port}/user/%{User-Name}/reflect/?station=%{Calling-Station-Id}", "{\"test\":\"foo\"}") {
+ map json %rest('POST', "http://%{server_host}:%rest.escape(%{server_port})/user/%{User-Name}/reflect/?station=%{Calling-Station-Id}", "{\"test\":\"foo\"}") {
&headers := '$.reply\.Reply-Message.value[0]'
&arguments := '$.reply\.Reply-Message.value[1]'
&body := '$.reply\.Reply-Message.value[2]'
&test_string := ""
- map json %rest(http://%{server_host}:%{server_port}/user/%{User-Name}/reflect/%{test_string}?station=%{User-Name}) {
+ map json %rest(http://%{server_host}:%rest.escape(%{server_port})/user/%{User-Name}/reflect/%{test_string}?station=%{User-Name}) {
&headers := '$.reply\.Reply-Message.value[0]'
&arguments := '$.reply\.Reply-Message.value[1]'
}
group {
string arguments
- map json %rest("http://%{server_host}:%{server_port}/user/%{User-Name}/reflect/%{NAS-Identifier}?station=%{Called-Station-Id}") {
+ map json %rest("http://%{server_host}:%rest.escape(%{server_port})/user/%{User-Name}/reflect/%{NAS-Identifier}?station=%{Called-Station-Id}") {
&arguments := '$.reply\.Reply-Message.value[1]'
}
}
# Test against endpoint which will time out
-&result_string := %restshorttimeout("http://%{server_host}:%{server_port}/delay")
+&result_string := %restshorttimeout("http://%{server_host}:%rest.escape(%{server_port})/delay")
if (&REST-HTTP-Status-Code) {
test_fail