The FreeRADIUS server  $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
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  */
23 RCSID("$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  */
45 int fr_uri_escape(fr_value_box_t *uri_vb, void *uctx)
46 {
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  */
141 int 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  */
168 int 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:481
#define unlikely(_x)
Definition: build.h:379
fr_dcursor_eval_t void const * uctx
Definition: dcursor.h:546
@ FR_TYPE_STRING
String of printable characters.
Definition: merged_model.c:83
unsigned char uint8_t
Definition: merged_model.c:30
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:1458
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:1830
#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)
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
return fr_table_value_by_longest_prefix(NULL, schemes, fr_sbuff_start(&sbuff), fr_sbuff_used(&sbuff), def)
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