All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
evaluate.c
Go to the documentation of this file.
1 /*
2  * evaluate.c Evaluate complex conditions
3  *
4  * Version: $Id: 5720955d826a3683aaf722c79b492c120bf71ba6 $
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  *
20  * Copyright 2007 The FreeRADIUS server project
21  * Copyright 2007 Alan DeKok <aland@deployingradius.com>
22  */
23 
24 RCSID("$Id: 5720955d826a3683aaf722c79b492c120bf71ba6 $")
25 
26 #include <freeradius-devel/radiusd.h>
27 #include <freeradius-devel/modules.h>
28 #include <freeradius-devel/parser.h>
29 #include <freeradius-devel/rad_assert.h>
30 
31 #include <ctype.h>
32 
33 #ifdef WITH_UNLANG
34 #ifdef WITH_EVAL_DEBUG
35 # define EVAL_DEBUG(fmt, ...) printf("EVAL: ");printf(fmt, ## __VA_ARGS__);printf("\n");fflush(stdout)
36 #else
37 # define EVAL_DEBUG(...)
38 #endif
39 
41  { "reject", RLM_MODULE_REJECT },
42  { "fail", RLM_MODULE_FAIL },
43  { "ok", RLM_MODULE_OK },
44  { "handled", RLM_MODULE_HANDLED },
45  { "invalid", RLM_MODULE_INVALID },
46  { "userlock", RLM_MODULE_USERLOCK },
47  { "notfound", RLM_MODULE_NOTFOUND },
48  { "noop", RLM_MODULE_NOOP },
49  { "updated", RLM_MODULE_UPDATED },
50  { NULL, 0 }
51 };
52 
53 
54 static bool all_digits(char const *string)
55 {
56  char const *p = string;
57 
58  rad_assert(p != NULL);
59 
60  if (*p == '\0') return false;
61 
62  if (*p == '-') p++;
63 
64  while (isdigit((int) *p)) p++;
65 
66  return (*p == '\0');
67 }
68 
69 /** Evaluate a template
70  *
71  * Converts a vp_tmpl_t to a boolean value.
72  *
73  * @param[in] request the REQUEST
74  * @param[in] modreturn the previous module return code
75  * @param[in] depth of the recursion (only used for debugging)
76  * @param[in] vpt the template to evaluate
77  * @return
78  * - -1 on failure.
79  * - 0 for "no match".
80  * - 1 for "match".
81  */
82 int radius_evaluate_tmpl(REQUEST *request, int modreturn, UNUSED int depth, vp_tmpl_t const *vpt)
83 {
84  int rcode;
85  int modcode;
87 
88  switch (vpt->type) {
89  case TMPL_TYPE_UNPARSED:
90  modcode = fr_str2int(modreturn_table, vpt->name, RLM_MODULE_UNKNOWN);
91  if (modcode != RLM_MODULE_UNKNOWN) {
92  rcode = (modcode == modreturn);
93  break;
94  }
95 
96  /*
97  * Else it's a literal string. Empty string is
98  * false, non-empty string is true.
99  *
100  * @todo: Maybe also check for digits?
101  *
102  * The VPT *doesn't* have a "bare word" type,
103  * which arguably it should.
104  */
105  rcode = (vpt->name != '\0');
106  break;
107 
108  case TMPL_TYPE_ATTR:
109  case TMPL_TYPE_LIST:
110  if (tmpl_find_vp(NULL, request, vpt) == 0) {
111  rcode = true;
112  } else {
113  rcode = false;
114  }
115  break;
116 
118  case TMPL_TYPE_XLAT:
119  case TMPL_TYPE_EXEC:
120  {
121  char *p;
122 
123  if (!*vpt->name) return false;
124  rcode = tmpl_aexpand(request, &p, request, vpt, NULL, NULL);
125  if (rcode < 0) {
126  EVAL_DEBUG("FAIL %d", __LINE__);
127  return -1;
128  }
129  data.strvalue = p;
130  rcode = (data.strvalue && (*data.strvalue != '\0'));
131  talloc_free(data.ptr);
132  }
133  break;
134 
135  /*
136  * Can't have a bare ... (/foo/) ...
137  */
138  case TMPL_TYPE_REGEX:
140  rad_assert(0 == 1);
141  /* FALL-THROUGH */
142 
143  default:
144  EVAL_DEBUG("FAIL %d", __LINE__);
145  rcode = -1;
146  break;
147  }
148 
149  return rcode;
150 }
151 
152 #ifdef HAVE_REGEX
153 /** Perform a regular expressions comparison between two operands
154  *
155  * @return
156  * - -1 on failure.
157  * - 0 for "no match".
158  * - 1 for "match".
159  */
160 static int cond_do_regex(REQUEST *request, fr_cond_t const *c,
161  PW_TYPE lhs_type, value_data_t const *lhs,
162  PW_TYPE rhs_type, value_data_t const *rhs)
163 {
164  vp_map_t const *map = c->data.map;
165 
166  ssize_t slen;
167  int ret;
168 
169  regex_t *preg, *rreg = NULL;
170  regmatch_t rxmatch[REQUEST_MAX_REGEX + 1]; /* +1 for %{0} (whole match) capture group */
171  size_t nmatch = sizeof(rxmatch) / sizeof(regmatch_t);
172 
173  rad_assert(lhs_type == PW_TYPE_STRING);
174  rad_assert(lhs != NULL);
175 
176  EVAL_DEBUG("CMP WITH REGEX %s %s",
177  map->rhs->tmpl_iflag ? "CASE INSENSITIVE" : "CASE SENSITIVE",
178  map->rhs->tmpl_mflag ? "MULTILINE" : "SINGLELINE");
179 
180  switch (map->rhs->type) {
181  case TMPL_TYPE_REGEX_STRUCT: /* pre-compiled to a regex */
182  preg = map->rhs->tmpl_preg;
183  break;
184 
185  default:
186  rad_assert(rhs_type == PW_TYPE_STRING);
187  rad_assert(rhs->strvalue);
188  slen = regex_compile(request, &rreg, rhs->strvalue, rhs->length,
189  map->rhs->tmpl_iflag, map->rhs->tmpl_mflag, true, true);
190  if (slen <= 0) {
191  REMARKER(rhs->strvalue, -slen, fr_strerror());
192  EVAL_DEBUG("FAIL %d", __LINE__);
193 
194  return -1;
195  }
196  preg = rreg;
197  break;
198  }
199 
200  ret = regex_exec(preg, lhs->strvalue, lhs->length, rxmatch, &nmatch);
201  switch (ret) {
202  case 0:
203  EVAL_DEBUG("CLEARING SUBCAPTURES");
204  regex_sub_to_request(request, NULL, NULL, 0, NULL, 0); /* clear out old entries */
205  break;
206 
207  case 1:
208  EVAL_DEBUG("SETTING SUBCAPTURES");
209  regex_sub_to_request(request, &preg, lhs->strvalue, lhs->length, rxmatch, nmatch);
210  break;
211 
212  case -1:
213  EVAL_DEBUG("REGEX ERROR");
214  REDEBUG("regex failed: %s", fr_strerror());
215  break;
216 
217  default:
218  break;
219  }
220 
221  if (preg) talloc_free(rreg);
222 
223  return ret;
224 }
225 #endif
226 
227 #ifdef WITH_EVAL_DEBUG
228 static void cond_print_operands(REQUEST *request,
229  PW_TYPE lhs_type, value_data_t const *lhs,
230  PW_TYPE rhs_type, value_data_t const *rhs)
231 {
232  if (lhs) {
233  if (lhs_type == PW_TYPE_STRING) {
234  EVAL_DEBUG("LHS: \"%s\" (%zu)" , lhs->strvalue, lhs->length);
235  } else {
236  char *lhs_hex;
237 
238  lhs_hex = talloc_array(request, char, (lhs->length * 2) + 1);
239 
240  if (lhs_type == PW_TYPE_OCTETS) {
241  fr_bin2hex(lhs_hex, lhs->octets, lhs->length);
242  } else {
243  fr_bin2hex(lhs_hex, (uint8_t const *)lhs, lhs->length);
244  }
245 
246  EVAL_DEBUG("LHS: 0x%s (%zu)", lhs_hex, lhs->length);
247 
248  talloc_free(lhs_hex);
249  }
250  } else {
251  EVAL_DEBUG("LHS: VIRTUAL");
252  }
253 
254  if (rhs) {
255  if (rhs_type == PW_TYPE_STRING) {
256  EVAL_DEBUG("RHS: \"%s\" (%zu)" , rhs->strvalue, rhs->length);
257  } else {
258  char *rhs_hex;
259 
260  rhs_hex = talloc_array(request, char, (rhs->length * 2) + 1);
261 
262  if (rhs_type == PW_TYPE_OCTETS) {
263  fr_bin2hex(rhs_hex, rhs->octets, rhs->length);
264  } else {
265  fr_bin2hex(rhs_hex, (uint8_t const *)rhs, rhs->length);
266  }
267 
268  EVAL_DEBUG("RHS: 0x%s (%zu)", rhs_hex, rhs->length);
269 
270  talloc_free(rhs_hex);
271  }
272  } else {
273  EVAL_DEBUG("RHS: COMPILED");
274  }
275 }
276 #endif
277 
278 /** Call the correct data comparison function for the condition
279  *
280  * Deals with regular expression comparisons, virtual attribute
281  * comparisons, and data comparisons.
282  *
283  * @return
284  * - -1 on failure.
285  * - 0 for "no match".
286  * - 1 for "match".
287  */
288 static int cond_cmp_values(REQUEST *request, fr_cond_t const *c,
289  PW_TYPE lhs_type, value_data_t const *lhs,
290  PW_TYPE rhs_type, value_data_t const *rhs)
291 {
292  vp_map_t const *map = c->data.map;
293  int rcode;
294 
295 #ifdef WITH_EVAL_DEBUG
296  EVAL_DEBUG("CMP OPERANDS");
297  cond_print_operands(request, lhs_type, lhs, rhs_type, rhs);
298 #endif
299 
300 #ifdef HAVE_REGEX
301  /*
302  * Regex comparison
303  */
304  if (map->op == T_OP_REG_EQ) {
305  rcode = cond_do_regex(request, c, lhs_type, lhs, rhs_type, rhs);
306  goto finish;
307  }
308 #endif
309  /*
310  * Virtual attribute comparison.
311  */
312  if (c->pass2_fixup == PASS2_PAIRCOMPARE) {
313  VALUE_PAIR *vp;
314 
315  EVAL_DEBUG("CMP WITH PAIRCOMPARE");
316  rad_assert(map->lhs->type == TMPL_TYPE_ATTR);
317 
318  vp = fr_pair_afrom_da(request, map->lhs->tmpl_da);
319  vp->op = c->data.map->op;
320 
321  value_data_copy(vp, &vp->data, rhs_type, rhs);
322 
323  rcode = paircompare(request, request->packet->vps, vp, NULL);
324  rcode = (rcode == 0) ? 1 : 0;
325  talloc_free(vp);
326  goto finish;
327  }
328 
329  /*
330  * At this point both operands should have been normalised
331  * to the same type, and there's no special comparisons
332  * left.
333  */
334  rad_assert(lhs_type == rhs_type);
335 
336  EVAL_DEBUG("CMP WITH VALUE DATA");
337  rcode = value_data_cmp_op(map->op, lhs_type, lhs, rhs_type, rhs);
338 finish:
339  switch (rcode) {
340  case 0:
341  EVAL_DEBUG("FALSE");
342  break;
343 
344  case 1:
345  EVAL_DEBUG("TRUE");
346  break;
347 
348  default:
349  EVAL_DEBUG("ERROR %i", rcode);
350  break;
351  }
352 
353  return rcode;
354 }
355 
356 
357 
358 /** Convert both operands to the same type
359  *
360  * If casting is successful, we call cond_cmp_values to do the comparison
361  *
362  * @return
363  * - -1 on failure.
364  * - 0 for "no match".
365  * - 1 for "match".
366  */
367 static int cond_normalise_and_cmp(REQUEST *request, fr_cond_t const *c,
368  PW_TYPE lhs_type, fr_dict_attr_t const *lhs_enumv,
369  value_data_t const *lhs)
370 {
371  vp_map_t const *map = c->data.map;
372 
373  fr_dict_attr_t const *cast = NULL;
374  PW_TYPE cast_type = PW_TYPE_INVALID;
375 
376  int rcode;
377 
378  PW_TYPE rhs_type = PW_TYPE_INVALID;
379  fr_dict_attr_t const *rhs_enumv = NULL;
380  value_data_t *rhs = NULL;
381 
382  value_data_t lhs_cast, rhs_cast;
383  void *lhs_cast_buff = NULL, *rhs_cast_buff = NULL;
384 
385  /*
386  * Cast operand to correct type.
387  *
388  * With hack for strings that look like integers, to cast them
389  * to 64 bit unsigned integers.
390  *
391  * @fixme For things like this it'd be useful to have a 64bit signed type.
392  */
393 #define CAST(_s) \
394 do {\
395  if ((cast_type != PW_TYPE_INVALID) && (_s ## _type != PW_TYPE_INVALID) && (cast_type != _s ## _type)) {\
396  EVAL_DEBUG("CASTING " #_s " FROM %s TO %s",\
397  fr_int2str(dict_attr_types, _s ## _type, "<INVALID>"),\
398  fr_int2str(dict_attr_types, cast_type, "<INVALID>"));\
399  if (value_data_cast(request, &_s ## _cast, cast_type, cast, _s ## _type, _s ## _enumv, _s) < 0) {\
400  REDEBUG("Failed casting " #_s " operand: %s", fr_strerror());\
401  rcode = -1;\
402  goto finish;\
403  }\
404  if (cast && cast->flags.is_pointer) _s ## _cast_buff = _s ## _cast.ptr;\
405  _s ## _type = cast_type;\
406  _s = &_s ## _cast;\
407  }\
408 } while (0)
409 
410 #define CHECK_INT_CAST(_l, _r) \
411 do {\
412  if ((cast_type == PW_TYPE_INVALID) &&\
413  _l && (_l ## _type == PW_TYPE_STRING) &&\
414  _r && (_r ## _type == PW_TYPE_STRING) &&\
415  all_digits(lhs->strvalue) && all_digits(rhs->strvalue)) {\
416  cast_type = PW_TYPE_INTEGER64;\
417  EVAL_DEBUG("OPERANDS ARE NUMBER STRINGS, SETTING CAST TO integer64");\
418  }\
419 } while (0)
420 
421  /*
422  * Regular expressions need both operands to be strings
423  */
424 #ifdef HAVE_REGEX
425  if (map->op == T_OP_REG_EQ) cast_type = PW_TYPE_STRING;
426  else
427 #endif
428  /*
429  * If it's a pair comparison, data gets cast to the
430  * type of the pair comparison attribute.
431  *
432  * Magic attribute is always the LHS.
433  */
434  if (c->pass2_fixup == PASS2_PAIRCOMPARE) {
435  rad_assert(!c->cast);
436  rad_assert(map->lhs->type == TMPL_TYPE_ATTR);
437 #ifndef NDEBUG
438  /* expensive assert */
439  rad_assert((map->rhs->type != TMPL_TYPE_ATTR) || !radius_find_compare(map->rhs->tmpl_da));
440 #endif
441  cast = map->lhs->tmpl_da;
442  cast_type = cast->type;
443 
444  EVAL_DEBUG("NORMALISATION TYPE %s (PAIRCMP TYPE)",
445  fr_int2str(dict_attr_types, cast->type, "<INVALID>"));
446  /*
447  * Otherwise we use the explicit cast, or implicit
448  * cast (from an attribute reference).
449  * We already have the data for the lhs, so we convert
450  * it here.
451  */
452  } else if (c->cast) {
453  cast = c->cast;
454  EVAL_DEBUG("NORMALISATION TYPE %s (EXPLICIT CAST)",
455  fr_int2str(dict_attr_types, cast->type, "<INVALID>"));
456  } else if (map->lhs->type == TMPL_TYPE_ATTR) {
457  cast = map->lhs->tmpl_da;
458  EVAL_DEBUG("NORMALISATION TYPE %s (IMPLICIT FROM LHS REF)",
459  fr_int2str(dict_attr_types, cast->type, "<INVALID>"));
460  } else if (map->rhs->type == TMPL_TYPE_ATTR) {
461  cast = map->rhs->tmpl_da;
462  EVAL_DEBUG("NORMALISATION TYPE %s (IMPLICIT FROM RHS REF)",
463  fr_int2str(dict_attr_types, cast->type, "<INVALID>"));
464  } else if (map->lhs->type == TMPL_TYPE_DATA) {
465  cast_type = map->lhs->tmpl_data_type;
466  EVAL_DEBUG("NORMALISATION TYPE %s (IMPLICIT FROM LHS DATA)",
467  fr_int2str(dict_attr_types, cast_type, "<INVALID>"));
468  } else if (map->rhs->type == TMPL_TYPE_DATA) {
469  cast_type = map->rhs->tmpl_data_type;
470  EVAL_DEBUG("NORMALISATION TYPE %s (IMPLICIT FROM RHS DATA)",
471  fr_int2str(dict_attr_types, cast_type, "<INVALID>"));
472  }
473 
474  if (cast) cast_type = cast->type;
475 
476  switch (map->rhs->type) {
477  case TMPL_TYPE_ATTR:
478  {
479  VALUE_PAIR *vp;
480  vp_cursor_t cursor;
481 
482  for (vp = tmpl_cursor_init(&rcode, &cursor, request, map->rhs);
483  vp;
484  vp = tmpl_cursor_next(&cursor, map->rhs)) {
485  rhs_type = vp->da->type;
486  rhs_enumv = vp->da;
487  rhs = &vp->data;
488 
489  CHECK_INT_CAST(lhs, rhs);
490  CAST(lhs);
491  CAST(rhs);
492 
493  rcode = cond_cmp_values(request, c, lhs_type, lhs, rhs_type, rhs);
494  if (rcode != 0) break;
495 
496  TALLOC_FREE(rhs_cast_buff);
497  }
498  }
499  break;
500 
501  case TMPL_TYPE_DATA:
502  rhs_type = map->rhs->tmpl_data_type;
503  rhs = &map->rhs->tmpl_data_value;
504 
505  CHECK_INT_CAST(lhs, rhs);
506  CAST(lhs);
507  CAST(rhs);
508 
509  rcode = cond_cmp_values(request, c, lhs_type, lhs, rhs_type, rhs);
510  break;
511 
512  /*
513  * Expanded types start as strings, then get converted
514  * to the type of the attribute or the explicit cast.
515  */
516  case TMPL_TYPE_UNPARSED:
517  case TMPL_TYPE_EXEC:
518  case TMPL_TYPE_XLAT:
520  {
521  ssize_t ret;
523 
524  if (map->rhs->type != TMPL_TYPE_UNPARSED) {
525  char *p;
526 
527  ret = tmpl_aexpand(request, &p, request, map->rhs, NULL, NULL);
528  if (ret < 0) {
529  EVAL_DEBUG("FAIL [%i]", __LINE__);
530  rcode = -1;
531  goto finish;
532  }
533  data.strvalue = p;
534  data.length = ret;
535 
536  } else {
537  data.strvalue = map->rhs->name;
538  data.length = map->rhs->len;
539  }
540  rad_assert(data.strvalue);
541 
542  rhs_type = PW_TYPE_STRING;
543  rhs = &data;
544 
545  CHECK_INT_CAST(lhs, rhs);
546  CAST(lhs);
547  CAST(rhs);
548 
549  rcode = cond_cmp_values(request, c, lhs_type, lhs, rhs_type, rhs);
550  if (map->rhs->type != TMPL_TYPE_UNPARSED)talloc_free(data.ptr);
551 
552  break;
553  }
554 
555  /*
556  * RHS is a compiled regex, we don't need to do anything with it.
557  */
559  CAST(lhs);
560  rcode = cond_cmp_values(request, c, lhs_type, lhs, PW_TYPE_INVALID, NULL);
561  break;
562  /*
563  * Unsupported types (should have been parse errors)
564  */
565  case TMPL_TYPE_NULL:
566  case TMPL_TYPE_LIST:
567  case TMPL_TYPE_UNKNOWN:
569  case TMPL_TYPE_REGEX: /* Should now be a TMPL_TYPE_REGEX_STRUCT or TMPL_TYPE_XLAT_STRUCT */
570  rad_assert(0);
571  rcode = -1;
572  break;
573  }
574 
575 finish:
576  talloc_free(lhs_cast_buff);
577  talloc_free(rhs_cast_buff);
578 
579  return rcode;
580 }
581 
582 /** Evaluate a map
583  *
584  * @param[in] request the REQUEST
585  * @param[in] modreturn the previous module return code
586  * @param[in] depth of the recursion (only used for debugging)
587  * @param[in] c the condition to evaluate
588  * @return
589  * - -1 on failure.
590  * - 0 for "no match".
591  * - 1 for "match".
592  */
593 int radius_evaluate_map(REQUEST *request, UNUSED int modreturn, UNUSED int depth, fr_cond_t const *c)
594 {
595  int rcode = 0;
596 
597  vp_map_t const *map = c->data.map;
598 
599  EVAL_DEBUG(">>> MAP TYPES LHS: %s, RHS: %s",
600  fr_int2str(tmpl_names, map->lhs->type, "???"),
601  fr_int2str(tmpl_names, map->rhs->type, "???"));
602 
603  switch (map->lhs->type) {
604  /*
605  * LHS is an attribute or list
606  */
607  case TMPL_TYPE_LIST:
608  case TMPL_TYPE_ATTR:
609  {
610  VALUE_PAIR *vp;
611  vp_cursor_t cursor;
612  /*
613  * Legacy paircompare call, skip processing the magic attribute
614  * if it's the LHS and cast RHS to the same type.
615  */
616  if ((c->pass2_fixup == PASS2_PAIRCOMPARE) && (map->op != T_OP_REG_EQ)) {
617 #ifndef NDEBUG
618  rad_assert(radius_find_compare(map->lhs->tmpl_da)); /* expensive assert */
619 #endif
620  rcode = cond_normalise_and_cmp(request, c, PW_TYPE_INVALID, NULL, NULL);
621  break;
622  }
623  for (vp = tmpl_cursor_init(&rcode, &cursor, request, map->lhs);
624  vp;
625  vp = tmpl_cursor_next(&cursor, map->lhs)) {
626  /*
627  * Evaluate all LHS values, condition evaluates to true
628  * if we get at least one set of operands that
629  * evaluates to true.
630  */
631  rcode = cond_normalise_and_cmp(request, c, vp->da->type, vp->da, &vp->data);
632  if (rcode != 0) break;
633  }
634  }
635  break;
636 
637  case TMPL_TYPE_DATA:
638  rcode = cond_normalise_and_cmp(request, c,
639  map->lhs->tmpl_data_type, NULL, &map->lhs->tmpl_data_value);
640  break;
641 
642  case TMPL_TYPE_UNPARSED:
643  case TMPL_TYPE_EXEC:
644  case TMPL_TYPE_XLAT:
646  {
647  ssize_t ret;
649 
650  if (map->lhs->type != TMPL_TYPE_UNPARSED) {
651  char *p;
652 
653  ret = tmpl_aexpand(request, &p, request, map->lhs, NULL, NULL);
654  if (ret < 0) {
655  EVAL_DEBUG("FAIL [%i]", __LINE__);
656  return ret;
657  }
658  data.strvalue = p;
659  data.length = (size_t)ret;
660  } else {
661  data.strvalue = map->lhs->name;
662  data.length = map->lhs->len;
663  }
664  rad_assert(data.strvalue);
665 
666  rcode = cond_normalise_and_cmp(request, c, PW_TYPE_STRING, NULL, &data);
667  if (map->lhs->type != TMPL_TYPE_UNPARSED) talloc_free(data.ptr);
668  }
669  break;
670 
671  /*
672  * Unsupported types (should have been parse errors)
673  */
674  case TMPL_TYPE_NULL:
676  case TMPL_TYPE_UNKNOWN:
677  case TMPL_TYPE_REGEX: /* should now be a TMPL_TYPE_REGEX_STRUCT or TMPL_TYPE_XLAT_STRUCT */
678  case TMPL_TYPE_REGEX_STRUCT: /* not allowed as LHS */
679  rad_assert(0);
680  rcode = -1;
681  break;
682  }
683 
684  EVAL_DEBUG("<<<");
685 
686  return rcode;
687 }
688 
689 /** Evaluate a fr_cond_t;
690  *
691  * @param[in] request the REQUEST
692  * @param[in] modreturn the previous module return code
693  * @param[in] depth of the recursion (only used for debugging)
694  * @param[in] c the condition to evaluate
695  * @return
696  * - -1 on failure.
697  * - -2 on attribute not found.
698  * - 0 for "no match".
699  * - 1 for "match".
700  */
701 int radius_evaluate_cond(REQUEST *request, int modreturn, int depth, fr_cond_t const *c)
702 {
703  int rcode = -1;
704 #ifdef WITH_EVAL_DEBUG
705  char buffer[1024];
706 
707  fr_cond_snprint(buffer, sizeof(buffer), c);
708  EVAL_DEBUG("%s", buffer);
709 #endif
710 
711  while (c) {
712  switch (c->type) {
713  case COND_TYPE_EXISTS:
714  rcode = radius_evaluate_tmpl(request, modreturn, depth, c->data.vpt);
715  /* Existence checks are special, because we expect them to fail */
716  if (rcode < 0) rcode = 0;
717  break;
718 
719  case COND_TYPE_MAP:
720  rcode = radius_evaluate_map(request, modreturn, depth, c);
721  break;
722 
723  case COND_TYPE_CHILD:
724  rcode = radius_evaluate_cond(request, modreturn, depth + 1, c->data.child);
725  break;
726 
727  case COND_TYPE_TRUE:
728  rcode = true;
729  break;
730 
731  case COND_TYPE_FALSE:
732  rcode = false;
733  break;
734  default:
735  EVAL_DEBUG("FAIL %d", __LINE__);
736  return -1;
737  }
738 
739  if (rcode < 0) return rcode;
740 
741  if (c->negate) rcode = !rcode;
742 
743  if (!c->next) break;
744 
745  /*
746  * FALSE && ... = FALSE
747  */
748  if (!rcode && (c->next_op == COND_AND)) return false;
749 
750  /*
751  * TRUE || ... = TRUE
752  */
753  if (rcode && (c->next_op == COND_OR)) return true;
754 
755  c = c->next;
756  }
757 
758  if (rcode < 0) {
759  EVAL_DEBUG("FAIL %d", __LINE__);
760  }
761  return rcode;
762 }
763 #endif
764 
765 
766 /*
767  * The fr_pair_list_move() function in src/lib/valuepair.c does all sorts of
768  * extra magic that we don't want here.
769  *
770  * FIXME: integrate this with the code calling it, so that we
771  * only fr_pair_list_copy() those attributes that we're really going to
772  * use.
773  */
774 void radius_pairmove(REQUEST *request, VALUE_PAIR **to, VALUE_PAIR *from, bool do_xlat)
775 {
776  int i, j, count, from_count, to_count, tailto;
777  vp_cursor_t cursor;
778  VALUE_PAIR *vp, *next, **last;
779  VALUE_PAIR **from_list, **to_list;
780  VALUE_PAIR *append, **append_tail;
781  VALUE_PAIR *to_copy;
782  bool *edited = NULL;
783  REQUEST *fixup = NULL;
784  TALLOC_CTX *ctx;
785 
786  /*
787  * Set up arrays for editing, to remove some of the
788  * O(N^2) dependencies. This also makes it easier to
789  * insert and remove attributes.
790  *
791  * It also means that the operators apply ONLY to the
792  * attributes in the original list. With the previous
793  * implementation of fr_pair_list_move(), adding two attributes
794  * via "+=" and then "=" would mean that the second one
795  * wasn't added, because of the existence of the first
796  * one in the "to" list. This implementation doesn't
797  * have that bug.
798  *
799  * Also, the previous implementation did NOT implement
800  * "-=" correctly. If two of the same attributes existed
801  * in the "to" list, and you tried to subtract something
802  * matching the *second* value, then the fr_pair_delete_by_num()
803  * function was called, and the *all* attributes of that
804  * number were deleted. With this implementation, only
805  * the matching attributes are deleted.
806  */
807  count = 0;
808  for (vp = fr_cursor_init(&cursor, &from); vp; vp = fr_cursor_next(&cursor)) count++;
809  from_list = talloc_array(request, VALUE_PAIR *, count);
810 
811  for (vp = fr_cursor_init(&cursor, to); vp; vp = fr_cursor_next(&cursor)) count++;
812  to_list = talloc_array(request, VALUE_PAIR *, count);
813 
814  append = NULL;
815  append_tail = &append;
816 
817  /*
818  * Move the lists to the arrays, and break the list
819  * chains.
820  */
821  from_count = 0;
822  for (vp = from; vp != NULL; vp = next) {
823  next = vp->next;
824  from_list[from_count++] = vp;
825  vp->next = NULL;
826  }
827 
828  to_count = 0;
829  ctx = talloc_parent(*to);
830  to_copy = fr_pair_list_copy(ctx, *to);
831  for (vp = to_copy; vp != NULL; vp = next) {
832  next = vp->next;
833  to_list[to_count++] = vp;
834  vp->next = NULL;
835  }
836  tailto = to_count;
837  edited = talloc_zero_array(request, bool, to_count);
838 
839  RDEBUG4("::: FROM %d TO %d MAX %d", from_count, to_count, count);
840 
841  /*
842  * Now that we have the lists initialized, start working
843  * over them.
844  */
845  for (i = 0; i < from_count; i++) {
846  int found;
847 
848  RDEBUG4("::: Examining %s", from_list[i]->da->name);
849 
850  if (do_xlat) radius_xlat_do(request, from_list[i]);
851 
852  /*
853  * Attribute should be appended, OR the "to" list
854  * is empty, and we're supposed to replace or
855  * "add if not existing".
856  */
857  if (from_list[i]->op == T_OP_ADD) goto do_append;
858 
859  found = false;
860  for (j = 0; j < to_count; j++) {
861  if (edited[j] || !to_list[j] || !from_list[i]) continue;
862 
863  /*
864  * Attributes aren't the same, skip them.
865  */
866  if (from_list[i]->da != to_list[j]->da) {
867  continue;
868  }
869 
870  /*
871  * We don't use a "switch" statement here
872  * because we want to break out of the
873  * "for" loop over 'j' in most cases.
874  */
875 
876  /*
877  * Over-write the FIRST instance of the
878  * matching attribute name. We free the
879  * one in the "to" list, and move over
880  * the one in the "from" list.
881  */
882  if (from_list[i]->op == T_OP_SET) {
883  RDEBUG4("::: OVERWRITING %s FROM %d TO %d",
884  to_list[j]->da->name, i, j);
885  fr_pair_list_free(&to_list[j]);
886  to_list[j] = from_list[i];
887  from_list[i] = NULL;
888  edited[j] = true;
889  break;
890  }
891 
892  /*
893  * Add the attribute only if it does not
894  * exist... but it exists, so we stop
895  * looking.
896  */
897  if (from_list[i]->op == T_OP_EQ) {
898  found = true;
899  break;
900  }
901 
902  /*
903  * Delete every attribute, independent
904  * of its value.
905  */
906  if (from_list[i]->op == T_OP_CMP_FALSE) {
907  goto delete;
908  }
909 
910  /*
911  * Delete all matching attributes from
912  * "to"
913  */
914  if ((from_list[i]->op == T_OP_SUB) ||
915  (from_list[i]->op == T_OP_CMP_EQ) ||
916  (from_list[i]->op == T_OP_LE) ||
917  (from_list[i]->op == T_OP_GE)) {
918  int rcode;
919  int old_op = from_list[i]->op;
920 
921  /*
922  * Check for equality.
923  */
924  from_list[i]->op = T_OP_CMP_EQ;
925 
926  /*
927  * If equal, delete the one in
928  * the "to" list.
929  */
930  rcode = radius_compare_vps(NULL, from_list[i],
931  to_list[j]);
932  /*
933  * We may want to do more
934  * subtractions, so we re-set the
935  * operator back to it's original
936  * value.
937  */
938  from_list[i]->op = old_op;
939 
940  switch (old_op) {
941  case T_OP_CMP_EQ:
942  if (rcode != 0) goto delete;
943  break;
944 
945  case T_OP_SUB:
946  if (rcode == 0) {
947  delete:
948  RDEBUG4("::: DELETING %s FROM %d TO %d",
949  from_list[i]->da->name, i, j);
950  fr_pair_list_free(&to_list[j]);
951  to_list[j] = NULL;
952  }
953  break;
954 
955  /*
956  * Enforce <=. If it's
957  * >, replace it.
958  */
959  case T_OP_LE:
960  if (rcode > 0) {
961  RDEBUG4("::: REPLACING %s FROM %d TO %d",
962  from_list[i]->da->name, i, j);
963  fr_pair_list_free(&to_list[j]);
964  to_list[j] = from_list[i];
965  from_list[i] = NULL;
966  edited[j] = true;
967  }
968  break;
969 
970  case T_OP_GE:
971  if (rcode < 0) {
972  RDEBUG4("::: REPLACING %s FROM %d TO %d",
973  from_list[i]->da->name, i, j);
974  fr_pair_list_free(&to_list[j]);
975  to_list[j] = from_list[i];
976  from_list[i] = NULL;
977  edited[j] = true;
978  }
979  break;
980  }
981 
982  continue;
983  }
984 
985  rad_assert(0 == 1); /* panic! */
986  }
987 
988  /*
989  * We were asked to add it if it didn't exist,
990  * and it doesn't exist. Move it over to the
991  * tail of the "to" list, UNLESS it was already
992  * moved by another operator.
993  */
994  if (!found && from_list[i]) {
995  if ((from_list[i]->op == T_OP_EQ) ||
996  (from_list[i]->op == T_OP_LE) ||
997  (from_list[i]->op == T_OP_GE) ||
998  (from_list[i]->op == T_OP_SET)) {
999  do_append:
1000  RDEBUG4("::: APPENDING %s FROM %d TO %d",
1001  from_list[i]->da->name, i, tailto);
1002  *append_tail = from_list[i];
1003  from_list[i]->op = T_OP_EQ;
1004  from_list[i] = NULL;
1005  append_tail = &(*append_tail)->next;
1006  }
1007  }
1008  }
1009 
1010  /*
1011  * Delete attributes in the "from" list.
1012  */
1013  for (i = 0; i < from_count; i++) {
1014  if (!from_list[i]) continue;
1015 
1016  fr_pair_list_free(&from_list[i]);
1017  }
1018  talloc_free(from_list);
1019 
1020  RDEBUG4("::: TO in %d out %d", to_count, tailto);
1021 
1022  /*
1023  * Re-chain the "to" list.
1024  */
1025  fr_pair_list_free(to);
1026  last = to;
1027 
1028  if (to == &request->packet->vps) {
1029  fixup = request;
1030  } else if (request->parent && (to == &request->parent->packet->vps)) {
1031  fixup = request->parent;
1032  }
1033 
1034  for (i = 0; i < tailto; i++) {
1035  if (!to_list[i]) continue;
1036 
1037  vp = to_list[i];
1038  RDEBUG4("::: to[%d] = %s", i, vp->da->name);
1039 
1040  /*
1041  * Mash the operator to a simple '='. The
1042  * operators in the "to" list aren't used for
1043  * anything. BUT they're used in the "detail"
1044  * file and debug output, where we don't want to
1045  * see the operators.
1046  */
1047  vp->op = T_OP_EQ;
1048 
1049  *last = vp;
1050  last = &(*last)->next;
1051  }
1052 
1053  /*
1054  * And finally add in the attributes we're appending to
1055  * the tail of the "to" list.
1056  */
1057  *last = append;
1058 
1059  /*
1060  * Fix dumb cache issues
1061  */
1062  if (fixup) {
1063  fixup->username = NULL;
1064  fixup->password = NULL;
1065 
1066  for (vp = fixup->packet->vps; vp != NULL; vp = vp->next) {
1067  if (vp->da->vendor) continue;
1068 
1069  if ((vp->da->attr == PW_USER_NAME) && !fixup->username) {
1070  fixup->username = vp;
1071 
1072  } else if (vp->da->attr == PW_STRIPPED_USER_NAME) {
1073  fixup->username = vp;
1074 
1075  } else if (vp->da->attr == PW_USER_PASSWORD) {
1076  fixup->password = vp;
1077  }
1078  }
1079  }
1080 
1081  rad_assert(request->packet != NULL);
1082 
1083  talloc_free(to_list);
1084  talloc_free(edited);
1085 }
void fr_pair_list_free(VALUE_PAIR **)
Free memory used by a valuepair list.
Definition: pair.c:544
#define EVAL_DEBUG(...)
Definition: evaluate.c:37
int radius_evaluate_tmpl(REQUEST *request, int modreturn, UNUSED int depth, vp_tmpl_t const *vpt)
Evaluate a template.
Definition: evaluate.c:82
FR_NAME_NUMBER const modreturn_table[]
Definition: evaluate.c:40
The module is OK, continue.
Definition: radiusd.h:91
Definition: parser.h:42
Dictionary attribute.
Definition: dict.h:77
union fr_cond_t::@6 data
char const * name
Raw string used to create the template.
Definition: tmpl.h:190
vp_tmpl_t * lhs
Typically describes the attribute to add, modify or compare.
Definition: map.h:47
Dictionary attribute.
Definition: tmpl.h:133
int paircompare(REQUEST *request, VALUE_PAIR *req_list, VALUE_PAIR *check, VALUE_PAIR **rep_list)
Compare two pair lists except for the password information.
Definition: pair.c:479
vp_tmpl_t * rhs
Typically describes a literal value or a src attribute to copy or compare.
Definition: map.h:48
Pre-parsed XLAT expansion.
Definition: tmpl.h:139
VALUE_PAIR * username
Cached username VALUE_PAIR from request RADIUS_PACKET.
Definition: radiusd.h:222
#define REMARKER(_m, _i, _e)
Output string with error marker, showing where format error occurred.
Definition: log.h:306
#define UNUSED
Definition: libradius.h:134
Unparsed literal string.
Definition: tmpl.h:131
VALUE_PAIR * vps
Result of decoding the packet into VALUE_PAIRs.
Definition: libradius.h:162
Error resolving rcode (should not be returned by modules).
Definition: radiusd.h:99
VALUE_PAIR * fr_cursor_init(vp_cursor_t *cursor, VALUE_PAIR *const *node)
Setup a cursor to iterate over attribute pairs.
Definition: cursor.c:60
void radius_pairmove(REQUEST *request, VALUE_PAIR **to, VALUE_PAIR *from, bool do_xlat)
Definition: evaluate.c:774
fr_dict_attr_t const * cast
Definition: parser.h:83
VALUE_PAIR * password
Cached password VALUE_PAIR from request RADIUS_PACKET.
Definition: radiusd.h:223
fr_cond_t * next
Definition: parser.h:86
Definition: token.h:46
The module considers the request invalid.
Definition: radiusd.h:93
static expr_map_t map[]
Definition: rlm_expr.c:169
int radius_find_compare(fr_dict_attr_t const *attribute)
Find a comparison function for two attributes.
Definition: pair.c:303
Abstraction to allow iterating over different configurations of VALUE_PAIRs.
Definition: pair.h:144
struct value_pair * next
Definition: pair.h:116
Definition: token.h:50
Attribute not found in the global dictionary.
Definition: tmpl.h:134
static int cond_cmp_values(REQUEST *request, fr_cond_t const *c, PW_TYPE lhs_type, value_data_t const *lhs, PW_TYPE rhs_type, value_data_t const *rhs)
Call the correct data comparison function for the condition.
Definition: evaluate.c:288
#define rad_assert(expr)
Definition: rad_assert.h:38
Reject the request (user is locked out).
Definition: radiusd.h:94
Pre-parsed regular expression.
Definition: tmpl.h:140
int fr_str2int(FR_NAME_NUMBER const *table, char const *name, int def)
Definition: token.c:451
VALUE_PAIR * tmpl_cursor_init(int *err, vp_cursor_t *cursor, REQUEST *request, vp_tmpl_t const *vpt)
Initialise a vp_cursor_t to the VALUE_PAIR specified by a vp_tmpl_t.
Definition: tmpl.c:1990
VALUE_PAIR * fr_pair_list_copy(TALLOC_CTX *ctx, VALUE_PAIR *from)
Copy a pairlist.
Definition: pair.c:1394
Value in native format.
Definition: tmpl.h:138
int tmpl_find_vp(VALUE_PAIR **out, REQUEST *request, vp_tmpl_t const *vpt)
Returns the first VP matching a vp_tmpl_t.
Definition: tmpl.c:2224
int radius_evaluate_map(REQUEST *request, UNUSED int modreturn, UNUSED int depth, fr_cond_t const *c)
Evaluate a map.
Definition: evaluate.c:593
int radius_evaluate_cond(REQUEST *request, int modreturn, int depth, fr_cond_t const *c)
Evaluate a fr_cond_t;.
Definition: evaluate.c:701
const FR_NAME_NUMBER dict_attr_types[]
Map data types to names representing those types.
Definition: dict.c:85
Regular expression.
Definition: tmpl.h:136
unsigned int attr
Attribute number.
Definition: dict.h:79
Immediately reject the request.
Definition: radiusd.h:89
Definition: token.h:43
bool negate
Definition: parser.h:80
Attribute list.
Definition: tmpl.h:135
unsigned int vendor
Vendor that defines this attribute.
Definition: dict.h:78
#define CAST(_s)
Definition: token.h:44
REQUEST * parent
Definition: radiusd.h:290
Stores an attribute, a value and various bits of other data.
Definition: pair.h:112
ssize_t tmpl_aexpand(TALLOC_CTX *ctx, char **out, REQUEST *request, vp_tmpl_t const *vpt, xlat_escape_t escape, void *escape_ctx)
Expand a template to a string, allocing a new buffer to hold the string.
Definition: tmpl.c:1653
Invalid (uninitialised) attribute type.
Definition: radius.h:32
Definition: token.h:45
FR_TOKEN op
Operator to use when moving or inserting valuepair into a list.
Definition: pair.h:118
int radius_xlat_do(REQUEST *request, VALUE_PAIR *vp)
Expands an attribute marked with fr_pair_mark_xlat.
Definition: pair.c:655
tmpl_type_t type
What type of value tmpl refers to.
Definition: tmpl.h:188
char const * fr_strerror(void)
Get the last library error.
Definition: log.c:212
size_t len
Length of the raw string used to create the template.
Definition: tmpl.h:191
int value_data_copy(TALLOC_CTX *ctx, value_data_t *dst, PW_TYPE type, const value_data_t *src)
Copy value data verbatim duplicating any buffers.
Definition: value.c:1479
Definition: token.h:48
Module succeeded without doing anything.
Definition: radiusd.h:96
Callout to an external script or program.
Definition: tmpl.h:137
char name[1]
Attribute name.
Definition: dict.h:89
uint8_t data[]
Definition: eap_pwd.h:625
size_t length
Length of value data.
Definition: pair.h:87
Module failed, don't reply.
Definition: radiusd.h:90
FR_TOKEN op
The operator that controls insertion of the dst attribute.
Definition: map.h:50
size_t fr_cond_snprint(char *buffer, size_t bufsize, fr_cond_t const *c)
Definition: parser.c:50
VALUE_PAIR * fr_cursor_next(vp_cursor_t *cursor)
Advanced the cursor to the next VALUE_PAIR.
Definition: cursor.c:263
static int cond_normalise_and_cmp(REQUEST *request, fr_cond_t const *c, PW_TYPE lhs_type, fr_dict_attr_t const *lhs_enumv, value_data_t const *lhs)
Convert both operands to the same type.
Definition: evaluate.c:367
static bool all_digits(char const *string)
Definition: evaluate.c:54
fr_cond_pass2_t pass2_fixup
Definition: parser.h:81
RADIUS_PACKET * packet
Incoming request.
Definition: radiusd.h:221
#define REDEBUG(fmt,...)
Definition: log.h:254
Uninitialised.
Definition: tmpl.h:130
VALUE_PAIR * fr_pair_afrom_da(TALLOC_CTX *ctx, fr_dict_attr_t const *da)
Dynamically allocate a new attribute.
Definition: pair.c:58
char const * fr_int2str(FR_NAME_NUMBER const *table, int number, char const *def)
Definition: token.c:506
XLAT expansion.
Definition: tmpl.h:132
int value_data_cmp_op(FR_TOKEN op, PW_TYPE a_type, value_data_t const *a, PW_TYPE b_type, value_data_t const *b)
Compare two attributes using an operator.
Definition: value.c:299
fr_dict_attr_t const * da
Dictionary attribute defines the attribute.
Definition: pair.h:113
#define RDEBUG4(fmt,...)
Definition: log.h:246
String of printable characters.
Definition: radius.h:33
PW_TYPE type
Value type.
Definition: dict.h:80
User not found.
Definition: radiusd.h:95
VALUE_PAIR * tmpl_cursor_next(vp_cursor_t *cursor, vp_tmpl_t const *vpt)
Returns the next VALUE_PAIR specified by vpt.
Definition: tmpl.c:2128
#define RCSID(id)
Definition: build.h:135
OK (pairs modified).
Definition: radiusd.h:97
The module handled the request, so stop.
Definition: radiusd.h:92
int radius_compare_vps(REQUEST *request, VALUE_PAIR *check, VALUE_PAIR *vp)
Value pair map.
Definition: map.h:46
#define CHECK_INT_CAST(_l, _r)
A source or sink of value data.
Definition: tmpl.h:187
size_t fr_bin2hex(char *hex, uint8_t const *bin, size_t inlen)
Convert binary data to a hex string.
Definition: misc.c:254
const FR_NAME_NUMBER tmpl_names[]
Map tmpl_type_t values to descriptive strings.
Definition: tmpl.c:36
Raw octets.
Definition: radius.h:38
Has no value.
Definition: tmpl.h:141
fr_cond_op_t next_op
Definition: parser.h:85
PW_TYPE
Internal data types used within libfreeradius.
Definition: radius.h:31
value_data_t data
Definition: pair.h:133
fr_cond_type_t type
Definition: parser.h:71