This is usually a pointer to a function used for escaping. This is still only partially integrated and requires more work.
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 ?
TARGET := $(TARGETNAME)$(L)
endif
-SOURCES := base.c io.c
+SOURCES := base.c io.c xlat.c
SRC_CFLAGS := @mod_cflags@
#endif
#include <freeradius-devel/util/talloc.h>
+#include <freeradius-devel/unlang/xlat_func.h>
#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*/
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
*
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;
}
fr_openssl_free();
#endif
curl_global_cleanup();
+
+ xlat_func_unregister("uri.escape");
+ xlat_func_unregister("uri.unescape");
}
/*
#include <freeradius-devel/server/request.h>
#include <freeradius-devel/util/event.h>
#include <freeradius-devel/util/slab.h>
+#include <freeradius-devel/unlang/xlat.h>
DIAG_OFF(DIAG_UNKNOWN_PRAGMAS)
DIAG_OFF(disabled-macro-expansion)
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
--- /dev/null
+/*
+ * 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 <freeradius-devel/util/value.h>
+#include <freeradius-devel/unlang/xlat.h>
+#include <freeradius-devel/curl/base.h>
+
+#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;
+}
--- /dev/null
+#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 <curl/curl.h>
+#include <freeradius-devel/unlang/xlat.h>
+
+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
/** 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
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
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;
*/
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);
* 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);
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;
///< 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;
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,
#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)
/* 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)));
return xa;
}
+
/** Process the result of a previous nested expansion
*
* @param[in] ctx to allocate value boxes in.
}
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;
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.
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).
///< 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.
* @copyright 2000,2006 The FreeRADIUS server project
*/
+#include "lib/util/value.h"
RCSID("$Id$")
#include <freeradius-devel/util/debug.h>
};
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
* - 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;
* 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);
* 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;
* @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;
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);
* 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.
* - >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) {
}
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);
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;
/*
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);
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);
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);
}
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);
}
*
* @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
*
* @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;
}
/** 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.
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
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.
#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)
*/
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));
/** @} */
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)
{
* @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
/*
* 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);
*/
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;
/*
* 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
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;
}
#include <freeradius-devel/server/tmpl.h>
#include <freeradius-devel/server/tmpl_escape.h>
#include <freeradius-devel/server/pairmove.h>
+#include <freeradius-devel/server/log.h>
#include <freeradius-devel/tls/base.h>
#include <freeradius-devel/util/atexit.h>
#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/unlang/call_env.h>
#include <freeradius-devel/unlang/xlat_func.h>
+#include <freeradius-devel/unlang/xlat.h>
#include "rest.h"
.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 \
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
*/
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;
/*
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;
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
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;
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;
* @copyright 2000 Mike Machado (mike@innercite.com)
* @copyright 2000 Alan DeKok (aland@freeradius.org)
*/
+
RCSID("$Id$")
#define LOG_PREFIX mctx->inst->name
-#include <ctype.h>
-
#include <freeradius-devel/server/base.h>
#include <freeradius-devel/server/exfile.h>
#include <freeradius-devel/server/map_proc.h>
/*
* 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) {
* 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);
* be returned instead.
*
@verbatim
-%{sql:<sql statement>}
+%sql(<sql statement>)
@endverbatim
*
* @ingroup xlat_functions