The FreeRADIUS server $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
Loading...
Searching...
No Matches
uri.c
Go to the documentation of this file.
1/*
2 * This library is free software; you can redistribute it and/or
3 * modify it under the terms of the GNU Lesser General Public
4 * License as published by the Free Software Foundation; either
5 * version 2.1 of the License, or (at your option) any later version.
6 *
7 * This library is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
10 * Lesser General Public License for more details.
11 *
12 * You should have received a copy of the GNU Lesser General Public
13 * License along with this library; if not, write to the Free Software
14 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
15 */
16
17/** Functions for dealing with URIs
18 *
19 * @file src/lib/util/uri.c
20 *
21 * @copyright 2021 The FreeRADIUS server project
22 */
23RCSID("$Id: c27814f8f3fdb18f3d49e45fbb2261e79595ca6a $")
24
25#include <freeradius-devel/util/sbuff.h>
26#include <freeradius-devel/util/table.h>
27#include <freeradius-devel/util/value.h>
28
29#include "uri.h"
30
31/** Escapes an individual value box that's part of a URI, advancing the pointer to uri_parts
32 *
33 * @note This function has a signature compatible with fr_uri_escape_func_t.
34 *
35 * @note This function may modify the type of boxes, as all boxes in the list are
36 * cast to strings before parsing.
37 *
38 * @param[in,out] uri_vb to escape
39 * @param[in] uctx A fr_uri_escape_ctx_t containing the initial fr_uri_part_t
40 * and the uctx to pass to the escaping function.
41 * @return
42 * - 0 on success.
43 * - -1 on failure.
44 */
45int fr_uri_escape(fr_value_box_t *uri_vb, void *uctx)
46{
47 fr_uri_escape_ctx_t *ctx = uctx;
48 fr_sbuff_t sbuff;
49 uint8_t adv;
50
51 /*
52 * Ensure boxes are strings before attempting to escape.
53 */
54 if (unlikely(uri_vb->type != FR_TYPE_STRING)) {
55 if (unlikely(fr_value_box_cast_in_place(uri_vb, uri_vb, FR_TYPE_STRING, uri_vb->enumv) < 0)) {
56 fr_strerror_printf_push("Unable to cast %pV to a string", uri_vb);
57 return -1;
58 }
59 }
60
61 /*
62 * Tainted boxes can only belong to a single part of the URI
63 */
64 if ((ctx->uri_part->safe_for > 0) && !fr_value_box_is_safe_for(uri_vb, ctx->uri_part->safe_for)) {
65 if (ctx->uri_part->func) {
66 /*
67 * Escaping often ends up breaking the vb's list pointers
68 * so remove it from the list and re-insert after the escaping
69 * has been done
70 */
71 fr_value_box_entry_t entry = uri_vb->entry;
72 if (ctx->uri_part->func(uri_vb, ctx->uctx) < 0) {
73 fr_strerror_printf_push("Unable to escape tainted input %pV", uri_vb);
74 return -1;
75 }
77 uri_vb->entry = entry;
78 } else {
79 fr_strerror_printf_push("Unsafe input \"%pV\" not allowed in URI part %s", uri_vb, ctx->uri_part->name);
80 return -1;
81 }
82 return 0;
83 }
84
85 /*
86 * This URI part has no term chars - so no need to look for them
87 */
88 if (!ctx->uri_part->terminals) return 0;
89
90 /*
91 * Zero length box - no terminators here
92 */
93 if (uri_vb->vb_length == 0) return 0;
94
95 /*
96 * Look for URI part terminator
97 */
98 fr_sbuff_init_in(&sbuff, uri_vb->vb_strvalue, uri_vb->vb_length);
99 do {
100 fr_sbuff_adv_until(&sbuff, SIZE_MAX, ctx->uri_part->terminals, '\0');
101
102 /*
103 * We've not found a terminal in the current box
104 */
105 adv = ctx->uri_part->part_adv[fr_sbuff_char(&sbuff, '\0')];
106 if (adv == 0) continue;
107
108 /*
109 * This terminator has trailing characters to skip
110 */
111 if (ctx->uri_part->extra_skip) fr_sbuff_advance(&sbuff, ctx->uri_part->extra_skip);
112
113 /*
114 * Move to the next part
115 */
116 ctx->uri_part += adv;
117 if (!ctx->uri_part->terminals) break;
118 } while (fr_sbuff_advance(&sbuff, 1) > 0);
119
120 return 0;
121}
122
123/** Parse a list of value boxes representing a URI
124 *
125 * Reads a URI from a list of value boxes and parses it according to the
126 * definition in uri_parts. Tainted values, where allowed, are escaped
127 * using the function specified for the uri part.
128 *
129 * @note This function may modify the type of boxes, as all boxes in the list are
130 * cast to strings before parsing.
131 *
132 * @param uri to parse. A list of string type value boxes containing
133 * fragments of a URI.
134 * @param uri_parts definition of URI structure. Should point to the start
135 * of the array of uri parts.
136 * @param uctx to pass to escaping function
137 * @return
138 * - 0 on success
139 * - -1 on failure
140 */
141int fr_uri_escape_list(fr_value_box_list_t *uri, fr_uri_part_t const *uri_parts, void *uctx)
142{
143 fr_uri_escape_ctx_t ctx = {
144 .uri_part = uri_parts,
145 .uctx = uctx,
146 };
147
149
150 fr_value_box_list_foreach_safe(uri, uri_vb) {
151 if (unlikely(fr_uri_escape(uri_vb, &ctx)) < 0) return -1;
152 }}
153
154 return 0;
155}
156
157/** Searches for a matching scheme in the table of schemes, using a list of value boxes representing the URI
158 *
159 * @note Unlikel
160 *
161 * @param uri to parse. A list of string type value boxes containing
162 * fragments of a URI.
163 * @param schemes Table of schemes to search.
164 * @param schemes_len Number of schemes in the table.
165 * @param def Default scheme to use if none is found.
166 * @return The matching scheme, or def if none is found.
167 */
168int fr_uri_has_scheme(fr_value_box_list_t *uri, fr_table_num_sorted_t const *schemes, size_t schemes_len, int def)
169{
170 char scheme_buff[20]; /* hopefully no schemes over 20 bytes */
171 fr_sbuff_t sbuff = FR_SBUFF_OUT(scheme_buff, sizeof(scheme_buff));
172
173 /*
174 * Fill the scheme buffer with at most sizeof(scheme_buff) - 1 bytes of string data.
175 */
177 fr_value_box_t tmp;
178 int ret;
179
180 if (unlikely(vb->type != FR_TYPE_STRING)) {
181 if (unlikely(fr_value_box_cast(NULL, &tmp, FR_TYPE_STRING, vb->enumv, vb) < 0)) {
182 fr_strerror_printf_push("Unable to cast %pV to a string", vb);
183 return 0;
184 }
185 ret = fr_sbuff_in_bstrncpy(&sbuff, tmp.vb_strvalue,
186 fr_sbuff_remaining(&sbuff) > tmp.vb_length ? tmp.vb_length : fr_sbuff_remaining(&sbuff));
188 } else {
189 ret = fr_sbuff_in_bstrncpy(&sbuff, vb->vb_strvalue,
190 fr_sbuff_remaining(&sbuff) > vb->vb_length ? vb->vb_length : fr_sbuff_remaining(&sbuff));
191 }
192
193 if (unlikely(ret < 0)) return -1;
194 }}
195
196 /*
197 * Ensure the first box is a valid scheme
198 */
199 return fr_table_value_by_longest_prefix(NULL, schemes, fr_sbuff_start(&sbuff), fr_sbuff_used(&sbuff), def);
200}
#define RCSID(id)
Definition build.h:483
#define unlikely(_x)
Definition build.h:381
@ FR_TYPE_STRING
String of printable characters.
unsigned char uint8_t
ssize_t fr_sbuff_in_bstrncpy(fr_sbuff_t *sbuff, char const *str, size_t len)
Copy bytes into the sbuff up to the first \0.
Definition sbuff.c:1480
size_t fr_sbuff_adv_until(fr_sbuff_t *sbuff, size_t len, fr_sbuff_term_t const *tt, char escape_chr)
Wind position until we hit a character in the terminal set.
Definition sbuff.c:1852
#define fr_sbuff_start(_sbuff_or_marker)
#define fr_sbuff_char(_sbuff_or_marker, _eob)
#define fr_sbuff_advance(_sbuff_or_marker, _len)
#define fr_sbuff_init_in(_out, _start, _len_or_end)
#define fr_sbuff_remaining(_sbuff_or_marker)
#define FR_SBUFF_OUT(_start, _len_or_end)
#define fr_sbuff_used(_sbuff_or_marker)
#define fr_table_value_by_longest_prefix(_match_len, _table, _name, _name_len, _def)
Find the longest string match using a sorted or ordered table.
Definition table.h:732
An element in a lexicographically sorted array of name to num mappings.
Definition table.h:49
int fr_uri_escape_list(fr_value_box_list_t *uri, fr_uri_part_t const *uri_parts, void *uctx)
Parse a list of value boxes representing a URI.
Definition uri.c:141
int fr_uri_escape(fr_value_box_t *uri_vb, void *uctx)
Escapes an individual value box that's part of a URI, advancing the pointer to uri_parts.
Definition uri.c:45
int fr_uri_has_scheme(fr_value_box_list_t *uri, fr_table_num_sorted_t const *schemes, size_t schemes_len, int def)
Searches for a matching scheme in the table of schemes, using a list of value boxes representing the ...
Definition uri.c:168
fr_sbuff_term_t const * terminals
Characters that mark the end of this part.
Definition uri.h:48
size_t extra_skip
How many additional characters to skip after the terminal.
Definition uri.h:50
fr_uri_part_t const * uri_part
Start of the uri parts array.
Definition uri.h:61
fr_uri_escape_func_t func
Function to use to escape tainted values.
Definition uri.h:53
fr_value_box_safe_for_t safe_for
What type of value is safe for this part.
Definition uri.h:52
char const * name
Name of this part of the URI.
Definition uri.h:47
void * uctx
to pass to fr_uri_escape_func_t.
Definition uri.h:63
uint8_t const part_adv[UINT8_MAX+1]
How many parts to advance for a specific terminal.
Definition uri.h:49
uctx to pass to fr_uri_escape
Definition uri.h:60
Definition for a single part of a URI.
Definition uri.h:46
void fr_strerror_clear(void)
Clears all pending messages from the talloc pools.
Definition strerror.c:577
#define fr_strerror_printf_push(_fmt,...)
Add a message to an existing stack of messages at the tail.
Definition strerror.h:84
int fr_value_box_cast(TALLOC_CTX *ctx, fr_value_box_t *dst, fr_type_t dst_type, fr_dict_attr_t const *dst_enumv, fr_value_box_t const *src)
Convert one type of fr_value_box_t to another.
Definition value.c:3352
int fr_value_box_cast_in_place(TALLOC_CTX *ctx, fr_value_box_t *vb, fr_type_t dst_type, fr_dict_attr_t const *dst_enumv)
Convert one type of fr_value_box_t to another in place.
Definition value.c:3572
void fr_value_box_clear_value(fr_value_box_t *data)
Clear/free any existing value.
Definition value.c:3681
#define fr_value_box_list_foreach_safe(_list_head, _iter)
Definition value.h:207
#define fr_value_box_mark_safe_for(_box, _safe_for)
Definition value.h:1048
#define fr_value_box_is_safe_for(_box, _safe_for)
Definition value.h:1055