/src/freeradius-server/src/lib/util/uri.c
Line | Count | Source |
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 | | */ |
23 | | RCSID("$Id: 55fdcb691a654af04c0bc5ac608352b11dd11848 $") |
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 | | */ |
44 | | int fr_uri_escape(fr_value_box_t *uri_vb, void *uctx) |
45 | 0 | { |
46 | 0 | fr_uri_escape_ctx_t *ctx = uctx; |
47 | 0 | fr_sbuff_t sbuff; |
48 | 0 | uint8_t adv; |
49 | | |
50 | | /* |
51 | | * Ensure boxes are strings before attempting to escape. |
52 | | */ |
53 | 0 | if (unlikely(uri_vb->type != FR_TYPE_STRING)) { |
54 | 0 | if (unlikely(fr_value_box_cast_in_place(uri_vb, uri_vb, FR_TYPE_STRING, uri_vb->enumv) < 0)) { |
55 | 0 | fr_strerror_printf_push("Unable to cast %pV to a string", uri_vb); |
56 | 0 | return -1; |
57 | 0 | } |
58 | 0 | } |
59 | | |
60 | | /* |
61 | | * Tainted boxes can only belong to a single part of the URI |
62 | | */ |
63 | 0 | if ((ctx->uri_part->safe_for > 0) && !fr_value_box_is_safe_for(uri_vb, ctx->uri_part->safe_for)) { |
64 | 0 | 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 | 0 | fr_value_box_entry_t entry = uri_vb->entry; |
71 | 0 | if (ctx->uri_part->func(uri_vb, ctx->uctx) < 0) { |
72 | 0 | fr_strerror_printf_push("Unable to escape tainted input %pV", uri_vb); |
73 | 0 | return -1; |
74 | 0 | } |
75 | 0 | fr_value_box_mark_safe_for(uri_vb, ctx->uri_part->safe_for); |
76 | 0 | uri_vb->entry = entry; |
77 | 0 | } else { |
78 | 0 | fr_strerror_printf_push("Unsafe input \"%pV\" not allowed in URI part %s", uri_vb, ctx->uri_part->name); |
79 | 0 | return -1; |
80 | 0 | } |
81 | 0 | return 0; |
82 | 0 | } |
83 | | |
84 | | /* |
85 | | * This URI part has no term chars - so no need to look for them |
86 | | */ |
87 | 0 | if (!ctx->uri_part->terminals) return 0; |
88 | | |
89 | | /* |
90 | | * Zero length box - no terminators here |
91 | | */ |
92 | 0 | if (uri_vb->vb_length == 0) return 0; |
93 | | |
94 | | /* |
95 | | * Look for URI part terminator |
96 | | */ |
97 | 0 | fr_sbuff_init_in(&sbuff, uri_vb->vb_strvalue, uri_vb->vb_length); |
98 | 0 | do { |
99 | 0 | fr_sbuff_adv_until(&sbuff, SIZE_MAX, ctx->uri_part->terminals, '\0'); |
100 | | |
101 | | /* |
102 | | * We've not found a terminal in the current box |
103 | | */ |
104 | 0 | adv = ctx->uri_part->part_adv[fr_sbuff_char(&sbuff, '\0')]; |
105 | 0 | if (adv == 0) continue; |
106 | | |
107 | | /* |
108 | | * This terminator has trailing characters to skip |
109 | | */ |
110 | 0 | if (ctx->uri_part->extra_skip) fr_sbuff_advance(&sbuff, ctx->uri_part->extra_skip); |
111 | | |
112 | | /* |
113 | | * Move to the next part |
114 | | */ |
115 | 0 | ctx->uri_part += adv; |
116 | 0 | if (!ctx->uri_part->terminals) break; |
117 | 0 | } while (fr_sbuff_advance(&sbuff, 1) > 0); |
118 | |
|
119 | 0 | return 0; |
120 | 0 | } |
121 | | |
122 | | /** Parse a list of value boxes representing a URI |
123 | | * |
124 | | * Reads a URI from a list of value boxes and parses it according to the |
125 | | * definition in uri_parts. Tainted values, where allowed, are escaped |
126 | | * using the function specified for the uri part. |
127 | | * |
128 | | * @note This function may modify the type of boxes, as all boxes in the list are |
129 | | * cast to strings before parsing. |
130 | | * |
131 | | * @param uri to parse. A list of string type value boxes containing |
132 | | * fragments of a URI. |
133 | | * @param uri_parts definition of URI structure. Should point to the start |
134 | | * of the array of uri parts. |
135 | | * @param uctx to pass to escaping function |
136 | | * @return |
137 | | * - 0 on success |
138 | | * - -1 on failure |
139 | | */ |
140 | | int fr_uri_escape_list(fr_value_box_list_t *uri, fr_uri_part_t const *uri_parts, void *uctx) |
141 | 0 | { |
142 | 0 | fr_uri_escape_ctx_t ctx = { |
143 | 0 | .uri_part = uri_parts, |
144 | 0 | .uctx = uctx, |
145 | 0 | }; |
146 | |
|
147 | 0 | fr_strerror_clear(); |
148 | |
|
149 | 0 | fr_value_box_list_foreach(uri, uri_vb) { |
150 | 0 | if (unlikely(fr_uri_escape(uri_vb, &ctx)) < 0) return -1; |
151 | 0 | } |
152 | | |
153 | 0 | return 0; |
154 | 0 | } |
155 | | |
156 | | /** Searches for a matching scheme in the table of schemes, using a list of value boxes representing the URI |
157 | | * |
158 | | * @note Unlikel |
159 | | * |
160 | | * @param uri to parse. A list of string type value boxes containing |
161 | | * fragments of a URI. |
162 | | * @param schemes Table of schemes to search. |
163 | | * @param schemes_len Number of schemes in the table. |
164 | | * @param def Default scheme to use if none is found. |
165 | | * @return The matching scheme, or def if none is found. |
166 | | */ |
167 | | int fr_uri_has_scheme(fr_value_box_list_t *uri, fr_table_num_sorted_t const *schemes, size_t schemes_len, int def) |
168 | 0 | { |
169 | 0 | char scheme_buff[20]; /* hopefully no schemes over 20 bytes */ |
170 | 0 | fr_sbuff_t sbuff = FR_SBUFF_OUT(scheme_buff, sizeof(scheme_buff)); |
171 | | |
172 | | /* |
173 | | * Fill the scheme buffer with at most sizeof(scheme_buff) - 1 bytes of string data. |
174 | | */ |
175 | 0 | fr_value_box_list_foreach(uri, vb) { |
176 | 0 | fr_value_box_t tmp; |
177 | 0 | int ret; |
178 | |
|
179 | 0 | if (unlikely(vb->type != FR_TYPE_STRING)) { |
180 | 0 | if (unlikely(fr_value_box_cast(NULL, &tmp, FR_TYPE_STRING, vb->enumv, vb) < 0)) { |
181 | 0 | fr_strerror_printf_push("Unable to cast %pV to a string", vb); |
182 | 0 | return 0; |
183 | 0 | } |
184 | 0 | ret = fr_sbuff_in_bstrncpy(&sbuff, tmp.vb_strvalue, |
185 | 0 | fr_sbuff_remaining(&sbuff) > tmp.vb_length ? tmp.vb_length : fr_sbuff_remaining(&sbuff)); |
186 | 0 | fr_value_box_clear_value(&tmp); |
187 | 0 | } else { |
188 | 0 | ret = fr_sbuff_in_bstrncpy(&sbuff, vb->vb_strvalue, |
189 | 0 | fr_sbuff_remaining(&sbuff) > vb->vb_length ? vb->vb_length : fr_sbuff_remaining(&sbuff)); |
190 | 0 | } |
191 | | |
192 | 0 | if (unlikely(ret < 0)) return -1; |
193 | 0 | } |
194 | | |
195 | | /* |
196 | | * Ensure the first box is a valid scheme |
197 | | */ |
198 | 0 | return fr_table_value_by_longest_prefix(NULL, schemes, fr_sbuff_start(&sbuff), fr_sbuff_used(&sbuff), def); |
199 | 0 | } |