]> git.ipfire.org Git - thirdparty/freeradius-server.git/commitdiff
Do a better job of marking up literals passed to tmpl_tokenize with safe_for values
authorArran Cudbard-Bell <a.cudbardb@freeradius.org>
Sat, 17 Feb 2024 22:04:29 +0000 (16:04 -0600)
committerArran Cudbard-Bell <a.cudbardb@freeradius.org>
Sat, 17 Feb 2024 22:09:54 +0000 (16:09 -0600)
Fix the uri functions to use safe_for instead of tainted

Allow a safe_for value to be passed in for maps too

31 files changed:
src/bin/unit_test_attribute.c
src/bin/unit_test_module.c
src/lib/server/cf_parse.c
src/lib/server/map_proc.c
src/lib/server/map_proc.h
src/lib/server/map_proc_priv.h
src/lib/server/tmpl.h
src/lib/server/tmpl_tokenize.c
src/lib/server/trigger.c
src/lib/unlang/call_env.c
src/lib/unlang/call_env.h
src/lib/unlang/compile.c
src/lib/unlang/xlat.h
src/lib/unlang/xlat_builtin.c
src/lib/unlang/xlat_eval.c
src/lib/unlang/xlat_expr.c
src/lib/unlang/xlat_inst.c
src/lib/unlang/xlat_tokenize.c
src/lib/util/uri.c
src/lib/util/uri.h
src/modules/rlm_client/rlm_client.c
src/modules/rlm_csv/rlm_csv.c
src/modules/rlm_json/rlm_json.c
src/modules/rlm_ldap/rlm_ldap.c
src/modules/rlm_rest/rlm_rest.c
src/modules/rlm_sql/rlm_sql.c
src/modules/rlm_sqlcounter/rlm_sqlcounter.c
src/modules/rlm_sqlippool/rlm_sqlippool.c
src/modules/rlm_winbind/rlm_winbind.c
src/tests/modules/ldap/xlat_profile.unlang
src/tests/modules/rest/rest_xlat.unlang

index 0af736626faf245e64869cb7262e715b7f9ea9c8..15b404eec48c4d3aec4f3e95435b9525a8d1fd0e 100644 (file)
@@ -2788,7 +2788,8 @@ static size_t command_xlat_normalise(command_result_t *result, command_file_ctx_
                                                .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);
 
index 43996b7e4f2302661465a3c1c147ad09d3af159a..233f3b911e0a0babb52f1e3f39f8fb8a8a01cc2d 100644 (file)
@@ -489,7 +489,7 @@ static bool do_xlats(fr_event_list_t *el, request_t *request, char const *filena
                                                                };
 
 
-                       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());
@@ -882,7 +882,7 @@ int main(int argc, char *argv[])
                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;
        }
 
index a5e7063de9cd3b24c4e0c3b332762b591e9795c5..c603b47cdaef84dbfccae514da694cafdc2b82a4 100644 (file)
@@ -1284,7 +1284,7 @@ int cf_section_parse_pass2(void *base, CONF_SECTION *cs)
                                                             .allow_unresolved = false,
                                                             .allow_foreign = (dict == NULL)
                                                     },
-                                            });
+                                            }, 0);
                        if (slen < 0) {
                                char *spaces, *text;
 
index 8e19454006e82dc8d7e28eb739f6af05e6027ff0..3452d679c08ed0c180c7969b1ff2f00a7b960e86 100644 (file)
  * @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;
 
@@ -101,13 +103,14 @@ map_proc_t *map_proc_find(char const *name)
  * @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;
 
@@ -145,6 +148,7 @@ int map_proc_register(void *mod_inst, char const *name,
        proc->evaluate = evaluate;
        proc->instantiate = instantiate;
        proc->inst_size = inst_size;
+       proc->literals_safe_for = literals_safe_for;
 
        return 0;
 }
index 62f1bf2e0dcd73f94ca3b270c39f1ce8a3fa18ac..c75c3d72039fe10d04f1b1e3a4ab0fef99d5b4a5 100644 (file)
@@ -40,6 +40,7 @@ typedef struct map_proc_inst map_proc_inst_t;
 #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" {
@@ -81,7 +82,7 @@ map_proc_t    *map_proc_find(char const *name);
 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);
