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: 1fea13a1577208380035032ea8f74338ab376ca3 $
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: 1fea13a1577208380035032ea8f74338ab376ca3 $")
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} fr_regcapture_t;
42
43/** Adds subcapture values to request data
44 *
45 * Allows use of %{n} expansions.
46 *
47 * @note If preg was runtime-compiled, it will be consumed and *preg will be set to NULL.
48 * @note regmatch will be consumed and *regmatch will be set to NULL.
49 * @note Their lifetimes will be bound to the match request data.
50 *
51 * @param[in] request Current request.
52 * @param[in,out] preg Compiled pattern. May be set to NULL if
53 * reparented to the regcapture struct.
54 * @param[in,out] regmatch Pointers into value. May be set to NULL if
55 * reparented to the regcapture struct.
56 */
57void regex_sub_to_request(request_t *request, regex_t **preg, fr_regmatch_t **regmatch)
58{
59 fr_regcapture_t *old_rc, *new_rc; /* lldb doesn't like bare new *sigh* */
60
61 /*
62 * Clear out old_rc matches
63 */
64 old_rc = request_data_get(request, request, REQUEST_DATA_REGEX);
65 if (old_rc) {
66 DEBUG4("Clearing %zu matches", old_rc->regmatch->used);
67 talloc_free(old_rc);
68 } else {
69 DEBUG4("No matches");
70 }
71
72 if (!regmatch || ((*regmatch)->used == 0)) return;
73
74 fr_assert(preg && *preg);
75 fr_assert(regmatch);
76
77 DEBUG4("Adding %zu matches", (*regmatch)->used);
78
79 /*
80 * Container struct for all the match data
81 */
82 MEM(new_rc = talloc(request, fr_regcapture_t));
83
84 /*
85 * Steal runtime pregs, leave precompiled ones
86 */
87#if defined(HAVE_REGEX_PCRE) || defined(HAVE_REGEX_PCRE2)
88 if (!(*preg)->precompiled) {
89 new_rc->preg = talloc_steal(new_rc, *preg);
90 *preg = NULL;
91 } else {
92 new_rc->preg = *preg; /* Compiled on startup, will hopefully stick around */
93 }
94#endif
95
96 /*
97 * Steal match data
98 */
99 new_rc->regmatch = talloc_steal(new_rc, *regmatch);
100 *regmatch = NULL;
101
102 request_data_talloc_add(request, request, REQUEST_DATA_REGEX, fr_regcapture_t, new_rc, true, false, false);
103}
104
105# if defined(HAVE_REGEX_PCRE2)
106/** Extract a subcapture value from the request
107 *
108 * @note This is the PCRE variant of the function.
109 *
110 * @param[in] ctx To allocate subcapture buffer in.
111 * @param[out] out Where to write the subcapture string.
112 * @param[in] request to extract.
113 * @param[in] num Subcapture index (0 for entire match).
114 * @return
115 * - 0 on success.
116 * - -1 on notfound.
117 */
118int regex_request_to_sub(TALLOC_CTX *ctx, char **out, request_t *request, uint32_t num)
119{
120 fr_regcapture_t *rc;
121 char *buff;
122 size_t len;
123 int ret;
124 pcre2_match_data *match_data;
125
126 rc = request_data_reference(request, request, REQUEST_DATA_REGEX);
127 if (!rc) {
128 RDEBUG4("No subcapture data found");
129 *out = NULL;
130 return -1;
131 }
132 match_data = talloc_get_type_abort(rc->regmatch->match_data, pcre2_match_data);
133
134 ret = pcre2_substring_length_bynumber(match_data, num, &len);
135 switch (ret) {
136 case PCRE2_ERROR_NOMEMORY:
137 MEM(NULL);
138 break;
139
140 /*
141 * Not finding a substring is fine
142 */
143 case PCRE2_ERROR_NOSUBSTRING:
144 RDEBUG4("%i/%zu Not found", num + 1, rc->regmatch->used);
145 *out = NULL;
146 return -1;
147
148 default:
149 if (ret < 0) {
150 *out = NULL;
151 return -1;
152 }
153
154 MEM(buff = talloc_array(ctx, char, ++len)); /* +1 for \0, it'll get reset by pcre2_substring */
155 pcre2_substring_copy_bynumber(match_data, num, (PCRE2_UCHAR *)buff, &len); /* can't error */
156
157 RDEBUG4("%i/%zu Found: %pV (%zu)", num + 1, rc->regmatch->used,
158 fr_box_strvalue_buffer(buff), talloc_array_length(buff) - 1);
159
160 *out = buff;
161 break;
162 }
163
164 return 0;
165}
166
167/** Extract a named subcapture value from the request
168 *
169 * @note This is the PCRE variant of the function.
170 *
171 * @param[in] ctx To allocate subcapture buffer in.
172 * @param[out] out Where to write the subcapture string.
173 * @param[in] request to extract.
174 * @param[in] name of subcapture.
175 * @return
176 * - 0 on success.
177 * - -1 on notfound.
178 */
179int regex_request_to_sub_named(TALLOC_CTX *ctx, char **out, request_t *request, char const *name)
180{
181 fr_regcapture_t *rc;
182 char *buff;
183 int ret;
184 size_t len;
185 pcre2_match_data *match_data;
186
187 rc = request_data_reference(request, request, REQUEST_DATA_REGEX);
188 if (!rc) {
189 RDEBUG4("No subcapture data found");
190 *out = NULL;
191 return -1;
192 }
193 match_data = rc->regmatch->match_data;
194
195 ret = pcre2_substring_length_byname(match_data, (PCRE2_UCHAR const *)name, &len);
196 switch (ret) {
197 case PCRE2_ERROR_NOMEMORY:
198 MEM(NULL);
199 break;
200
201 /*
202 * Not finding a substring is fine
203 */
204 case PCRE2_ERROR_NOSUBSTRING:
205 RDEBUG4("No named capture group \"%s\"", name);
206 *out = NULL;
207 return -1;
208
209 default:
210 if (ret < 0) {
211 *out = NULL;
212 return -1;
213 }
214
215 MEM(buff = talloc_array(ctx, char, ++len)); /* +1 for \0, it'll get reset by pcre2_substring */
216 pcre2_substring_copy_byname(match_data, (PCRE2_UCHAR const *)name, (PCRE2_UCHAR *)buff, &len); /* can't error */
217
218 RDEBUG4("Found \"%s\": %pV (%zu)", name,
219 fr_box_strvalue_buffer(buff), talloc_array_length(buff) - 1);
220
221 *out = buff;
222 break;
223 }
224
225 return 0;
226}
227# elif defined(HAVE_REGEX_PCRE)
228/** Extract a subcapture value from the request
229 *
230 * @note This is the PCRE variant of the function.
231 *
232 * @param[in] ctx To allocate subcapture buffer in.
233 * @param[out] out Where to write the subcapture string.
234 * @param[in] request to extract.
235 * @param[in] num Subcapture index (0 for entire match).
236 * @return
237 * - 0 on success.
238 * - -1 on notfound.
239 */
240int regex_request_to_sub(TALLOC_CTX *ctx, char **out, request_t *request, uint32_t num)
241{
242 fr_regcapture_t *rc;
243 char const *p;
244 int ret;
245
246 rc = request_data_reference(request, request, REQUEST_DATA_REGEX);
247 if (!rc) {
248 RDEBUG4("No subcapture data found");
249 *out = NULL;
250 return -1;
251 }
252
253 ret = pcre_get_substring(rc->regmatch->subject,
254 (int *)rc->regmatch->match_data, (int)rc->regmatch->used, num, &p);
255 switch (ret) {
256 case PCRE_ERROR_NOMEMORY:
257 MEM(NULL);
258 break;
259
260 /*
261 * Not finding a substring is fine
262 */
263 case PCRE_ERROR_NOSUBSTRING:
264 RDEBUG4("%i/%zu Not found", num + 1, rc->regmatch->used);
265 *out = NULL;
266 return -1;
267
268 default:
269 if (ret < 0) {
270 *out = NULL;
271 return -1;
272 }
273
274 talloc_set_type(p, char);
275 p = talloc_steal(ctx, p);
276
277 RDEBUG4("%i/%zu Found: %pV (%zu)", num + 1, rc->regmatch->used,
278 fr_box_strvalue_buffer(p), talloc_array_length(p) - 1);
279
280 memcpy(out, &p, sizeof(*out));
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, char **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 *out = NULL;
310 return -1;
311 }
312
313 rc = talloc_get_type_abort(rd, fr_regcapture_t);
314 ret = pcre_get_named_substring(rc->preg->compiled, rc->regmatch->subject,
315 (int *)rc->regmatch->match_data, (int)rc->regmatch->used, name, &p);
316 switch (ret) {
317 case PCRE_ERROR_NOMEMORY:
318 MEM(NULL);
319 break;
320
321 /*
322 * Not finding a substring is fine
323 */
324 case PCRE_ERROR_NOSUBSTRING:
325 RDEBUG4("No named capture group \"%s\"", name);
326 *out = NULL;
327 return -1;
328
329 default:
330 if (ret < 0) {
331 *out = NULL;
332 return -1;
333 }
334
335 /*
336 * Check libpcre really is using our overloaded
337 * memory allocation and freeing talloc wrappers.
338 */
339 talloc_set_type(p, char);
340 talloc_steal(ctx, p);
341
342 RDEBUG4("Found \"%s\": %pV (%zu)", name,
343 fr_box_strvalue_buffer(p), talloc_array_length(p) - 1);
344
345 memcpy(out, &p, sizeof(*out));
346
347 break;
348 }
349
350 return 0;
351}
352# else
353/** Extract a subcapture value from the request
354 *
355 * @note This is the POSIX variant of the function.
356 *
357 * @param[in] ctx To allocate subcapture buffer in.
358 * @param[out] out Where to write the subcapture string.
359 * @param[in] request to extract.
360 * @param[in] num Subcapture index (0 for entire match).
361 * @return
362 * - 0 on success.
363 * - -1 on notfound.
364 */
365int regex_request_to_sub(TALLOC_CTX *ctx, char **out, request_t *request, uint32_t num)
366{
367 fr_regcapture_t *rc;
368 char *buff;
369 char const *start;
370 size_t len;
371 regmatch_t *match_data;
372
373 rc = request_data_reference(request, request, REQUEST_DATA_REGEX);
374 if (!rc) {
375 RDEBUG4("No subcapture data found");
376 *out = NULL;
377 return -1;
378 }
379 match_data = rc->regmatch->match_data;
380
381 /*
382 * Greater than our capture array
383 *
384 * -1 means no value in this capture group.
385 */
386 if ((num >= rc->regmatch->used) || (match_data[num].rm_eo == -1) || (match_data[num].rm_so == -1)) {
387 RDEBUG4("%i/%zu Not found", num + 1, rc->regmatch->used);
388 *out = NULL;
389 return -1;
390 }
391
392 /*
393 * Sanity checks on the offsets
394 */
395 fr_assert(match_data[num].rm_eo <= (regoff_t)talloc_array_length(rc->regmatch->subject));
396 fr_assert(match_data[num].rm_so <= (regoff_t)talloc_array_length(rc->regmatch->subject));
397
398 start = rc->regmatch->subject + match_data[num].rm_so;
399 len = match_data[num].rm_eo - match_data[num].rm_so;
400
401 MEM(buff = talloc_bstrndup(ctx, start, len));
402 RDEBUG4("%i/%zu Found: %pV (%zu)", num + 1, rc->regmatch->used, fr_box_strvalue_buffer(buff), len);
403
404 *out = buff;
405
406 return 0;
407}
408# endif
409#endif
#define RCSID(id)
Definition build.h:483
#define MEM(x)
Definition debug.h:36
#define DEBUG4(_fmt,...)
Definition log.h:267
#define RDEBUG4(fmt,...)
Definition log.h:344
talloc_free(reap)
unsigned int uint32_t
#define fr_assert(_expr)
Definition rad_assert.h:38
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
#define fr_box_strvalue_buffer(_val)
Definition value.h:289
static size_t char ** out
Definition value.h:997