The FreeRADIUS server $Id: f3670dba8951ca10eb4948feb3dc3db9423a334f $
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: 4e7f44dd5c36aaa260adee9261c37316978bda39 $")
24
25#include <freeradius-devel/util/sbuff.h>
26#include <freeradius-devel/util/value.h>
27
28#include "uri.h"
29
30/** Escapes an individual value box that's part of a URI, advancing the pointer to uri_parts
31 *
32 * @note This function has a signature compatible with fr_uri_escape_func_t.
33 *
34 * @note This function may modify the type of boxes, as all boxes in the list are
35 * cast to strings before parsing.
36 *
37 * @param[in,out] uri_vb to escape
38 * @param[in] uctx A fr_uri_escape_ctx_t containing the initial fr_uri_part_t
39 * and the uctx to pass to the escaping function.
40 * @return
41 * - 0 on success.
42 * - -1 on failure.
43 */
44int fr_uri_escape(fr_value_box_t *uri_vb, void *uctx)
45{
46 fr_uri_escape_ctx_t *ctx = uctx;
47 fr_sbuff_t sbuff;
48 uint8_t adv;
49
50 /*
51 * Ensure boxes are strings before attempting to escape.
52 */
53 if (unlikely(uri_vb->type != FR_TYPE_STRING)) {
54 if (unlikely(fr_value_box_cast_in_place(uri_vb, uri_vb, FR_TYPE_STRING, uri_vb->enumv) < 0)) {
55 fr_strerror_printf_push("Unable to cast %pV to a string", uri_vb);
56 return -1;
57 }
58 }
59
60 /*
61 * Tainted boxes can only belong to a single part of the URI
62 */
63 if ((ctx->uri_part->safe_for > 0) && !fr_value_box_is_safe_for(uri_vb, ctx->uri_part->safe_for)) {
64 if (ctx->uri_part->func) {
65 /*
66 * Escaping often ends up breaking the vb's list pointers
67 * so remove it from the list and re-insert after the escaping
68 * has been done
69 */
70 fr_value_box_entry_t entry = uri_vb->entry;
71
72 switch (ctx->uri_part->func(uri_vb, ctx->uctx)) {
73 case 1: /* The function set safe_for itself */
74 break;
75
76 case 0:
78 break;
79
80 default:
81 fr_strerror_printf_push("Unable to escape tainted input %pV", uri_vb);
82 return -1;
83 }
84 uri_vb->entry = entry;
85 } else {
86 fr_strerror_printf_push("Unsafe input \"%pV\" not allowed in URI part %s", uri_vb, ctx->uri_part->name);
87 return -1;
88 }
89 return 0;
90 }
91
92 /*
93 * This URI part has no term chars - so no need to look for them
94 */
95 if (!ctx->uri_part->terminals) return 0;
96
97 /*
98 * Zero length box - no terminators here
99 */
100 if (uri_vb->vb_length == 0) return 0;
101
102 /*
103 * Look for URI part terminator
104 */
105 fr_sbuff_init_in(&sbuff, uri_vb->vb_strvalue, uri_vb->vb_length);
106 do {
107 fr_sbuff_adv_until(&sbuff, SIZE_MAX, ctx->uri_part->terminals, '\0');
108
109 /*
110 * We've not found a terminal in the current box
111 */
112 adv = ctx->uri_part->part_adv[fr_sbuff_uint8(&sbuff, '\0')];
113 if (adv == 0) continue;
114
115 /*
116 * This terminator has trailing characters to skip
117 */
118 if (ctx->uri_part->extra_skip) fr_sbuff_advance(&sbuff, ctx->uri_part->extra_skip);
119
120 /*
121 * Move to the next part
122 */
123 ctx->uri_part += adv;
124 if (!ctx->uri_part->terminals) break;
125 } while (fr_sbuff_advance(&sbuff, 1) > 0);
126
127 return 0;
128}
129
130/** Parse a list of value boxes representing a URI
131 *
132 * Reads a URI from a list of value boxes and parses it according to the
133 * definition in uri_parts. Tainted values, where allowed, are escaped
134 * using the function specified for the uri part.
135 *
136 * @note This function may modify the type of boxes, as all boxes in the list are
137 * cast to strings before parsing.
138 *
139 * @param uri to parse. A list of string type value boxes containing
140 * fragments of a URI.
141 * @param uri_parts definition of URI structure. Should point to the start
142 * of the array of uri parts.
143 * @param uctx to pass to escaping function
144 * @return
145 * - 0 on success
146 * - -1 on failure
147 */
148int fr_uri_escape_list(fr_value_box_list_t *uri, fr_uri_part_t const *uri_parts, void *uctx)
149{
150 fr_uri_escape_ctx_t ctx = {
151 .uri_part = uri_parts,
152 .uctx = uctx,
153 };
154
156
157 fr_value_box_list_foreach(uri, uri_vb) {
158 if (unlikely(fr_uri_escape(uri_vb, &ctx)) < 0) return -1;
159 }
160
161 return 0;
162}
163
164/** Searches for a matching scheme in the table of schemes, using a list of value boxes representing the URI
165 *
166 * @note Unlikel
167 *
168 * @param uri to parse. A list of string type value boxes containing
169 * fragments of a URI.
170 * @param schemes Table of schemes to search.
171 * @param schemes_len Number of schemes in the table.
172 * @param def Default scheme to use if none is found.
173 * @return The matching scheme, or def if none is found.
174 */
175int fr_uri_has_scheme(fr_value_box_list_t *uri, fr_table_num_sorted_t const *schemes, size_t schemes_len, int def)
176{
177 char scheme_buff[20]; /* hopefully no schemes over 20 bytes */
178 fr_sbuff_t sbuff = FR_SBUFF_OUT(scheme_buff, sizeof(scheme_buff));
179
180 /*
181 * Fill the scheme buffer with at most sizeof(scheme_buff) - 1 bytes of string data.
182 */
184 fr_value_box_t tmp;
185 ssize_t slen;
186
187 if (unlikely(vb->type != FR_TYPE_STRING)) {
188 if (unlikely(fr_value_box_cast(NULL, &tmp, FR_TYPE_STRING, vb->enumv, vb) < 0)) {
189 fr_strerror_printf_push("Unable to cast %pV to a string", vb);
190 return 0;
191 }
192 slen = fr_sbuff_in_bstrncpy(&sbuff, tmp.vb_strvalue,
193 fr_sbuff_remaining(&sbuff) > tmp.vb_length ? tmp.vb_length : fr_sbuff_remaining(&sbuff));
195 } else {
196 slen = fr_sbuff_in_bstrncpy(&sbuff, vb->vb_strvalue,
197 fr_sbuff_remaining(&sbuff) > vb->vb_length ? vb->vb_length : fr_sbuff_remaining(&sbuff));
198 }
199
200 if (unlikely(slen < 0)) return -1;
201 }
202
203 /*
204 * Ensure the first box is a valid scheme
205 */
206 return fr_table_value_by_longest_prefix(NULL, schemes, fr_sbuff_start(&sbuff), fr_sbuff_used(&sbuff), def);
207}
#define RCSID(id)
Definition build.h:560
#define unlikely(_x)
Definition build.h:455
@ FR_TYPE_STRING
String of printable characters.
long int ssize_t
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:1500
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:1958
#define fr_sbuff_start(_sbuff_or_marker)
#define fr_sbuff_uint8(_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:764
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:148
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:44
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:175
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:581
#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:3974
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:4224
void fr_value_box_clear_value(fr_value_box_t *data)
Clear/free any existing value.
Definition value.c:4359
#define fr_value_box_mark_safe_for(_box, _safe_for)
Definition value.h:1125
#define fr_value_box_is_safe_for(_box, _safe_for)
Definition value.h:1132
#define fr_value_box_list_foreach(_list_head, _iter)
Definition value.h:247