index a6037237b0f4218848b933924b09161c4125e6f8..4b333bfdc02553d8ca059e3274c9fc091213d154 100644 (file)
@@ -28,6 +28,7 @@
 #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" {
@@ -44,6 +45,7 @@ struct map_proc {
        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
index e4842da404031e423cfc06cb43d8ecac62b769d6..83f15f5dc9cc5bb095236d030bcbde6ca85bceaf 100644 (file)
@@ -335,32 +335,27 @@ struct tmpl_xlat_rules_s {
        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
index 689e4628eb59704998d90dc553225ff5fb54759d..6d6d65a26488c2c79b0252be5445096d23bc9032 100644 (file)
@@ -37,6 +37,7 @@ RCSID("$Id$")
 #include <freeradius-devel/util/misc.h>
 
 #include <freeradius-devel/util/sbuff.h>
+#include <freeradius-devel/util/value.h>
 
 #include <ctype.h>
 
@@ -2414,6 +2415,7 @@ static fr_slen_t tmpl_afrom_value_substr(TALLOC_CTX *ctx, tmpl_t **out, fr_sbuff
                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);
 
@@ -3080,7 +3082,7 @@ fr_slen_t tmpl_afrom_substr(TALLOC_CTX *ctx, tmpl_t **out,
                        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);
@@ -3112,8 +3114,7 @@ fr_slen_t tmpl_afrom_substr(TALLOC_CTX *ctx, tmpl_t **out,
 
                        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);
                }
 
                /*
@@ -3263,7 +3264,7 @@ fr_slen_t tmpl_afrom_substr(TALLOC_CTX *ctx, tmpl_t **out,
 
                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);
 
                /*
@@ -3331,7 +3332,7 @@ fr_slen_t tmpl_afrom_substr(TALLOC_CTX *ctx, tmpl_t **out,
                 *      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);
                }
@@ -3356,7 +3357,7 @@ fr_slen_t tmpl_afrom_substr(TALLOC_CTX *ctx, tmpl_t **out,
 
                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);
 
                /*
index b3cf109b7c3851aa89ffdefcd25a6140260507b6..82d354c97f627bd85a9d8975abdb6df274c1243f 100644 (file)
@@ -461,12 +461,7 @@ int trigger_exec(unlang_interpret_t *intp,
                }
        }
 
-       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;
index 776bf1231e486f9b0ce0e5a529755916541f4589..c93658e89e21d036a1868210a11950b63c908d3e 100644 (file)
@@ -430,6 +430,7 @@ static int call_env_parse(TALLOC_CTX *ctx, call_env_parsed_head_t *parsed, char
 
                        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;
index f7438ae4763f6032543e3fccd03b85e12eece0a2..9d1af0b249dcf24a7968369cdbe325196109841b 100644 (file)
@@ -23,6 +23,7 @@
  *
  * @copyright 2023 Network RADIUS SAS (legal@networkradius.com)
  */
+#include "lib/util/value.h"
 RCSIDH(call_env_h, "$Id$")
 
 #ifdef __cplusplus
@@ -193,6 +194,7 @@ struct call_env_parser_s {
                                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;
 
index 688ff62b45767522da47b8dc5775331c4e75d4bd..61177f8a0aabf966a0d951cc93899e7a74f94040 100644 (file)
@@ -855,6 +855,7 @@ static unlang_t *compile_map(unlang_t *parent, unlang_compile_t *unlang_ctx, CON
                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;
index dbc62b69349b0f75049d87cf2bcc6739c3ffbb2e..a59317cacb922c376aaa37bfa8c6e6f0690ef1fd 100644 (file)
@@ -398,7 +398,8 @@ fr_slen_t   xlat_tokenize_argv(TALLOC_CTX *ctx, xlat_exp_head_t **head, fr_sbuff_
                                   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);
 
@@ -464,7 +465,7 @@ int         xlat_instance_unregister_func(xlat_exp_t *node);
 
 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);
 
index 2ca7046eefca8696ab34669318452b82f754899a..677570e0bf5217abdffd680058c5c77ec16fa5ef 100644 (file)
@@ -1494,7 +1494,7 @@ static xlat_action_t xlat_func_eval(TALLOC_CTX *ctx, fr_dcursor_t *out,
                                          .runtime_el = unlang_interpret_event_list(request),
                                  },
                                  .at_runtime = true
-                         }) < 0) {
+                         }, 0) < 0) {
                RPEDEBUG("Failed parsing expansion");
        error:
                talloc_free(rctx);
index 8f76e66819d9d2b62f403b94ef82e69355a621c3..91ac371a2266df042c545e2df2bd04011b3e0afe 100644 (file)
@@ -1425,7 +1425,7 @@ ssize_t _xlat_eval(TALLOC_CTX *ctx, char **out, size_t outlen, request_t *reques
                                            .runtime_el = unlang_interpret_event_list(request),
                                    },
                                    .at_runtime = true,
-                           });
+                           }, 0);
        if (len == 0) {
                if (*out) {
                        **out = '\0';
index b3fcc15cde7bbfbbc9ddf2de0492efcdfc04b898..559ad09f06e83a482f10495c1973db21ddf419d9 100644 (file)
@@ -3005,7 +3005,7 @@ static fr_slen_t xlat_tokenize_expression_internal(TALLOC_CTX *ctx, xlat_exp_hea
         *      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;
        }
@@ -3015,13 +3015,13 @@ static fr_slen_t xlat_tokenize_expression_internal(TALLOC_CTX *ctx, xlat_exp_hea
 }
 
 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);
 }
