The FreeRADIUS server $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
Loading...
Searching...
No Matches
regex.c
Go to the documentation of this file.
1/*
2 * This program is free software; you can redistribute it and/or modify
3 * it under the terms of the GNU General Public License as published by
4 * the Free Software Foundation; either version 2 of the License, or
5 * (at your option) any later version.
6 *
7 * This program 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
10 * GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software
14 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
15 */
16
17/*
18 * $Id: 139649ce6fd5eb75ad1d1c6c2c50105ec8fefff8 $
19 *
20 * @file src/lib/server/regex.c
21 * @brief Regular expression functions used by the server library.
22 *
23 * @copyright 2014 The FreeRADIUS server project
24 */
25
26RCSID("$Id: 139649ce6fd5eb75ad1d1c6c2c50105ec8fefff8 $")
27
28#include <freeradius-devel/server/regex.h>
29#include <freeradius-devel/server/request_data.h>
30#include <freeradius-devel/util/debug.h>
31
32#ifdef HAVE_REGEX
33
34#define REQUEST_DATA_REGEX (0xadbeef00)
35
36typedef struct {
37#if defined(HAVE_REGEX_PCRE) || defined(HAVE_REGEX_PCRE2)
38 regex_t *preg; //!< Compiled pattern.
39#endif
40 fr_regmatch_t *regmatch; //!< Match vectors.
41
43 bool secret;
44} fr_regcapture_t;
45
46/** Adds subcapture values to request data
47 *
48 * Allows use of %{n} expansions.
49 *
50 * @note If preg was runtime-compiled, it will be consumed and *preg will be set to NULL.
51 * @note regmatch will be consumed and *regmatch will be set to NULL.
52 * @note Their lifetimes will be bound to the match request data.
53 *
54 * @param[in] request Current request.
55 * @param[in,out] preg Compiled pattern. May be set to NULL if
56 * reparented to the regcapture struct.
57 * @param[in,out] regmatch Pointers into value. May be set to NULL if
58 * reparented to the regcapture struct.
59 * @param[in] in value-box which we matched against
60 */
61void regex_sub_to_request(request_t *request, regex_t **preg, fr_regmatch_t **regmatch, fr_value_box_t const *in)
62{
63 fr_regcapture_t *old_rc, *new_rc; /* lldb doesn't like bare new *sigh* */
64
65 /*
66 * Clear out old_rc matches
67 */
68 old_rc = request_data_get(request, request, REQUEST_DATA_REGEX);
69 if (old_rc) {
70 DEBUG4("Clearing %zu matches", old_rc->regmatch->used);
71 talloc_free(old_rc);
72 } else {
73 DEBUG4("No matches");
74 }
75
76 if (!regmatch || ((*regmatch)->used == 0)) return;
77
78 fr_assert(preg && *preg);
79 fr_assert(regmatch);
81
82 DEBUG4("Adding %zu matches", (*regmatch)->used);
83
84 /*
85 * Container struct for all the match data
86 */
87 MEM(new_rc = talloc(request, fr_regcapture_t));
88
89 /*
90 * Steal runtime pregs, leave precompiled ones
91 */
92#if defined(HAVE_REGEX_PCRE) || defined(HAVE_REGEX_PCRE2)
93 if (!(*preg)->precompiled) {
94 new_rc->preg = talloc_steal(new_rc, *preg);
95 *preg = NULL;
96 } else {
97 new_rc->preg = *preg; /* Compiled on startup, will hopefully stick around */
98 }
99#endif
100
101 /*
102 * Steal match data
103 */
104 new_rc->regmatch = talloc_steal(new_rc, *regmatch);
105 new_rc->safe_for = in->safe_for;
106 new_rc->secret = in->secret;
107 *regmatch = NULL;
108
109 request_data_talloc_add(request, request, REQUEST_DATA_REGEX, fr_regcapture_t, new_rc, true, false, false);
110}
111
112# if defined(HAVE_REGEX_PCRE2)
113/** Extract a subcapture value from the request
114 *
115 * @note This is the PCRE variant of the function.
116 *
117 * @param[in] ctx To allocate subcapture buffer in.
118 * @param[out] out Where to write the subcapture string.
119 * @param[in] request to extract.
120 * @param[in] num Subcapture index (0 for entire match).
121 * @return
122 * - 0 on success.
123 * - -1 on notfound.
124 */
125int regex_request_to_sub(TALLOC_CTX *ctx, fr_value_box_t *out, request_t *request, uint32_t num)
126{
127 fr_regcapture_t *rc;
128 char *buff;
129 size_t len;
130 int ret;
131 pcre2_match_data *match_data;
132
133 rc = request_data_reference(request, request, REQUEST_DATA_REGEX);
134 if (!rc) {
135 RDEBUG4("No subcapture data found");
136 return -1;
137 }
138 match_data = talloc_get_type_abort(rc->regmatch->match_data, pcre2_match_data);
139
140 ret = pcre2_substring_length_bynumber(match_data, num, &len);
141 switch (ret) {
142 case PCRE2_ERROR_NOMEMORY:
143 MEM(NULL);
144 break;
145
146 /*
147 * Not finding a substring is fine
148 */
149 case PCRE2_ERROR_NOSUBSTRING:
150 RDEBUG4("%i/%zu Not found", num + 1, rc->regmatch->used);
151 return -1;
152
153 default:
154 if (ret < 0) {
155 return -1;
156 }
157
158 MEM(buff = talloc_array(ctx, char, ++len)); /* +1 for \0, it'll get reset by pcre2_substring */
159 pcre2_substring_copy_bynumber(match_data, num, (PCRE2_UCHAR *)buff, &len); /* can't error */
160
161 fr_value_box_init(out, FR_TYPE_STRING, NULL, false);
162 fr_value_box_bstrndup_shallow(out, NULL, buff, len, false);
163 fr_value_box_mark_safe_for(out, rc->safe_for);
164 fr_value_box_set_secret(out, rc->secret);
165
166 RDEBUG4("%i/%zu Found: %pV (%zu)", num + 1, rc->regmatch->used, out, out->vb_length);
167 break;
168 }
169
170 return 0;
171}
172
173/** Extract a named subcapture value from the request
174 *
175 * @note This is the PCRE variant of the function.
176 *
177 * @param[in] ctx To allocate subcapture buffer in.
178 * @param[out] out Where to write the subcapture string.
179 * @param[in] request to extract.
180 * @param[in] name of subcapture.
181 * @return
182 * - 0 on success.
183 * - -1 on notfound.
184 */
185int regex_request_to_sub_named(TALLOC_CTX *ctx, fr_value_box_t *out, request_t *request, char const *name)
186{
187 fr_regcapture_t *rc;
188 char *buff;
189 int ret;
190 size_t len;
191 pcre2_match_data *match_data;
192
193 rc = request_data_reference(request, request, REQUEST_DATA_REGEX);
194 if (!rc) {
195 RDEBUG4("No subcapture data found");
196 return -1;
197 }
198 match_data = rc->regmatch->match_data;
199
200 ret = pcre2_substring_length_byname(match_data, (PCRE2_UCHAR const *)name, &len);
201 switch (ret) {
202 case PCRE2_ERROR_NOMEMORY:
203 MEM(NULL);
204 break;
205
206 /*
207 * Not finding a substring is fine
208 */
209 case PCRE2_ERROR_NOSUBSTRING:
210 RDEBUG4("No named capture group \"%s\"", name);
211 return -1;
212
213 default:
214 if (ret < 0) return -1;
215
216 MEM(buff = talloc_array(ctx, char, ++len)); /* +1 for \0, it'll get reset by pcre2_substring */
217 pcre2_substring_copy_byname(match_data, (PCRE2_UCHAR const *)name, (PCRE2_UCHAR *)buff, &len); /* can't error */
218
219 fr_value_box_init(out, FR_TYPE_STRING, NULL, false);
220 fr_value_box_bstrndup_shallow(out, NULL, buff, len, false);
221 fr_value_box_mark_safe_for(out, rc->safe_for);
222 fr_value_box_set_secret(out, rc->secret);
223
224 RDEBUG4("Found \"%s\": %pV (%zu)", name, out, out->vb_length);
225 break;
226 }
227
228 return 0;
229}
230# elif defined(HAVE_REGEX_PCRE)
231/** Extract a subcapture value from the request
232 *
233 * @note This is the PCRE variant of the function.
234 *
235 * @param[in] ctx To allocate subcapture buffer in.
236 * @param[out] out Where to write the subcapture string.
237 * @param[in] request to extract.
238 * @param[in] num Subcapture index (0 for entire match).
239 * @return
240 * - 0 on success.
241 * - -1 on notfound.
242 */
243int regex_request_to_sub(TALLOC_CTX *ctx, fr_value_box_t *out, request_t *request, uint32_t num)
244{
245 fr_regcapture_t *rc;
246 char const *p;
247 int ret;
248
249 rc = request_data_reference(request, request, REQUEST_DATA_REGEX);
250 if (!rc) {
251 RDEBUG4("No subcapture data found");
252 return -1;
253 }
254
255 ret = pcre_get_substring(rc->regmatch->subject,
256 (int *)rc->regmatch->match_data, (int)rc->regmatch->used, num, &p);
257 switch (ret) {
258 case PCRE_ERROR_NOMEMORY:
259 MEM(NULL);
260 break;
261
262 /*
263 * Not finding a substring is fine
264 */
265 case PCRE_ERROR_NOSUBSTRING:
266 RDEBUG4("%i/%zu Not found", num + 1, rc->regmatch->used);
267 return -1;
268
269 default:
270 if (ret < 0) return -1;
271
272 talloc_set_type(p, char);
273 talloc_steal(ctx, p);
274
275 fr_value_box_init(out, FR_TYPE_STRING, NULL, false);
276 fr_value_box_bstrndup_shallow(out, NULL, p, talloc_array_length(p) - 1, false);
277 fr_value_box_mark_safe_for(out, rc->safe_for);
278 fr_value_box_set_secret(out, rc->secret);
279
280 RDEBUG4("%i/%zu Found: %pV (%zu)", num + 1, rc->regmatch->used, out, out->vb_length);
281 break;
282 }
283
284 return 0;
285}
286
287/** Extract a named subcapture value from the request
288 *
289 * @note This is the PCRE variant of the function.
290 *
291 * @param[in] ctx To allocate subcapture buffer in.
292 * @param[out] out Where to write the subcapture string.
293 * @param[in] request to extract.
294 * @param[in] name of subcapture.
295 * @return
296 * - 0 on success.
297 * - -1 on notfound.
298 */
299int regex_request_to_sub_named(TALLOC_CTX *ctx, fr_value_box_t *out, request_t *request, char const *name)
300{
301 fr_regcapture_t *rc;
302 void *rd;
303 char const *p;
304 int ret;
305
306 rd = request_data_reference(request, request, REQUEST_DATA_REGEX);
307 if (!rd) {
308 RDEBUG4("No subcapture data found");
309 return -1;
310 }
311
312 rc = talloc_get_type_abort(rd, fr_regcapture_t);
313 ret = pcre_get_named_substring(rc->preg->compiled, rc->regmatch->subject,
314 (int *)rc->regmatch->match_data, (int)rc->regmatch->used, name, &p);
315 switch (ret) {
316 case PCRE_ERROR_NOMEMORY:
317 MEM(NULL);
318 break;
319
320 /*
321 * Not finding a substring is fine
322 */
323 case PCRE_ERROR_NOSUBSTRING:
324 RDEBUG4("No named capture group \"%s\"", name);
325 return -1;
326
327 default:
328 if (ret < 0) return -1;
329
330 talloc_set_type(p, char);
331 talloc_steal(ctx, p);
332
333 fr_value_box_init(out, FR_TYPE_STRING, NULL, false);
334 fr_value_box_bstrndup_shallow(out, NULL, p, talloc_array_length(p) - 1, false);
335 fr_value_box_mark_safe_for(out, rc->safe_for);
336 fr_value_box_set_secret(out, rc->secret);
337
338 RDEBUG4("Found \"%s\": %pV (%zu)", name, out, out->vb_length);
339
340 break;
341 }
342
343 return 0;
344}
345# else
346/** Extract a subcapture value from the request
347 *
348 * @note This is the POSIX variant of the function.
349 *
350 * @param[in] ctx To allocate subcapture buffer in.
351 * @param[out] out Where to write the subcapture string.
352 * @param[in] request to extract.
353 * @param[in] num Subcapture index (0 for entire match).
354 * @return
355 * - 0 on success.
356 * - -1 on notfound.
357 */
358int regex_request_to_sub(TALLOC_CTX *ctx, fr_value_box_t *out, request_t *request, uint32_t num)
359{
360 fr_regcapture_t *rc;
361 char *buff;
362 char const *start;
363 size_t len;
364 regmatch_t *match_data;
365
366 rc = request_data_reference(request, request, REQUEST_DATA_REGEX);
367 if (!rc) {
368 RDEBUG4("No subcapture data found");
369 return -1;
370 }
371 match_data = rc->regmatch->match_data;
372
373 /*
374 * Greater than our capture array
375 *
376 * -1 means no value in this capture group.
377 */
378 if ((num >= rc->regmatch->used) || (match_data[num].rm_eo == -1) || (match_data[num].rm_so == -1)) {
379 RDEBUG4("%i/%zu Not found", num + 1, rc->regmatch->used);
380 return -1;
381 }
382
383 /*
384 * Sanity checks on the offsets
385 */
386 fr_assert(match_data[num].rm_eo <= (regoff_t)talloc_array_length(rc->regmatch->subject));
387 fr_assert(match_data[num].rm_so <= (regoff_t)talloc_array_length(rc->regmatch->subject));
388
389 start = rc->regmatch->subject + match_data[num].rm_so;
390 len = match_data[num].rm_eo - match_data[num].rm_so;
391
392 MEM(buff = talloc_bstrndup(ctx, start, len));
393
394 fr_value_box_init(out, FR_TYPE_STRING, NULL, false);
395 fr_value_box_bstrndup_shallow(out, NULL, buff, len, false);
396 fr_value_box_mark_safe_for(out, rc->safe_for);
397 fr_value_box_set_secret(out, rc->secret);
398
399 RDEBUG4("%i/%zu Found: %pV (%zu)", num + 1, rc->regmatch->used, out, out->vb_length);
400
401 return 0;
402}
403# endif
404#endif
#define RCSID(id)
Definition build.h:485
#define MEM(x)
Definition debug.h:36
static fr_slen_t in
Definition dict.h:831
#define DEBUG4(_fmt,...)
Definition log.h:267
#define RDEBUG4(fmt,...)
Definition log.h:344
talloc_free(reap)
@ FR_TYPE_STRING
String of printable characters.
unsigned int uint32_t
#define fr_assert(_expr)
Definition rad_assert.h:38
static char * secret
void * request_data_reference(request_t *request, void const *unique_ptr, int unique_int)
Get opaque data from a request without removing it.
void * request_data_get(request_t *request, void const *unique_ptr, int unique_int)
Get opaque data from a request.
#define request_data_talloc_add(_request, _unique_ptr, _unique_int, _type, _opaque, _free_on_replace, _free_on_parent, _persist)
Add opaque data to a request_t.
static char const * name
static char buff[sizeof("18446744073709551615")+3]
Definition size_tests.c:41
char * talloc_bstrndup(TALLOC_CTX *ctx, char const *in, size_t inlen)
Binary safe strndup function.
Definition talloc.c:564
void fr_value_box_bstrndup_shallow(fr_value_box_t *dst, fr_dict_attr_t const *enumv, char const *src, size_t len, bool tainted)
Assign a string to to a fr_value_box_t.
Definition value.c:4262
#define fr_value_box_mark_safe_for(_box, _safe_for)
Definition value.h:1063
static void fr_value_box_set_secret(fr_value_box_t *box, bool secret)
Definition value.h:1100
uintptr_t fr_value_box_safe_for_t
Escaping that's been applied to a value box.
Definition value.h:155
#define fr_value_box_init(_vb, _type, _enumv, _tainted)
Initialise a fr_value_box_t.
Definition value.h:598
static size_t char ** out
Definition value.h:1012