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 #include "lib/util/sbuff.h"
24 #include "lib/util/table.h"
25 #include "lib/util/value.h"
26 RCSID("$Id: c54d54b96efdcd731cdc56710f4a387270c55f93 $")
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 {
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  if (ctx->uri_part->func(uri_vb, ctx->uctx) < 0) {
72  fr_strerror_printf_push("Unable to escape tainted input %pV", uri_vb);
73  return -1;
74  }
76  uri_vb->entry = entry;
77  } else {
78  fr_strerror_printf_push("Unsafe input \"%pV\" not allowed in URI part %s", uri_vb, ctx->uri_part->name);
79  return -1;
80  }
81  return 0;
82  }
83 
84  /*
85  * This URI part has no term chars - so no need to look for them
86  */
87  if (!ctx->uri_part->terminals) return 0;
88 
89  /*
90  * Zero length box - no terminators here
91  */
92  if (uri_vb->vb_length == 0) return 0;
93 
94  /*
95  * Look for URI part terminator
96  */
97  fr_sbuff_init_in(&sbuff, uri_vb->vb_strvalue, uri_vb->vb_length);
98  do {
99  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  adv = ctx->uri_part->part_adv[fr_sbuff_char(&sbuff, '\0')];
105  if (adv == 0) continue;
106 
107  /*
108  * This terminator has trailing characters to skip
109  */
110  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  ctx->uri_part += adv;
116  if (!ctx->uri_part->terminals) break;
117  } while (fr_sbuff_advance(&sbuff, 1) > 0);
118 
119  return 0;
120 }
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 {
142  fr_uri_escape_ctx_t ctx = {
143  .uri_part = uri_parts,
144  .uctx = uctx,
145  };
146 
148 
149  fr_value_box_list_foreach_safe(uri, uri_vb) {
150  if (unlikely(fr_uri_escape(uri_vb, &ctx)) < 0) return -1;
151  }}
152 
153  return 0;
154 }
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 {
169  char scheme_buff[20]; /* hopefully no schemes over 20 bytes */
170  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  */
176  fr_value_box_t tmp;
177  int ret;
178 
179  if (unlikely(vb->type != FR_TYPE_STRING)) {
180  if (unlikely(fr_value_box_cast(NULL, &tmp, FR_TYPE_STRING, vb->enumv, vb) < 0)) {
181  fr_strerror_printf_push("Unable to cast %pV to a string", vb);
182  return 0;
183  }
184  ret = fr_sbuff_in_bstrncpy(&sbuff, tmp.vb_strvalue,
185  fr_sbuff_remaining(&sbuff) > tmp.vb_length ? tmp.vb_length : fr_sbuff_remaining(&sbuff));
187  } else {
188  ret = fr_sbuff_in_bstrncpy(&sbuff, vb->vb_strvalue,
189  fr_sbuff_remaining(&sbuff) > vb->vb_length ? vb->vb_length : fr_sbuff_remaining(&sbuff));
190  }
191 
192  if (unlikely(ret < 0)) return -1;
193  }}
194 
195  /*
196  * Ensure the first box is a valid scheme
197  */
198  return fr_table_value_by_longest_prefix(NULL, schemes, fr_sbuff_start(&sbuff), fr_sbuff_used(&sbuff), def);
199 }
#define RCSID(id)
Definition: build.h:444
#define unlikely(_x)
Definition: build.h:378
@ 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:1445
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:1811
A generic buffer structure for string printing and parsing strings.
#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)
Lookup table functions.
An element in a lexicographically sorted array of name to num mappings.
Definition: table.h:45
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:140
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: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:167
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:3301
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:3521
void fr_value_box_clear_value(fr_value_box_t *data)
Clear/free any existing value.
Definition: value.c:3630
Boxed value structures and functions to manipulate them.
#define fr_value_box_list_foreach_safe(_list_head, _iter)
Definition: value.h:200
#define fr_value_box_mark_safe_for(_box, _safe_for)
Definition: value.h:1035
#define fr_value_box_is_safe_for(_box, _safe_for)
Definition: value.h:1042