index 156b5e2c2b7df0d4e38febe4b850117dd211936c..ee016f658c52eb1a5c105ed52ad17ec2041587df 100644 (file)
@@ -679,16 +679,18 @@ static inline CC_HINT(always_inline) int xlat_instance_register(xlat_exp_head_t
 
 /** 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
index 423d9012aecda579cfecf2a46301d246fb058663..c6747504bc92b014ed84345f275b2422de38b4f8 100644 (file)
@@ -25,6 +25,7 @@
  * @copyright 2000,2006 The FreeRADIUS server project
  */
 
+#include "lib/util/event.h"
 #include "lib/util/value.h"
 RCSID("$Id$")
 
@@ -897,7 +898,8 @@ check_for_attr:
  *     - -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;
@@ -1611,12 +1613,17 @@ fr_slen_t xlat_tokenize_argv(TALLOC_CTX *ctx, xlat_exp_head_t **out, fr_sbuff_t
 
 /** 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.
@@ -1624,19 +1631,17 @@ fr_slen_t xlat_tokenize_argv(TALLOC_CTX *ctx, xlat_exp_head_t **out, fr_sbuff_t
  *     - < 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);
        }
@@ -1645,7 +1650,7 @@ fr_slen_t xlat_tokenize(TALLOC_CTX *ctx, xlat_exp_head_t **out, fr_sbuff_t *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;
        }
index 50c57c4067ef8b5c99d8f28d6549789e83b12107..c54d54b96efdcd731cdc56710f4a387270c55f93 100644 (file)
@@ -20,6 +20,9 @@
  *
  * @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"
@@ -28,6 +31,9 @@ RCSID("$Id$")
  *
  * @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.
@@ -41,11 +47,6 @@ int fr_uri_escape(fr_value_box_t *uri_vb, void *uctx)
        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.
         */
@@ -59,7 +60,7 @@ int fr_uri_escape(fr_value_box_t *uri_vb, void *uctx)
        /*
         *      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
@@ -71,7 +72,11 @@ int fr_uri_escape(fr_value_box_t *uri_vb, void *uctx)
                                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;
        }
@@ -120,6 +125,9 @@ int fr_uri_escape(fr_value_box_t *uri_vb, void *uctx)
  * 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
@@ -144,3 +152,48 @@ int fr_uri_escape_list(fr_value_box_list_t *uri, fr_uri_part_t const *uri_parts,
 
        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);
+}
index 14ce1b8fcf6810ff812f9ebc8bf0d084dd845071..ec9fc03acb9a451983a99f3c9873e5dc70ad3671 100644 (file)
@@ -44,13 +44,13 @@ typedef int (*fr_uri_escape_func_t)(fr_value_box_t *vb, void *uctx);
  *
  */
 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
@@ -63,12 +63,14 @@ typedef struct {
        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
index dad5cad6ab2781d7fd8302507f3c4dd39479b71a..a6d3d24ae3a56d4e58240f819fd737b74c43c0fc 100644 (file)
@@ -357,7 +357,7 @@ static int mod_load(void)
        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;
 }
index 2dc964bc8a65e798f097d54b1cab84461cb56ca0..063ba4291c2c838cc11c0fc335f71050196c3b5c 100644 (file)
@@ -737,12 +737,11 @@ static int mod_bootstrap(module_inst_ctx_t const *mctx)
        /*
         *      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.
index 2276522a81d86780f2a6aed87294e8d87bb166d2..584b9966968f133c50d9e5fa912ba382ea299802 100644 (file)
@@ -584,7 +584,7 @@ static int mod_bootstrap(module_inst_ctx_t const *mctx)
        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;
 }
 
index e62ea9173a4324b6e46caa3455fac6630e5496fc..bdf3881d697871cecfd9089af4f7178b71d09e43 100644 (file)
@@ -33,7 +33,9 @@ RCSID("$Id$")
 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>
@@ -346,8 +348,25 @@ typedef struct {
        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
 };
 
@@ -396,7 +415,6 @@ static xlat_action_t ldap_escape_xlat(TALLOC_CTX *ctx, fr_dcursor_t *out,
         */
        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;
@@ -443,17 +461,12 @@ static xlat_action_t ldap_unescape_xlat(TALLOC_CTX *ctx, fr_dcursor_t *out,
 /** 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
@@ -471,7 +484,6 @@ static int uri_part_escape(fr_value_box_t *vb, UNUSED void *uctx)
        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;
 }
@@ -563,28 +575,32 @@ static void ldap_xlat_signal(xlat_ctx_t const *xctx, request_t *request, UNUSED
        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
 };
 
@@ -829,7 +845,7 @@ static xlat_action_t ldap_memberof_xlat_resume(TALLOC_CTX *ctx, fr_dcursor_t *ou
 }
 
 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
 };
 
@@ -982,18 +998,32 @@ static xlat_action_t ldap_profile_xlat(UNUSED TALLOC_CTX *ctx, UNUSED fr_dcursor
        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");
@@ -1006,7 +1036,7 @@ static xlat_action_t ldap_profile_xlat(UNUSED TALLOC_CTX *ctx, UNUSED fr_dcursor
        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;
@@ -1229,11 +1259,13 @@ static unlang_action_t mod_map_proc(rlm_rcode_t *p_result, void *mod_inst, UNUSE
        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)");
@@ -2273,7 +2305,7 @@ static int mod_bootstrap(module_inst_ctx_t const *mctx)
        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;
 }
@@ -2564,6 +2596,8 @@ static int mod_load(void)
        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);
index aa7992e0cc68fa1cd57e1219304ec7c405e35d77..1dc539bb3b7ac9f0de1a5e699d9ea02716bd4431 100644 (file)
@@ -49,19 +49,17 @@ RCSID("$Id$")
 
 #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
 };
 
@@ -212,13 +210,14 @@ static const call_env_method_t _var = { \
                                                                                        .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 \
                                                        })) }, \
@@ -334,7 +333,7 @@ static int rlm_rest_status_update(request_t *request, void *handle)
        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);
 }
@@ -345,7 +344,7 @@ static int _uri_part_escape_uctx_free(void *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;
 
@@ -353,7 +352,7 @@ static void *uri_part_escape_uctx_alloc(UNUSED request_t *request, void const *u
                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));
        }
@@ -369,7 +368,7 @@ static void *uri_part_escape_uctx_alloc(UNUSED request_t *request, void const *u
  *     - 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;
 
@@ -498,7 +497,7 @@ finish:
 }
 
 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
 };
@@ -1379,9 +1378,33 @@ static int mod_load(void)
        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'.
@@ -1401,6 +1424,7 @@ module_rlm_t rlm_rest = {
                .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,
index 3c4c4ac16e149b9ddaf24a90d694d60ae3b3fa79..f30b5a965857b77206342b56f9448f44f4e12eb7 100644 (file)
@@ -1619,7 +1619,7 @@ static int mod_bootstrap(module_inst_ctx_t const *mctx)
        /*
         *      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;
 }
index 25cdc24a02a7d7eb69495220b59319d3ba40fd8b..fee8783fff89e06c1a2690403d63a0267a079902 100644 (file)
@@ -527,7 +527,7 @@ static int call_env_query_parse(TALLOC_CTX *ctx, void *out, tmpl_rules_t const *
                                        ['%'] = '%',
                                        ['\\'] = '\\',
                                },
-                 }}, t_rules) < 0) {
+                 }}, t_rules, 0) < 0) {
                talloc_free(query);
                return -1;
        }
index 2d92fd285ce40cdac0cf23e38f76eb29e92a660d..1e1357d2dc9c6a40699d7819fa15e8ace78e5f30 100644 (file)
@@ -612,7 +612,7 @@ static int call_env_parse(TALLOC_CTX *ctx, void *out, tmpl_rules_t const *t_rule
         */
        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),
index cbbd4f9dac5d56c6e225d1dbca0d81c79e4f0996..b436a9d27c9e1d072037d28c967f6040e3e9693a 100644 (file)
@@ -447,7 +447,8 @@ static int domain_call_env_parse(TALLOC_CTX *ctx, void *out, tmpl_rules_t const
        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
index 96317e8943170e079e4b7b537930929bc9e10c38..09cd526cc5be52e7eb3f7e5d04dabebfa6ebdbb1 100644 (file)
@@ -14,4 +14,33 @@ if (!%ldap.profile('cn=suspended,ou=profiles,dc=example,dc=com')) {
       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
index 360583c1219f61da13217c47ca79ae3b9d2c27e7..465e22eee7a64de4699b24bcedb712acf4fd2bb6 100644 (file)
@@ -13,7 +13,7 @@ string result_string
 &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
@@ -24,7 +24,7 @@ if (!(&result_string == "Sample text response\n")) {
 }
 
 # 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
@@ -33,19 +33,19 @@ if (!(&REST-HTTP-Status-Code == 200) || !(&result_string == "Sample text respons
 # 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
@@ -67,7 +67,7 @@ if (!(&control.User-Name == "Bob")) {
 &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'
 }
 
@@ -78,7 +78,7 @@ if (!(&control.User-Name[0] == 'Bob') || !(&control.User-Name[1] == 'dummy')) {
 &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
@@ -105,7 +105,7 @@ if (!(&control.NAS-IP-Address == "192.168.1.1")) {
 &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
@@ -122,7 +122,7 @@ group {
        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]'
@@ -160,7 +160,7 @@ group {
 
        &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]'
        }
@@ -190,7 +190,7 @@ group {
 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]'
        }
 
@@ -202,7 +202,7 @@ group {
 }
 
 # 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