From: Arran Cudbard-Bell Date: Mon, 19 Jul 2021 21:23:54 +0000 (-0500) Subject: Pull URI escaping code into util library X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=db0611b9ec7c322f1e8268c93c953e26de88264c;p=thirdparty%2Ffreeradius-server.git Pull URI escaping code into util library We'll likely need this for utilities at some point --- diff --git a/src/lib/curl/base.h b/src/lib/curl/base.h index 3d406eaf05f..4f2b01f4470 100644 --- a/src/lib/curl/base.h +++ b/src/lib/curl/base.h @@ -98,7 +98,7 @@ typedef struct { typedef struct { CURL *candle; //!< Request specific handle. CURLcode result; //!< Result of executing the request. - request_t *request; //!< Current request. + request_t *request; //!< Current request. void *uctx; //!< Private data for the module using the API. } fr_curl_io_request_t; diff --git a/src/lib/unlang/xlat.h b/src/lib/unlang/xlat.h index 4b3b3948e1d..b6cdf4c6055 100644 --- a/src/lib/unlang/xlat.h +++ b/src/lib/unlang/xlat.h @@ -126,21 +126,6 @@ typedef struct { #define XLAT_ARG_PARSER_TERMINATOR { .required = false, .concat = false, .single = false, .variadic = false, \ .type = FR_TYPE_NULL, .func = NULL, .uctx = NULL } -/** Definition for a single part of a URI - * - */ -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 - xlat_escape_func_t func; //!< Function to use to escape tainted values -} xlat_uri_part_t; - -#define XLAT_URI_PART_TERMINATOR { .name = NULL, .terminals = NULL, .tainted_allowed = false, .func = NULL } - /** A callback when the the timeout occurs * * Used when a xlat needs wait for an event. @@ -343,8 +328,6 @@ int xlat_eval_pair(request_t *request, fr_pair_t *vp); bool xlat_async_required(xlat_exp_t const *xlat); -int xlat_parse_uri(request_t *request, fr_value_box_list_t *uri, xlat_uri_part_t const *uri_parts, void *uctx); - ssize_t xlat_tokenize_ephemeral(TALLOC_CTX *ctx, xlat_exp_t **head, xlat_flags_t *flags, fr_sbuff_t *in, fr_sbuff_parse_rules_t const *p_rules, tmpl_rules_t const *t_rules); diff --git a/src/lib/unlang/xlat_eval.c b/src/lib/unlang/xlat_eval.c index 357f76363cb..02c8bf9d680 100644 --- a/src/lib/unlang/xlat_eval.c +++ b/src/lib/unlang/xlat_eval.c @@ -2191,7 +2191,6 @@ void xlat_eval_free(void) done_init = false; } - /** Return whether or not async is required for this xlat. * * If the xlat is needs_async, then it MAY yield @@ -2216,84 +2215,3 @@ bool xlat_async_required(xlat_exp_t const *xlat) return false; } - - -/** Parse a list of value boxes representing a URI - * - * Reads a URI from a list of value boxes and parses it according to the - * definition in uri_parts. Tainted values, where allowed, are escaped - * using the function specified for the uri part. - * - * @param request currently being processed - * @param uri to parse - * @param uri_parts definition of URI structure - * @param uctx to pass to escaping function - * @return - * - 0 on success - * - -1 on failure - */ -int xlat_parse_uri(request_t *request, fr_value_box_list_t *uri, xlat_uri_part_t const *uri_parts, void *uctx) -{ - fr_value_box_t *uri_vb = NULL; - xlat_uri_part_t const *uri_part; - fr_sbuff_t sbuff; - char const *p; - - uri_part = uri_parts; - - while ((uri_vb = fr_dlist_next(uri, uri_vb))){ - if (uri_vb->tainted && !uri_part->tainted_allowed) { - REDEBUG("Tainted value not allowed for %s", uri_part->name); - return -1; - } - - /* - * Tainted boxes can only belong to a single part of the URI - */ - if (uri_vb->tainted) { - if ((uri_part->func) && (uri_part->func(request, uri_vb, uctx) < 0)) { - REDEBUG("Unable to escape tainted input %pV", uri_vb); - return -1; - } - continue; - } - - /* - * This URI part has no term chars - so no need to look for them - */ - if (!uri_part->terminals) continue; - - /* - * Zero length box - no terminators here - */ - if (uri_vb->length == 0) continue; - - /* - * Look for URI part terminator - */ - fr_sbuff_init(&sbuff, uri_vb->vb_strvalue, uri_vb->length); - - do { - fr_sbuff_adv_until(&sbuff, SIZE_MAX, uri_part->terminals, '\0'); - p = fr_sbuff_current(&sbuff); - - /* - * We've not found a terminal in the current box - */ - if (uri_part->part_adv[(uint8_t)*p] == 0) continue; - - /* - * This terminator has trailing characters to skip - */ - if (uri_part->extra_skip) fr_sbuff_advance(&sbuff, uri_part->extra_skip); - - /* - * Move to the next part - */ - uri_part += uri_part->part_adv[(uint8_t)*p]; - if (!uri_part->terminals) break; - } while (fr_sbuff_advance(&sbuff, 1) > 0); - } - - return 0; -} diff --git a/src/lib/util/libfreeradius-util.mk b/src/lib/util/libfreeradius-util.mk index a1cd4fff5e5..c9a193f48ca 100644 --- a/src/lib/util/libfreeradius-util.mk +++ b/src/lib/util/libfreeradius-util.mk @@ -79,6 +79,7 @@ SOURCES := \ types.c \ udp.c \ udpfromto.c \ + uri.c \ value.c \ version.c diff --git a/src/lib/util/uri.c b/src/lib/util/uri.c new file mode 100644 index 00000000000..48df1ffaa3c --- /dev/null +++ b/src/lib/util/uri.c @@ -0,0 +1,107 @@ +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +/** Functions for dealing with URIs + * + * @file src/lib/util/uri.c + * + * @copyright 2021 The FreeRADIUS server project + */ +RCSID("$Id$") + +#include "uri.h" + +/** Parse a list of value boxes representing a URI + * + * Reads a URI from a list of value boxes and parses it according to the + * definition in uri_parts. Tainted values, where allowed, are escaped + * using the function specified for the uri part. + * + * @param uri to parse. A list of string type value boxes containing + * fragments of a URI. + * @param uri_parts definition of URI structure + * @param uctx to pass to escaping function + * @return + * - 0 on success + * - -1 on failure + */ +int fr_uri_escape(fr_value_box_list_t *uri, fr_uri_part_t const *uri_parts, void *uctx) +{ + fr_value_box_t *uri_vb = NULL; + fr_uri_part_t const *uri_part; + fr_sbuff_t sbuff; + char const *p; + + uri_part = uri_parts; + + fr_strerror_clear(); + + while ((uri_vb = fr_dlist_next(uri, uri_vb))){ + if (uri_vb->tainted && !uri_part->tainted_allowed) { + fr_strerror_printf_push("Tainted value not allowed for %s", uri_part->name); + return -1; + } + + /* + * Tainted boxes can only belong to a single part of the URI + */ + if (uri_vb->tainted) { + if ((uri_part->func) && (uri_part->func(uri_vb, uctx) < 0)) { + fr_strerror_printf_push("Unable to escape tainted input %pV", uri_vb); + return -1; + } + continue; + } + + /* + * This URI part has no term chars - so no need to look for them + */ + if (!uri_part->terminals) continue; + + /* + * Zero length box - no terminators here + */ + if (uri_vb->length == 0) continue; + + /* + * Look for URI part terminator + */ + fr_sbuff_init(&sbuff, uri_vb->vb_strvalue, uri_vb->length); + + do { + fr_sbuff_adv_until(&sbuff, SIZE_MAX, uri_part->terminals, '\0'); + p = fr_sbuff_current(&sbuff); + + /* + * We've not found a terminal in the current box + */ + if (uri_part->part_adv[(uint8_t)*p] == 0) continue; + + /* + * This terminator has trailing characters to skip + */ + if (uri_part->extra_skip) fr_sbuff_advance(&sbuff, uri_part->extra_skip); + + /* + * Move to the next part + */ + uri_part += uri_part->part_adv[(uint8_t)*p]; + if (!uri_part->terminals) break; + } while (fr_sbuff_advance(&sbuff, 1) > 0); + } + + return 0; +} diff --git a/src/lib/util/uri.h b/src/lib/util/uri.h new file mode 100644 index 00000000000..e634d87cb71 --- /dev/null +++ b/src/lib/util/uri.h @@ -0,0 +1,62 @@ +#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 + */ + +/** Functions for dealing with URIs + * + * @file src/lib/util/uri.c + * + * @copyright 2021 The FreeRADIUS server project + */ +RCSIDH(uri_h, "$Id$") + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** A function used to escape an argument passed to an xlat + * + * @param[in,out] vb to escape + * @param[in] uctx a "context" for the escaping + * @return + * - 0 on success. + * - -1 on failure. + */ +typedef int (*fr_uri_escape_func_t)(fr_value_box_t *vb, void *uctx); + +/** Definition for a single part of a URI + * + */ +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 +} fr_uri_part_t; + +#define XLAT_URI_PART_TERMINATOR { .name = NULL, .terminals = NULL, .tainted_allowed = false, .func = NULL } + +int fr_uri_escape(fr_value_box_list_t *uri, fr_uri_part_t const *uri_parts, void *uctx); + +#ifdef __cplusplus +} +#endif diff --git a/src/modules/rlm_rest/rlm_rest.c b/src/modules/rlm_rest/rlm_rest.c index 20790d98d39..487f43b244a 100644 --- a/src/modules/rlm_rest/rlm_rest.c +++ b/src/modules/rlm_rest/rlm_rest.c @@ -27,10 +27,11 @@ RCSID("$Id$") #include #include #include -#include #include #include +#include #include +#include #include #include "rest.h" @@ -295,18 +296,18 @@ finish: /** URL escape a single box forming part of a URL * - * @param request being processed - * @param vb to escape - * @param uctx context containing CURL handle + * @param[in] vb to escape + * @param[in] uctx context containing CURL handle * @return * - 0 on success * - -1 on failure */ -static int uri_part_escape(request_t *request, fr_value_box_t *vb, void *uctx) +static int uri_part_escape(fr_value_box_t *vb, void *uctx) { char *escaped; fr_curl_io_request_t *randle = talloc_get_type_abort(uctx, fr_curl_io_request_t); fr_dlist_t entry; + request_t *request = randle->request; escaped = curl_easy_escape(randle->candle, vb->vb_strvalue, vb->length); if (!escaped) return -1; @@ -337,7 +338,7 @@ static int uri_part_escape(request_t *request, fr_value_box_t *vb, void *uctx) return 0; } -static xlat_uri_part_t const rest_uri_parts[] = { +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 }, @@ -435,12 +436,9 @@ static xlat_action_t rest_xlat(UNUSED TALLOC_CTX *ctx, UNUSED fr_dcursor_t *out, fr_assert(in_vb->type == FR_TYPE_GROUP); - if (xlat_parse_uri(request, &in_vb->vb_group, rest_uri_parts, randle) < 0) return XLAT_ACTION_FAIL; + if (fr_uri_escape(&in_vb->vb_group, rest_uri_parts, randle) < 0) { + RPEDEBUG("Failed escaping URI"); - uri_vb = fr_dlist_head(&in_vb->vb_group); - - if (fr_value_box_list_concat(uri_vb, uri_vb, &in_vb->vb_group, FR_TYPE_STRING, true) < 0) { - REDEBUG("Failed to concatenate URI"); error: rest_request_cleanup(mod_inst, randle); fr_pool_connection_release(t->pool, request, randle); @@ -449,6 +447,12 @@ static xlat_action_t rest_xlat(UNUSED TALLOC_CTX *ctx, UNUSED fr_dcursor_t *out, return XLAT_ACTION_FAIL; } + uri_vb = fr_dlist_head(&in_vb->vb_group); + if (fr_value_box_list_concat(uri_vb, uri_vb, &in_vb->vb_group, FR_TYPE_STRING, true) < 0) { + REDEBUG("Concatenating URI"); + goto error; + } + /* * Any additional arguments are freeform data */