The FreeRADIUS server  $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
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 
26 RCSID("$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 
36 typedef 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  */
57 void 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  */
118 int 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  */
179 int 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  */
240 int 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  */
299 int 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  */
365 int 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:444
#define DEBUG4(_fmt,...)
Definition: log.h:267
#define RDEBUG4(fmt,...)
Definition: log.h:344
talloc_free(reap)
unsigned int uint32_t
Definition: merged_model.c:33
void * request_data_get(request_t *request, void const *unique_ptr, int unique_int)
Get opaque data from a request.
Definition: request_data.c:292
void * request_data_reference(request_t *request, void const *unique_ptr, int unique_int)
Get opaque data from a request without removing it.
Definition: request_data.c:339
#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.
Definition: request_data.h:86
static char const * name
static char buff[sizeof("18446744073709551615")+3]
Definition: size_tests.c:41
fr_assert(0)
MEM(pair_append_request(&vp, attr_eap_aka_sim_identity) >=0)
char * talloc_bstrndup(TALLOC_CTX *ctx, char const *in, size_t inlen)
Binary safe strndup function.
Definition: talloc.c:452
#define fr_box_strvalue_buffer(_val)
Definition: value.h:282
static size_t char ** out
Definition: value.h:984