From: Arran Cudbard-Bell Date: Fri, 26 Jan 2024 00:19:33 +0000 (-0600) Subject: Replace vb->safe with vb->safe_for X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=3d34ec1333848be69666cf925ee6c388f6ec7178;p=thirdparty%2Ffreeradius-server.git Replace vb->safe with vb->safe_for This is usually a pointer to a function used for escaping. This is still only partially integrated and requires more work. --- diff --git a/src/bin/unit_test_attribute.c b/src/bin/unit_test_attribute.c index ec356f1dab2..b0d9f49e65c 100644 --- a/src/bin/unit_test_attribute.c +++ b/src/bin/unit_test_attribute.c @@ -2913,7 +2913,7 @@ static size_t command_xlat_argv(command_result_t *result, command_file_ctx_t *cc char buff[1024]; slen = xlat_tokenize_argv(cc->tmp_ctx, &head, &FR_SBUFF_IN(in, input_len), - NULL, + NULL, NULL, &(tmpl_rules_t) { .attr = { .dict_def = cc->tmpl_rules.attr.dict_def ? diff --git a/src/lib/curl/all.mk.in b/src/lib/curl/all.mk.in index 70054f57747..edb51b1f3e7 100644 --- a/src/lib/curl/all.mk.in +++ b/src/lib/curl/all.mk.in @@ -5,7 +5,7 @@ ifneq "$(TARGETNAME)" "" TARGET := $(TARGETNAME)$(L) endif -SOURCES := base.c io.c +SOURCES := base.c io.c xlat.c SRC_CFLAGS := @mod_cflags@ diff --git a/src/lib/curl/base.c b/src/lib/curl/base.c index a8767303181..ad184d054b3 100644 --- a/src/lib/curl/base.c +++ b/src/lib/curl/base.c @@ -27,8 +27,10 @@ #endif #include +#include #include "attrs.h" +#include "xlat.h" fr_dict_attr_t const *attr_tls_certificate; static fr_dict_t const *dict_freeradius; /*internal dictionary for server*/ @@ -197,6 +199,39 @@ int fr_curl_response_certinfo(request_t *request, fr_curl_io_request_t *randle) return 0; } +/** Free the curl easy handle + * + * @param[in] arg curl easy handle to free. + */ +static int _curl_tmpl_handle(void *arg) +{ + curl_easy_cleanup(arg); + return 0; +} + +/** Return a thread local curl easy handle + * + * This should only be used for calls into libcurl functions + * which don't operate on an active request, like the + * escape/unescape functions. + * + * @return + * - A thread local curl easy handle. + * - NULL on failure. + */ +CURL *fr_curl_tmp_handle(void) +{ + static _Thread_local CURL *t_candle; + + if (unlikely(t_candle == NULL)) { + CURL *candle; + + MEM(candle = curl_easy_init()); + fr_atexit_thread_local(t_candle, _curl_tmpl_handle, candle); + } + + return t_candle; +} /** Initialise global curl options * @@ -242,6 +277,16 @@ static int fr_curl_init(void) INFO("libcurl version: %s", curl_version()); + { + xlat_t *xlat; + + xlat = xlat_func_register(NULL, "uri.escape", fr_curl_xlat_uri_escape, FR_TYPE_STRING); + xlat_func_args_set(xlat, &fr_curl_xlat_uri_args); + xlat_func_safe_for_set(xlat, fr_curl_xlat_uri_escape); + xlat = xlat_func_register(NULL, "uri.unescape", fr_curl_xlat_uri_unescape, FR_TYPE_STRING); + xlat_func_args_set(xlat, &fr_curl_xlat_uri_args); + } + return 0; } @@ -253,6 +298,9 @@ static void fr_curl_free(void) fr_openssl_free(); #endif curl_global_cleanup(); + + xlat_func_unregister("uri.escape"); + xlat_func_unregister("uri.unescape"); } /* diff --git a/src/lib/curl/base.h b/src/lib/curl/base.h index aa2ec8513e1..ebda6f66dfa 100644 --- a/src/lib/curl/base.h +++ b/src/lib/curl/base.h @@ -37,6 +37,7 @@ extern "C" { #include #include #include +#include DIAG_OFF(DIAG_UNKNOWN_PRAGMAS) DIAG_OFF(disabled-macro-expansion) @@ -138,6 +139,16 @@ int fr_curl_response_certinfo(request_t *request, fr_curl_io_request_t *randle int fr_curl_easy_tls_init (fr_curl_io_request_t *randle, fr_curl_tls_t const *conf); +CURL *fr_curl_tmp_handle(void); + +xlat_action_t fr_curl_xlat_uri_escape(UNUSED TALLOC_CTX *ctx, UNUSED fr_dcursor_t *out, + UNUSED xlat_ctx_t const *xctx, UNUSED request_t *request, + fr_value_box_list_t *in); + +xlat_action_t fr_curl_xlat_uri_unescape(UNUSED TALLOC_CTX *ctx, UNUSED fr_dcursor_t *out, + UNUSED xlat_ctx_t const *xctx, UNUSED request_t *request, + fr_value_box_list_t *in); + #ifdef __cplusplus } #endif diff --git a/src/lib/curl/xlat.c b/src/lib/curl/xlat.c new file mode 100644 index 00000000000..5e80f4b064d --- /dev/null +++ b/src/lib/curl/xlat.c @@ -0,0 +1,104 @@ +/* + * This program is is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +/** + * $Id$ + * @file curl/xlat.c + * @brief Generic xlat functions dependent on libcurl + * + * @copyright 2024 Arran Cudbard-Bell (a.cudbardb@freeradius.org) + */ + +#include +#include +#include + +#include "base.h" + +int fr_curl_xlat_refs = 0; + +xlat_arg_parser_t const fr_curl_xlat_uri_args[] = { + { .required = true, .concat = true, .type = FR_TYPE_STRING }, + XLAT_ARG_PARSER_TERMINATOR +}; + +/** xlat function to escape URI encoded strings + * + */ +xlat_action_t fr_curl_xlat_uri_escape(UNUSED TALLOC_CTX *ctx, UNUSED fr_dcursor_t *out, + UNUSED xlat_ctx_t const *xctx, UNUSED request_t *request, + fr_value_box_list_t *in) +{ + fr_value_box_t *to_escape = fr_value_box_list_head(in); + char *escaped; + + escaped = curl_easy_escape(fr_curl_tmp_handle(), to_escape->vb_strvalue, to_escape->vb_length); + if (!escaped) return -1; + + /* + * Returned string the same length - nothing changed + */ + if (strlen(escaped) == to_escape->vb_length) { + curl_free(escaped); + return 0; + } + + fr_value_box_clear_value(to_escape); + fr_value_box_strdup(to_escape, to_escape, NULL, escaped, to_escape->tainted); + + curl_free(escaped); + + fr_value_box_list_remove(in, to_escape); + fr_dcursor_insert(out, to_escape); + + return XLAT_ACTION_FAIL; +} + +/** xlat function to unescape URI encoded strings + * + */ +xlat_action_t fr_curl_xlat_uri_unescape(UNUSED TALLOC_CTX *ctx, UNUSED fr_dcursor_t *out, + UNUSED xlat_ctx_t const *xctx, UNUSED request_t *request, + fr_value_box_list_t *in) +{ + fr_value_box_t *to_unescape = fr_value_box_list_head(in); + int unescaped_len; + char *unescaped; + + fr_value_box_list_remove(in, to_unescape); + + unescaped = curl_easy_unescape(fr_curl_tmp_handle(), to_unescape->vb_strvalue, to_unescape->vb_length, &unescaped_len); + if (!unescaped) { + talloc_free(to_unescape); + return XLAT_ACTION_FAIL; + } + + /* + * Returned string the same length - nothing changed + */ + if ((size_t)unescaped_len == to_unescape->vb_length) { + curl_free(unescaped); + fr_dcursor_insert(out, to_unescape); + return XLAT_ACTION_DONE; + } + + fr_value_box_clear_value(to_unescape); + fr_value_box_bstrndup(to_unescape, to_unescape, NULL, unescaped, unescaped_len, to_unescape->tainted); + curl_free(unescaped); + fr_dcursor_insert(out, to_unescape); + + return XLAT_ACTION_DONE; +} diff --git a/src/lib/curl/xlat.h b/src/lib/curl/xlat.h new file mode 100644 index 00000000000..16ad2e24b60 --- /dev/null +++ b/src/lib/curl/xlat.h @@ -0,0 +1,45 @@ +#pragma once +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +/** Generic xlat functions dependent on libcurl + * + * @file src/lib/curl/xlat.h + * + * @copyright 2024 Arran Cudbard-Bell (a.cudbardb@freeradius.org) + */ +RCSIDH(curl_xlat_h, "$Id$") + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +extern xlat_arg_parser_t const fr_curl_xlat_uri_args; + +xlat_action_t fr_curl_xlat_uri_escape(UNUSED TALLOC_CTX *ctx, UNUSED fr_dcursor_t *out, + UNUSED xlat_ctx_t const *xctx, UNUSED request_t *request, + fr_value_box_list_t *in); + +xlat_action_t fr_curl_xlat_uri_unescape(UNUSED TALLOC_CTX *ctx, UNUSED fr_dcursor_t *out, + UNUSED xlat_ctx_t const *xctx, UNUSED request_t *request, + fr_value_box_list_t *in); + +#ifdef __cplusplus +} +#endif diff --git a/src/lib/server/tmpl_escape.h b/src/lib/server/tmpl_escape.h index 42faac73a50..8f12ebc363f 100644 --- a/src/lib/server/tmpl_escape.h +++ b/src/lib/server/tmpl_escape.h @@ -56,7 +56,9 @@ typedef void(*tmpl_escape_uctx_free_t)(void *uctx); /** When to apply escaping */ typedef enum { - TMPL_ESCAPE_PRE_CONCAT = 0, //!< Pre-concatenation escaping is useful for + TMPL_ESCAPE_NONE = 0, //!< No escaping is performed. + + TMPL_ESCAPE_PRE_CONCAT, //!< Pre-concatenation escaping is useful for ///< DSLs where elements of the expansion are ///< static, specified by the user, and other parts ///< are dynamic, which may or may not need to be @@ -78,6 +80,8 @@ typedef enum { typedef struct { fr_value_box_escape_t func; //!< How to escape when returned from evaluation. ///< Currently only used for async evaluation. + fr_value_box_safe_for_t safe_for; //!< Value to set on boxes which have been escaped + ///< by the #fr_value_box_escape_t function. tmpl_escape_mode_t mode; //!< Whether to apply escape function after ///< concatenation, i.e. to the final output diff --git a/src/lib/server/tmpl_eval.c b/src/lib/server/tmpl_eval.c index eb0cd845514..56a50081438 100644 --- a/src/lib/server/tmpl_eval.c +++ b/src/lib/server/tmpl_eval.c @@ -1358,7 +1358,11 @@ int tmpl_eval_cast_in_place(fr_value_box_list_t *list, request_t *request, tmpl_ if (tmpl_escape_pre_concat(vpt)) { uctx = tmpl_eval_escape_uctx_alloc(request, &vpt->rules.escape); - if (unlikely(fr_value_box_list_escape_in_place(list, vpt->rules.escape.func, uctx) < 0)) { + /* + * Sets escaped values, so boxes don't get re-escaped + */ + if (unlikely(fr_value_box_list_escape_in_place(list, vpt->rules.escape.func, + vpt->rules.escape.safe_for, uctx) < 0)) { error: tmpl_eval_escape_uctx_free(&vpt->rules.escape, uctx); return -1; @@ -1410,7 +1414,22 @@ int tmpl_eval_cast_in_place(fr_value_box_list_t *list, request_t *request, tmpl_ */ if ((!did_concat && tmpl_escape_pre_concat(vpt)) || tmpl_escape_post_concat(vpt)) { uctx = tmpl_eval_escape_uctx_alloc(request, &vpt->rules.escape); - if (unlikely(fr_value_box_list_escape_in_place(list, vpt->rules.escape.func, uctx) < 0)) goto error; + if (unlikely(fr_value_box_list_escape_in_place(list, vpt->rules.escape.func, + vpt->rules.escape.safe_for, uctx) < 0)) goto error; + } + + /* + * If there's no escape function, but there is + * an escaped value, mark all the boxes up with + * this value. + * + * This is mostly useful for call_env usage in + * modules where certain values are implicitly safe + * for consumption, like SQL statements in the SQL + * module. + */ + if (!vpt->rules.escape.func && vpt->rules.escape.safe_for) { + fr_value_box_list_mark_safe_for(list, vpt->rules.escape.safe_for); } VALUE_BOX_LIST_VERIFY(list); diff --git a/src/lib/server/tmpl_tokenize.c b/src/lib/server/tmpl_tokenize.c index 9ebf6c19496..689e4628eb5 100644 --- a/src/lib/server/tmpl_tokenize.c +++ b/src/lib/server/tmpl_tokenize.c @@ -3321,7 +3321,7 @@ fr_slen_t tmpl_afrom_substr(TALLOC_CTX *ctx, tmpl_t **out, * FIXME - We need an ephemeral version of this * too. */ - slen = xlat_tokenize_argv(vpt, &head, &our_in, p_rules, t_rules, false, false); + slen = xlat_tokenize_argv(vpt, &head, &our_in, NULL, p_rules, t_rules, false, false); if ((slen <= 0) || !head) { talloc_free(vpt); FR_SBUFF_ERROR_RETURN(&our_in); diff --git a/src/lib/server/trigger.c b/src/lib/server/trigger.c index 26f1f687071..b3cf109b7c3 100644 --- a/src/lib/server/trigger.c +++ b/src/lib/server/trigger.c @@ -430,7 +430,8 @@ int trigger_exec(unlang_interpret_t *intp, trigger->timeout = fr_time_delta_from_sec(5); /* FIXME - Should be configurable? */ slen = xlat_tokenize_argv(trigger, &trigger->xlat, - &FR_SBUFF_IN(trigger->command, talloc_array_length(trigger->command) - 1), NULL, NULL, false, false); + &FR_SBUFF_IN(trigger->command, talloc_array_length(trigger->command) - 1), + NULL, NULL, NULL, false, false); if (slen <= 0) { char *spaces, *text; diff --git a/src/lib/unlang/xlat.h b/src/lib/unlang/xlat.h index 3ecf2ad7c1b..6d14aca2704 100644 --- a/src/lib/unlang/xlat.h +++ b/src/lib/unlang/xlat.h @@ -152,6 +152,8 @@ typedef struct { ///< tainted ones. fr_type_t type; //!< Type to cast argument to. xlat_escape_func_t func; //!< Function to handle tainted values. + fr_value_box_safe_for_t safe_for; //!< Escaped value to set for boxes processed by + ///< this escape function. void *uctx; //!< Argument to pass to escape callback. } xlat_arg_parser_t; @@ -392,6 +394,7 @@ fr_slen_t xlat_tokenize_condition(TALLOC_CTX *ctx, xlat_exp_head_t **head, fr_sb fr_sbuff_parse_rules_t const *p_rules, tmpl_rules_t const *t_rules); fr_slen_t xlat_tokenize_argv(TALLOC_CTX *ctx, xlat_exp_head_t **head, fr_sbuff_t *in, + xlat_t const *xlat, 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, diff --git a/src/lib/unlang/xlat_eval.c b/src/lib/unlang/xlat_eval.c index 07a7f55d48e..9dd48a413e1 100644 --- a/src/lib/unlang/xlat_eval.c +++ b/src/lib/unlang/xlat_eval.c @@ -221,10 +221,12 @@ static xlat_action_t xlat_process_arg_list(TALLOC_CTX *ctx, fr_value_box_list_t #define ESCAPE(_arg, _vb, _arg_num) \ do { \ - if ((_arg)->func && ((_vb)->tainted || (_arg)->always_escape) && \ - ((_arg)->func(request, _vb, (_arg)->uctx) < 0)) { \ - RPEDEBUG("Function \"%s\" failed escaping argument %u", name, _arg_num); \ - return XLAT_ACTION_FAIL; \ + if ((_arg)->func && (!(_vb)->safe_for || !fr_value_box_is_safe_for((_vb), (_arg)->safe_for) || (_arg)->always_escape)) { \ + if ((_arg)->func(request, _vb, (_arg)->uctx) < 0) { \ + RPEDEBUG("Function \"%s\" failed escaping argument %u", name, _arg_num); \ + return XLAT_ACTION_FAIL; \ + } \ + fr_value_box_mark_safe_for((_vb), (_arg)->safe_for); \ } \ } while (0) @@ -520,6 +522,7 @@ bool xlat_process_return(request_t *request, xlat_t const *func, fr_value_box_li /* We are not forgiving for debug builds */ fr_assert_fail("Treating invalid return type as fatal"); } + fr_value_box_mark_safe_for(pos, func->return_safe_for); /* Always set this */ count++; } while ((pos = fr_value_box_list_next(returned, pos))); @@ -845,6 +848,7 @@ xlat_action_t xlat_frame_eval_resume(TALLOC_CTX *ctx, fr_dcursor_t *out, return xa; } + /** Process the result of a previous nested expansion * * @param[in] ctx to allocate value boxes in. @@ -1305,7 +1309,7 @@ static ssize_t xlat_eval_sync(TALLOC_CTX *ctx, char **out, request_t *request, x } len = vb->vb_length * 3; - escaped = talloc_array(pool, char, len); + MEM(escaped = talloc_array(pool, char, len)); real_len = escape(request, escaped, len, vb->vb_strvalue, UNCONST(void *, escape_ctx)); entry = vb->entry; diff --git a/src/lib/unlang/xlat_func.c b/src/lib/unlang/xlat_func.c index cb9b1f9edf9..ffbc950e239 100644 --- a/src/lib/unlang/xlat_func.c +++ b/src/lib/unlang/xlat_func.c @@ -439,6 +439,16 @@ void xlat_purify_func_set(xlat_t *xlat, xlat_purify_t func) xlat->purify = func; } +/** Set the escaped values for output boxes + * + * @param[in] xlat function to set the escaped value for (as returned by xlat_register). + * @param[in] safe_for escaped value to write to output boxes. + */ +void _xlat_func_safe_for_set(xlat_t *xlat, fr_value_box_safe_for_t safe_for) +{ + xlat->return_safe_for = safe_for; +} + /** Set global instantiation/detach callbacks * * @param[in] xlat to set instantiation callbacks for. diff --git a/src/lib/unlang/xlat_func.h b/src/lib/unlang/xlat_func.h index 565cb7be0e4..4fc69b1c01d 100644 --- a/src/lib/unlang/xlat_func.h +++ b/src/lib/unlang/xlat_func.h @@ -72,6 +72,14 @@ void xlat_func_resolve_set(xlat_t *xlat, xlat_resolve_t func); void xlat_purify_func_set(xlat_t *xlat, xlat_purify_t func); +/** Set the escaped values for output boxes + * + * @param[in] _xlat function to set the escaped value for (as returned by xlat_register). + * @param[in] _escaped escaped value to write to output boxes. + */ +#define xlat_func_safe_for_set(_xlat, _escaped) _xlat_func_safe_for_set(_xlat, (uintptr_t) (_escaped)) +void _xlat_func_safe_for_set(xlat_t *xlat, uintptr_t escaped); + /** Set a callback for global instantiation of xlat functions * * @param[in] _xlat function to set the callback for (as returned by xlat_register). diff --git a/src/lib/unlang/xlat_priv.h b/src/lib/unlang/xlat_priv.h index 4d1c7dc9ad5..7a656094602 100644 --- a/src/lib/unlang/xlat_priv.h +++ b/src/lib/unlang/xlat_priv.h @@ -92,6 +92,7 @@ typedef struct xlat_s { ///< xlat. Typically used for xlats which refer to tmpls ///< in their module config. + fr_value_box_safe_for_t return_safe_for; //!< Escaped value to set in output boxes. fr_type_t return_type; //!< Function is guaranteed to return one or more boxes ///< of this type. If the return type is FR_TYPE_VOID ///< then the xlat function can return any type of output. diff --git a/src/lib/unlang/xlat_tokenize.c b/src/lib/unlang/xlat_tokenize.c index 231f72ba45e..aba71092beb 100644 --- a/src/lib/unlang/xlat_tokenize.c +++ b/src/lib/unlang/xlat_tokenize.c @@ -25,6 +25,7 @@ * @copyright 2000,2006 The FreeRADIUS server project */ +#include "lib/util/value.h" RCSID("$Id$") #include @@ -111,7 +112,7 @@ static fr_sbuff_parse_rules_t const xlat_new_arg_rules = { }; 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_sbuff_parse_rules_t const *p_rules, tmpl_rules_t const *t_rules, fr_value_box_safe_for_t safe_for); #ifdef HAVE_REGEX /** Parse an xlat reference @@ -285,8 +286,7 @@ int xlat_validate_function_args(xlat_exp_t *node) * - 0 if the string was parsed into a function. * - <0 on parse error. */ -int xlat_tokenize_function_args(xlat_exp_head_t *head, fr_sbuff_t *in, - tmpl_rules_t const *t_rules) +int xlat_tokenize_function_args(xlat_exp_head_t *head, fr_sbuff_t *in, tmpl_rules_t const *t_rules) { xlat_exp_t *node; xlat_t *func; @@ -355,7 +355,7 @@ int xlat_tokenize_function_args(xlat_exp_head_t *head, fr_sbuff_t *in, * Now parse the child nodes that form the * function's arguments. */ - if (xlat_tokenize_argv(node, &node->call.args, in, &xlat_multi_arg_rules, t_rules, false, false) < 0) { + if (xlat_tokenize_argv(node, &node->call.args, in, func, &xlat_multi_arg_rules, t_rules, false, false) < 0) { goto error; } xlat_flags_merge(&node->flags, &node->call.args->flags); @@ -500,7 +500,8 @@ static int xlat_tokenize_function_new(xlat_exp_head_t *head, fr_sbuff_t *in, tmp * Now parse the child nodes that form the * function's arguments. */ - if (xlat_tokenize_argv(node, &node->call.args, in, &xlat_new_arg_rules, t_rules, true, (node->call.input_type == XLAT_INPUT_MONO)) < 0) { + if (xlat_tokenize_argv(node, &node->call.args, in, func, + &xlat_new_arg_rules, t_rules, true, (node->call.input_type == XLAT_INPUT_MONO)) < 0) { error: talloc_free(node); return -1; @@ -889,12 +890,14 @@ check_for_attr: * @param[in] in sbuff to parse. * @param[in] p_rules that control parsing. * @param[in] t_rules that control attribute reference and xlat function parsing. + * @param[in] safe_for mark up literal values as being pre-escaped. May be merged + * with t_rules in future. * @return * - 0 on success. * - -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_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; @@ -944,6 +947,7 @@ static int xlat_tokenize_input(xlat_exp_head_t *head, fr_sbuff_t *in, do_value_box: xlat_exp_set_name_buffer_shallow(node, str); fr_value_box_strdup(node, &node->data, NULL, str, false); + fr_value_box_mark_safe_for(&node->data, safe_for); node->flags.constant = true; fr_assert(node->flags.pure); @@ -1379,6 +1383,7 @@ ssize_t xlat_print(fr_sbuff_t *out, xlat_exp_head_t const *head, fr_sbuff_escape * later. * @param[out] out the head of the xlat list / tree structure. * @param[in] in the format string to expand. + * @param[in] xlat we're tokenizing arguments for. * @param[in] p_rules controlling how to parse the string outside of * any expansions. * @param[in] t_rules controlling how attribute references are parsed. @@ -1389,15 +1394,24 @@ ssize_t xlat_print(fr_sbuff_t *out, xlat_exp_head_t const *head, fr_sbuff_escape * - >0 on success which is the number of characters parsed. */ fr_slen_t xlat_tokenize_argv(TALLOC_CTX *ctx, xlat_exp_head_t **out, fr_sbuff_t *in, + xlat_t const *xlat, fr_sbuff_parse_rules_t const *p_rules, tmpl_rules_t const *t_rules, bool comma, bool allow_attr) { int argc = 0; fr_sbuff_t our_in = FR_SBUFF(in); ssize_t slen; fr_sbuff_marker_t m; - fr_sbuff_parse_rules_t const *our_p_rules; /* Bareword parse rules */ + fr_sbuff_parse_rules_t const *our_p_rules; /* Bareword parse rules */ fr_sbuff_parse_rules_t tmp_p_rules; xlat_exp_head_t *head; + xlat_arg_parser_t const *arg = NULL; + + if (xlat && xlat->args) { + arg = xlat->args; /* Track the arguments as we parse */ + } else { + static xlat_arg_parser_t default_arg = { .variadic = XLAT_ARG_VARIADIC_EMPTY_SQUASH }; + arg = &default_arg; + } MEM(head = xlat_exp_head_alloc(ctx)); if (p_rules && p_rules->terminals) { @@ -1468,7 +1482,7 @@ fr_slen_t xlat_tokenize_argv(TALLOC_CTX *ctx, xlat_exp_head_t **out, fr_sbuff_t } if (xlat_tokenize_input(node->group, &our_in, - our_p_rules, t_rules) < 0) { + our_p_rules, t_rules, arg->safe_for) < 0) { error: if (our_p_rules != &value_parse_rules_bareword_quoted) { talloc_const_free(our_p_rules->terminals); @@ -1486,7 +1500,7 @@ fr_slen_t xlat_tokenize_argv(TALLOC_CTX *ctx, xlat_exp_head_t **out, fr_sbuff_t XLAT_DEBUG("ARGV double quotes <-- %.*s", (int) fr_sbuff_remaining(&our_in), fr_sbuff_current(&our_in)); if (xlat_tokenize_input(node->group, &our_in, - &value_parse_rules_double_quoted, t_rules) < 0) goto error; + &value_parse_rules_double_quoted, t_rules, arg->safe_for) < 0) goto error; break; /* @@ -1507,6 +1521,7 @@ fr_slen_t xlat_tokenize_argv(TALLOC_CTX *ctx, xlat_exp_head_t **out, fr_sbuff_t xlat_exp_set_name_buffer_shallow(child, str); fr_value_box_strdup(child, &child->data, NULL, str, false); + fr_value_box_mark_safe_for(&child->data, arg->safe_for); /* Literal values are treated as implicitly safe */ child->flags.constant = true; fr_assert(child->flags.pure); xlat_exp_insert_tail(node->group, child); @@ -1576,6 +1591,14 @@ fr_slen_t xlat_tokenize_argv(TALLOC_CTX *ctx, xlat_exp_head_t **out, fr_sbuff_t fr_strerror_const("Unexpected text after argument"); goto error; } + + if (!arg->variadic) { + arg++; + if (arg->type == FR_TYPE_NULL) { + fr_strerror_printf("Too many arguments, expected %u", argc - 1); + goto error; + } + } } if (our_p_rules != &value_parse_rules_bareword_quoted) talloc_const_free(our_p_rules->terminals); @@ -1613,7 +1636,7 @@ fr_slen_t xlat_tokenize(TALLOC_CTX *ctx, xlat_exp_head_t **out, fr_sbuff_t *in, fr_strerror_clear(); /* Clear error buffer */ - if (xlat_tokenize_input(head, &our_in, p_rules, t_rules) < 0) { + if (xlat_tokenize_input(head, &our_in, p_rules, t_rules, 0) < 0) { talloc_free(head); FR_SBUFF_ERROR_RETURN(&our_in); } diff --git a/src/lib/util/value.c b/src/lib/util/value.c index e2c0ac3237a..f1141e65c30 100644 --- a/src/lib/util/value.c +++ b/src/lib/util/value.c @@ -621,8 +621,8 @@ static inline void fr_value_box_copy_meta(fr_value_box_t *dst, fr_value_box_t co dst->enumv = src->enumv; dst->type = src->type; dst->tainted = src->tainted; + dst->safe_for = src->safe_for; dst->secret = src->secret; - dst->safe = src->safe; fr_value_box_list_entry_init(dst); } @@ -5866,22 +5866,33 @@ int fr_value_box_list_concat_in_place(TALLOC_CTX *ctx, * * @param[in] vb to escape. * @param[in] escape function to apply to the value box. + * @param[in] safe_for the escaped value to check value boxes again. + * box has an escaped value that matches, it will + * not be re-escaped. * @param[in] uctx user context to pass to the escape function. * @return * - 0 on success. * - -1 on failure. */ -int fr_value_box_escape_in_place(fr_value_box_t *vb, fr_value_box_escape_t escape, void *uctx) +int fr_value_box_escape_in_place(fr_value_box_t *vb, fr_value_box_escape_t escape, + fr_value_box_safe_for_t safe_for, void *uctx) { + int ret; + switch (vb->type) { case FR_TYPE_GROUP: - return fr_value_box_list_escape_in_place(&vb->vb_group, escape, uctx); + return fr_value_box_list_escape_in_place(&vb->vb_group, escape, safe_for, uctx); default: break; } - return escape(vb, uctx); + ret = escape(vb, uctx); + if (unlikely(ret < 0)) return ret; + + vb->safe_for = safe_for; + + return 0; } /** Escape a list of value boxes in place @@ -5892,17 +5903,21 @@ int fr_value_box_escape_in_place(fr_value_box_t *vb, fr_value_box_escape_t escap * * @param[in] list to escape. * @param[in] escape function to apply to the value box. + * @param[in] safe_for the escaped value to check value boxes again. + * box has an escaped value that matches, it will + * not be re-escaped. * @param[in] uctx user context to pass to the escape function. * @return * - 0 on success. * - -1 on failure. */ -int fr_value_box_list_escape_in_place(fr_value_box_list_t *list, fr_value_box_escape_t escape, void *uctx) +int fr_value_box_list_escape_in_place(fr_value_box_list_t *list, fr_value_box_escape_t escape, + fr_value_box_safe_for_t safe_for, void *uctx) { int ret = 0; fr_value_box_list_foreach(list, vb) { - ret = fr_value_box_escape_in_place(vb, escape, uctx); + ret = fr_value_box_escape_in_place(vb, escape, safe_for, uctx); if (unlikely(ret < 0)) return ret; } @@ -6205,34 +6220,31 @@ void fr_value_box_list_verify(char const *file, int line, fr_value_box_list_t co /** Mark a value-box as "safe", of a particular type. * - * This means that users of that data who understand this particular value of the "safe" flag - * can then ignore the "tainted" flag, and use the value as if it was untainted. Every other user - * of the data must still treat it as tainted. - * - * Tainted data can be marked "safe". But marking it "safe" does not remove the "tainted" flag. - * - * Once data is marked safe, it cannot be marked as a different type of "safe". */ -int fr_value_box_mark_safe(fr_value_box_t *box, uint16_t safe) +void _fr_value_box_mark_safe_for(fr_value_box_t *vb, fr_value_box_safe_for_t safe_for) { - if (box->safe == safe) return 0; - - if (box->safe != 0) { - fr_strerror_const("Data was already marked 'safe', of a different type"); - return -1; - } - - box->safe = safe; - return 0; + vb->safe_for = safe_for; } /** Mark a value-box as "unsafe" * * This always succeeds, and there are no side effects. */ -void fr_value_box_mark_unsafe(fr_value_box_t *box) +void fr_value_box_mark_unsafe(fr_value_box_t *vb) +{ + vb->safe_for = 0; +} + +/** Set the escaped flag for all value boxes in a list + * + * @note Only operates on a single level. + * + * @param[in] list to operate on. + * @param[in] safe_for value to set. + */ +void fr_value_box_list_mark_safe_for(fr_value_box_list_t *list, fr_value_box_safe_for_t safe_for) { - box->safe = 0; + fr_value_box_list_foreach(list, vb) vb->safe_for = safe_for; } /** Check truthiness of values. diff --git a/src/lib/util/value.h b/src/lib/util/value.h index f6764067b4c..d2901a73352 100644 --- a/src/lib/util/value.h +++ b/src/lib/util/value.h @@ -146,6 +146,14 @@ typedef union { fr_value_box_list_t children; //!< for groups } fr_value_box_datum_t; +/** Escaping that's been applied to a value box + * + * This should be a unique value for each dialect being escaped. If the value is 0, + * then the box is not escaped. If the escaped value matches the escaped value of + * the function performing the escaping then it should not be re-escaped. + */ +typedef uintptr_t fr_value_box_safe_for_t; + /** Union containing all data types supported by the server * * This union contains all data types that can be represented by fr_pair_ts. It may also be used in other parts @@ -164,8 +172,12 @@ struct value_box_s { unsigned int secret : 1; //!< Same as #fr_dict_attr_flags_t secret unsigned int immutable : 1; //!< once set, the value cannot be changed unsigned int talloced : 1; //!< Talloced, not stack or text allocated. - - uint16_t _CONST safe; //!< more detailed safety + fr_value_box_safe_for_t _CONST safe_for; //!< A unique value to indicate if that value box is safe + ///< for consumption by a particular module for a particular + ///< purpose. e.g. LDAP, SQL, etc. + ///< Usually set by the xlat framework on behalf of an xlat + ///< escaping function, and checked by a #fr_value_box_escape_t + ///< to see if it needs to operate. fr_value_box_entry_t entry; //!< Doubly linked list entry. @@ -180,14 +192,6 @@ struct value_box_s { #endif }; -/** Macro to automatically define a value for the "safe" field based on the current module / library. - * - * Functions which escape tainted data can mark it "safe" for a - * particular purpose. Each module has it's own version of safety. - * e.g. LDAP, SQL, etc. Each module can then manage its own list of sub-types for safety. - */ -#define FR_VALUE_BOX_SAFE(_x) ((LOG_ID_LIB << 8) | _x) - /** @name List and cursor function definitions */ FR_DLIST_FUNCS(fr_value_box_list, fr_value_box_t, entry) @@ -633,9 +637,11 @@ fr_value_box_t *_fr_value_box_alloc(NDEBUG_LOCATION_ARGS TALLOC_CTX *ctx, fr_typ */ typedef int (*fr_value_box_escape_t)(fr_value_box_t *vb, void *uctx); -int fr_value_box_escape_in_place(fr_value_box_t *vb, fr_value_box_escape_t escape, void *uctx) +int fr_value_box_escape_in_place(fr_value_box_t *vb, fr_value_box_escape_t escape, + fr_value_box_safe_for_t escaped, void *uctx) CC_HINT(nonnull(1,2)); -int fr_value_box_list_escape_in_place(fr_value_box_list_t *list, fr_value_box_escape_t escape, void *uctx) +int fr_value_box_list_escape_in_place(fr_value_box_list_t *list, fr_value_box_escape_t escape, + fr_value_box_safe_for_t escaped, void *uctx) CC_HINT(nonnull(1,2)); /** @} */ @@ -1026,20 +1032,17 @@ int fr_value_box_ipaddr(fr_value_box_t *dst, fr_dict_attr_t const *enumv, int fr_value_unbox_ipaddr(fr_ipaddr_t *dst, fr_value_box_t *src) CC_HINT(nonnull); -static inline CC_HINT(nonnull, always_inline) -bool fr_value_box_is_safe(fr_value_box_t const *box, uint16_t safe) -{ - if (!safe) return false; - - return (box->safe == safe); -} - -int fr_value_box_mark_safe(fr_value_box_t *box, uint16_t safe) +#define fr_value_box_mark_safe_for(_box, _safe_for) _fr_value_box_mark_safe_for(_box, (fr_value_box_safe_for_t)_safe_for) +void _fr_value_box_mark_safe_for(fr_value_box_t *box, fr_value_box_safe_for_t safe_for) CC_HINT(nonnull); void fr_value_box_mark_unsafe(fr_value_box_t *box) CC_HINT(nonnull); +#define fr_value_box_is_safe_for(_box, _safe_for) (_box->safe_for == (fr_value_box_safe_for_t)_safe_for) + +void fr_value_box_list_mark_safe_for(fr_value_box_list_t *list, fr_value_box_safe_for_t safe_for); + static inline CC_HINT(nonnull, always_inline) bool fr_value_box_is_secret(fr_value_box_t const *box) { diff --git a/src/modules/rlm_ldap/rlm_ldap.c b/src/modules/rlm_ldap/rlm_ldap.c index a64c3a6e607..19f10472d05 100644 --- a/src/modules/rlm_ldap/rlm_ldap.c +++ b/src/modules/rlm_ldap/rlm_ldap.c @@ -27,6 +27,7 @@ * @copyright 2012 Alan DeKok (aland@freeradius.org) * @copyright 1999-2013 The FreeRADIUS Server Project. */ +#include "lib/util/value.h" RCSID("$Id$") USES_APPLE_DEPRECATED_API @@ -370,7 +371,7 @@ static xlat_action_t ldap_escape_xlat(TALLOC_CTX *ctx, fr_dcursor_t *out, /* * If it's already safe, just copy it over. */ - if (in_vb->safe == FR_VALUE_BOX_SAFE(1)) { + if (fr_value_box_is_safe_for(in_vb, ldap_escape_xlat)) { fr_value_box_copy(vb, vb, in_vb); fr_dcursor_append(out, vb); @@ -397,7 +398,7 @@ 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(vb, FR_VALUE_BOX_SAFE(1)); + fr_value_box_mark_safe_for(vb, ldap_escape_xlat); fr_dcursor_append(out, vb); return XLAT_ACTION_DONE; @@ -453,7 +454,7 @@ static int uri_part_escape(fr_value_box_t *vb, UNUSED void *uctx) /* * If it's already safe, don't do anything. */ - if (vb->safe == FR_VALUE_BOX_SAFE(1)) return 0; + 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 @@ -472,7 +473,7 @@ 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(vb, FR_VALUE_BOX_SAFE(1)); + fr_value_box_mark_safe_for(vb, uri_part_escape); return 0; } diff --git a/src/modules/rlm_rest/rlm_rest.c b/src/modules/rlm_rest/rlm_rest.c index 5f40300bfbc..48742b76f0d 100644 --- a/src/modules/rlm_rest/rlm_rest.c +++ b/src/modules/rlm_rest/rlm_rest.c @@ -35,13 +35,16 @@ RCSID("$Id$") #include #include #include +#include #include #include #include #include #include +#include #include #include +#include #include "rest.h" @@ -212,7 +215,8 @@ static const call_env_method_t _var = { \ .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 */ \ REST_CALL_ENV_REQUEST_COMMON(_dflt_username, _dflt_password) \ CALL_ENV_TERMINATOR \ @@ -356,17 +360,6 @@ static void *uri_part_escape_uctx_alloc(UNUSED request_t *request, void const *u return t_ctx; } - -/** Free the curl easy handle - * - * @param[in] arg curl easy handle to free. - */ -static int _uri_part_escape_free_on_exit(void *arg) -{ - curl_easy_cleanup(arg); - return 0; -} - /** URL escape a single box forming part of a URL * * @param[in] vb to escape @@ -377,21 +370,9 @@ static int _uri_part_escape_free_on_exit(void *arg) */ static int uri_part_escape(fr_value_box_t *vb, UNUSED void *uctx) { - static _Thread_local CURL *t_candle; char *escaped; - /* - * libcurl doesn't actually use the handle, but we pass one - * in anyway, just in case it does in the future. - */ - if (unlikely(t_candle == NULL)) { - CURL *candle; - - MEM(candle = curl_easy_init()); - fr_atexit_thread_local(t_candle, _uri_part_escape_free_on_exit, candle); - } - - escaped = curl_easy_escape(t_candle, vb->vb_strvalue, vb->vb_length); + escaped = curl_easy_escape(fr_curl_tmp_handle(), vb->vb_strvalue, vb->vb_length); if (!escaped) return -1; /* @@ -761,7 +742,10 @@ static unlang_action_t CC_HINT(nonnull) mod_authorize(rlm_rcode_t *p_result, mod void *handle; int ret; - if (!section->name) RETURN_MODULE_NOOP; + if (!section->name) { + RDEBUG2("No authorize section configured"); + RETURN_MODULE_NOOP; + } handle = rest_slab_reserve(t->slab); if (!handle) RETURN_MODULE_FAIL; @@ -870,7 +854,10 @@ static unlang_action_t CC_HINT(nonnull) mod_authenticate(rlm_rcode_t *p_result, int ret; - if (!section->name) RETURN_MODULE_NOOP; + if (!section->name) { + RDEBUG2("No authentication section configured"); + RETURN_MODULE_NOOP; + } /* * We can only authenticate user requests which HAVE @@ -976,7 +963,10 @@ static unlang_action_t CC_HINT(nonnull) mod_accounting(rlm_rcode_t *p_result, mo void *handle; int ret; - if (!section->name) RETURN_MODULE_NOOP; + if (!section->name) { + RDEBUG2("No accounting section configured"); + RETURN_MODULE_NOOP; + } handle = rest_slab_reserve(t->slab); if (!handle) RETURN_MODULE_FAIL; @@ -1051,7 +1041,10 @@ static unlang_action_t CC_HINT(nonnull) mod_post_auth(rlm_rcode_t *p_result, mod void *handle; int ret; - if (!section->name) RETURN_MODULE_NOOP; + if (!section->name) { + RDEBUG2("No post-auth section configured"); + RETURN_MODULE_NOOP; + } handle = rest_slab_reserve(t->slab); if (!handle) RETURN_MODULE_FAIL; diff --git a/src/modules/rlm_sql/rlm_sql.c b/src/modules/rlm_sql/rlm_sql.c index 3e8f3a4eda8..32e81734afa 100644 --- a/src/modules/rlm_sql/rlm_sql.c +++ b/src/modules/rlm_sql/rlm_sql.c @@ -24,12 +24,11 @@ * @copyright 2000 Mike Machado (mike@innercite.com) * @copyright 2000 Alan DeKok (aland@freeradius.org) */ + RCSID("$Id$") #define LOG_PREFIX mctx->inst->name -#include - #include #include #include @@ -196,7 +195,7 @@ static int sql_xlat_escape(request_t *request, fr_value_box_t *vb, void *uctx) /* * If it's already safe, don't do anything. */ - if (vb->safe == FR_VALUE_BOX_SAFE(inst->driver->number)) return 0; + if (fr_value_box_is_safe_for(vb, inst->driver)) return 0; handle = fr_pool_connection_get(inst->pool, request); if (!handle) { @@ -234,7 +233,7 @@ static int sql_xlat_escape(request_t *request, fr_value_box_t *vb, void *uctx) * safe value. This means that we don't * cross-contaminate "safe" values across databases. */ - fr_value_box_mark_safe(vb, FR_VALUE_BOX_SAFE(inst->driver->number)); + fr_value_box_mark_safe_for(vb, inst->driver); vb->entry = entry; fr_pool_connection_release(inst->pool, request, handle); @@ -248,7 +247,7 @@ static int sql_xlat_escape(request_t *request, fr_value_box_t *vb, void *uctx) * be returned instead. * @verbatim -%{sql:} +%sql() @endverbatim * * @ingroup xlat_functions