The FreeRADIUS server  $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
pairmove.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: 00035a763ce6a806f70935c91df80e202749298c $
19  *
20  * @file src/lib/server/pairmove.c
21  * @brief Old style mapping code
22  *
23  * @copyright 2007 The FreeRADIUS server project
24  * @copyright 2007 Alan DeKok (aland@deployingradius.com)
25  */
26 RCSID("$Id: 00035a763ce6a806f70935c91df80e202749298c $")
27 
28 #include <freeradius-devel/server/base.h>
29 #include <freeradius-devel/server/pairmove.h>
30 #include <freeradius-devel/server/tmpl_dcursor.h>
31 
32 #include <freeradius-devel/util/debug.h>
33 #include <freeradius-devel/util/calc.h>
34 #include <freeradius-devel/util/edit.h>
35 
36 #include <freeradius-devel/protocol/radius/rfc2865.h>
37 #include <freeradius-devel/protocol/freeradius/freeradius.internal.h>
38 
39 #include <ctype.h>
40 
41 /*
42  * @fixme - integrate this with the code calling it, so that we
43  * only fr_pair_list_copy() those attributes that we're really going to
44  * use.
45  */
47 {
48  int i, j, count, to_count, tailto;
49  fr_pair_t *from_vp, *next_from, *to_vp, *next_to = NULL;
50  fr_pair_list_t append, prepend;
51  bool *edited = NULL;
52  bool *deleted = NULL;
53 
54  /*
55  * Set up arrays for editing, to remove some of the
56  * O(N^2) dependencies. These record which elements in
57  * the "to" list have been either edited or marked for
58  * deletion.
59  *
60  * It also means that the operators apply ONLY to the
61  * attributes in the original list.
62  *
63  * Also, the previous implementation did NOT implement
64  * "-=" correctly. If two of the same attributes existed
65  * in the "to" list, and you tried to subtract something
66  * matching the *second* value, then the fr_pair_delete_by_da()
67  * function was called, and the *all* attributes of that
68  * number were deleted. With this implementation, only
69  * the matching attributes are deleted.
70  */
71 
72  fr_pair_list_init(&append);
73  fr_pair_list_init(&prepend);
74 
75  to_count = fr_pair_list_num_elements(to);
76  tailto = to_count;
77  edited = talloc_zero_array(request, bool, to_count);
78  deleted = talloc_zero_array(request, bool, to_count);
79 
80  count = to_count + fr_pair_list_num_elements(from);
81 
82  RDEBUG4("::: FROM %ld TO %d MAX %d", fr_pair_list_num_elements(from), to_count, count);
83 
84  /*
85  * Now that we have the lists initialized, start working
86  * over them.
87  */
88  for (i = 0, from_vp = fr_pair_list_head(from); from_vp; i++, from_vp = next_from) {
89  int found;
90  /* Find the next from pair before any manipulation happens */
91  next_from = fr_pair_list_next(from, from_vp);
92 
93  RDEBUG4("::: Examining %s", from_vp->da->name);
94 
95  /*
96  * Attribute should be appended, OR the "to" list
97  * is empty, and we're supposed to replace or
98  * "add if not existing".
99  */
100  if (from_vp->op == T_OP_ADD_EQ) goto do_append;
101 
102  /*
103  * The attribute needs to be prepended to the "to"
104  * list - store it in the prepend list
105  */
106 
107  if (from_vp->op == T_OP_PREPEND) {
108  RDEBUG4("::: PREPENDING %s FROM %d", from_vp->da->name, i);
109  fr_pair_remove(from, from_vp);
110  fr_pair_prepend(&prepend, from_vp);
111  from_vp->op = T_OP_EQ;
112  continue;
113  }
114  found = false;
115  j = 0;
116  for (to_vp = fr_pair_list_head(to); to_vp; to_vp = next_to, j++) {
117  next_to = fr_pair_list_next(to, to_vp);
118  if (edited[j] || deleted[j] || !from_vp) continue;
119 
120  /*
121  * Attributes aren't the same, skip them.
122  */
123  if (from_vp->da != to_vp->da) {
124  continue;
125  }
126 
127  /*
128  * We don't use a "switch" statement here
129  * because we want to break out of the
130  * "for" loop over 'j' in most cases.
131  */
132 
133  /*
134  * Over-write the FIRST instance of the
135  * matching attribute name. We free the
136  * one in the "to" list, and move over
137  * the one in the "from" list.
138  */
139  if (from_vp->op == T_OP_SET) {
140  RDEBUG4("::: OVERWRITING %s FROM %d TO %d",
141  to_vp->da->name, i, j);
142  fr_pair_remove(from, from_vp);
143  fr_pair_replace(to, to_vp, from_vp);
144  from_vp = NULL;
145  edited[j] = true;
146  break;
147  }
148 
149  /*
150  * Add the attribute only if it does not
151  * exist... but it exists, so we stop
152  * looking.
153  */
154  if (from_vp->op == T_OP_EQ) {
155  found = true;
156  break;
157  }
158 
159  /*
160  * Delete every attribute, independent
161  * of its value.
162  */
163  if (from_vp->op == T_OP_CMP_FALSE) {
164  goto delete;
165  }
166 
167  /*
168  * Delete all matching attributes from
169  * "to"
170  */
171  if ((from_vp->op == T_OP_SUB_EQ) ||
172  (from_vp->op == T_OP_CMP_EQ) ||
173  (from_vp->op == T_OP_LE) ||
174  (from_vp->op == T_OP_GE)) {
175  int rcode;
176  int old_op = from_vp->op;
177  /*
178  * Check for equality.
179  */
180  from_vp->op = T_OP_CMP_EQ;
181 
182  /*
183  * If equal, delete the one in
184  * the "to" list.
185  */
186  rcode = paircmp_pairs(NULL, from_vp,
187  to_vp);
188  /*
189  * We may want to do more
190  * subtractions, so we re-set the
191  * operator back to it's original
192  * value.
193  */
194  from_vp->op = old_op;
195 
196  switch (old_op) {
197  case T_OP_CMP_EQ:
198  if (rcode != 0) goto delete;
199  break;
200 
201  case T_OP_SUB_EQ:
202  if (rcode == 0) {
203  delete:
204  RDEBUG4("::: DELETING %s FROM %d TO %d",
205  from_vp->da->name, i, j);
206  /*
207  * Mark that this will be deleted
208  */
209  deleted[j] = true;
210  }
211  break;
212 
213  /*
214  * Enforce <=. If it's
215  * >, replace it.
216  */
217  case T_OP_LE:
218  if (rcode > 0) {
219  RDEBUG4("::: REPLACING %s FROM %d TO %d",
220  from_vp->da->name, i, j);
221  goto replace;
222  }
223  break;
224 
225  case T_OP_GE:
226  if (rcode < 0) {
227  RDEBUG4("::: REPLACING %s FROM %d TO %d",
228  from_vp->da->name, i, j);
229  replace:
230  fr_pair_remove(from, from_vp);
231  fr_pair_replace(to, to_vp, from_vp);
232  from_vp = NULL;
233  edited[j] = true;
234  }
235  break;
236  }
237 
238  continue;
239  }
240 
241  fr_assert(0 == 1); /* panic! */
242  }
243 
244  /*
245  * We were asked to add it if it didn't exist,
246  * and it doesn't exist. Move it over to the
247  * tail of the "to" list, UNLESS it was already
248  * moved by another operator.
249  */
250  if (!found && from_vp) {
251  if ((from_vp->op == T_OP_EQ) ||
252  (from_vp->op == T_OP_LE) ||
253  (from_vp->op == T_OP_GE) ||
254  (from_vp->op == T_OP_SET)) {
255  do_append:
256  RDEBUG4("::: APPENDING %s FROM %d TO %d",
257  from_vp->da->name, i, tailto++);
258  fr_pair_remove(from, from_vp);
259  fr_pair_append(&append, from_vp);
260  from_vp->op = T_OP_EQ;
261  }
262  }
263  }
264 
265  /*
266  * Delete remaining attributes in the "from" list.
267  */
268  fr_pair_list_free(from);
269 
270  RDEBUG4("::: TO in %d out %d", to_count, tailto);
271 
272  /*
273  * Delete any "to" items marked for deletion
274  */
275 
276  i = 0;
277  for (to_vp = fr_pair_list_head(to); to_vp; to_vp = next_to, i++) {
278  next_to = fr_pair_list_next(to, to_vp);
279 
280  if (deleted[i]) {
281  fr_pair_remove(to, to_vp);
282  continue;
283  }
284 
285  RDEBUG4("::: to[%d] = %s", i, to_vp->da->name);
286 
287  /*
288  * Mash the operator to a simple '='. The
289  * operators in the "to" list aren't used for
290  * anything. BUT they're used in the "detail"
291  * file and debug output, where we don't want to
292  * see the operators.
293  */
294  to_vp->op = T_OP_EQ;
295  }
296 
297  /*
298  * Now prepend any items in the "prepend" list to
299  * the head of the "to" list.
300  */
301  fr_pair_list_prepend(to, &prepend);
302 
303  /*
304  * And finally add in the attributes we're appending to
305  * the tail of the "to" list.
306  */
307  fr_pair_list_append(to, &append);
308 
309  fr_assert(request->packet != NULL);
310 
311  talloc_free(edited);
312  talloc_free(deleted);
313 }
314 
315 static int radius_legacy_map_to_vp(request_t *request, fr_pair_t *parent, map_t const *map)
316 {
317  fr_pair_t *vp;
318  fr_dict_attr_t const *da;
319  fr_value_box_t *box, *to_free = NULL;
320 
321  RDEBUG(" %s %s %s", map->lhs->name, fr_tokens[map->op], map->rhs->name);
322 
323  da = tmpl_attr_tail_da(map->lhs);
324  fr_assert(fr_type_is_leaf(da->type));
325 
326  if (tmpl_is_data(map->rhs)) {
327  box = tmpl_value(map->rhs);
328 
329  } else if (tmpl_is_attr(map->rhs)) {
330  fr_pair_t *rhs;
331 
332  if (tmpl_find_vp(&rhs, request, map->rhs) < 0) return -1;
333 
334  if (rhs->vp_type != da->type) {
335  fr_strerror_const("Incompatible data types");
336  return -1;
337  }
338 
339  box = &rhs->data;
340 
341  } else if (tmpl_is_xlat(map->rhs)) {
342  if (tmpl_aexpand(parent, &to_free, request, map->rhs, NULL, NULL) < 0) return -1;
343 
344  box = to_free;
345 
346  } else {
347  fr_strerror_const("Unknown RHS");
348  return -1;
349  }
350 
351  if (fr_pair_append_by_da(parent, &vp, &parent->vp_group, da) < 0) return -1;
352 
353  if (fr_value_box_cast(vp, &vp->data, vp->vp_type, vp->da, box) < 0) {
354  TALLOC_FREE(to_free);
355  return -1;
356  }
357 
358  TALLOC_FREE(to_free);
359  return 0;
360 }
361 
362 
363 static int CC_HINT(nonnull) radius_legacy_map_apply_structural(request_t *request, map_t const *map, fr_pair_t *vp)
364 {
365  fr_value_box_t *box, *to_free = NULL;
366 
367  /*
368  * No RHS map, but we have children. Create them, and add them to the list.
369  */
370  if (!map->rhs) {
371  map_t *child;
372 
373  /*
374  * Convert the child maps to VPs. We know that
375  * we just created the pair, so there's no reason
376  * to apply operators to the children.
377  */
378  for (child = map_list_next(&map->child, NULL);
379  child != NULL;
380  child = map_list_next(&map->child, child)) {
381  fr_assert(child->op == T_OP_EQ);
382  if (radius_legacy_map_to_vp(request, vp, child) < 0) return -1;
383  }
384 
385  return 0;
386  }
387 
388  /*
389  * Copy an existing attribute.
390  */
391  if (tmpl_is_attr(map->rhs)) {
392  fr_pair_t *rhs;
393 
394  if (tmpl_find_vp(&rhs, request, map->rhs) < 0) return -1;
395 
396  if (rhs->vp_type != vp->vp_type) {
397  fr_strerror_const("Incompatible data types");
398  return -1;
399  }
400 
401  if (rhs == vp) {
402  fr_strerror_const("Invalid self-reference");
403  return -1;
404  }
405 
406  return fr_pair_list_copy(vp, &vp->vp_group, &rhs->vp_group);
407  }
408 
409  /*
410  * RHS is a string or an xlat expansion.
411  */
412  if (tmpl_is_data(map->rhs)) {
413  box = tmpl_value(map->rhs);
414 
415  } else if (tmpl_is_xlat(map->rhs)) {
416  if (tmpl_aexpand(request, &to_free, request, map->rhs, NULL, NULL) < 0) return -1;
417 
418  box = to_free;
419 
420  } else {
421  fr_strerror_const("Unknown RHS");
422  return -1;
423  }
424 
425  if (box->type != FR_TYPE_STRING) {
426  fr_strerror_const("Cannot parse child list");
427  TALLOC_FREE(to_free);
428  return -1;
429  }
430 
431  /*
432  * If there's no value, just leave the list alone.
433  *
434  * Otherwise parse the children in the context of the parent.
435  */
436  if (box->vb_strvalue[0]) {
437  fr_pair_parse_t root, relative;
438 
439  /*
440  * Parse the string as a list of pairs.
441  */
442  root = (fr_pair_parse_t) {
443  .ctx = vp,
444  .da = vp->da,
445  .list = &vp->vp_group,
446  .allow_compare = false,
447  .tainted = box->tainted,
448  };
449  relative = (fr_pair_parse_t) { };
450 
451  if (fr_pair_list_afrom_substr(&root, &relative, &FR_SBUFF_IN(box->vb_strvalue, box->vb_length)) < 0) {
452  RPEDEBUG("Failed parsing string '%pV' as attribute list", box);
453  TALLOC_FREE(to_free);
454  return -1;
455  }
456  }
457 
458  TALLOC_FREE(to_free);
459  return 0;
460 }
461 
462 typedef struct {
464  fr_pair_t *vp; /* the one we created */
466 
467 /** Build the relevant pairs at each level.
468  *
469  * See edit_list_pair_build() for similar code.
470  */
471 static fr_pair_t *legacy_pair_build(fr_pair_t *parent, fr_dcursor_t *cursor, fr_dict_attr_t const *da, void *uctx)
472 {
473  fr_pair_t *vp;
474  legacy_pair_build_t *lp = uctx;
475 
476  vp = fr_pair_afrom_da(parent, da);
477  if (!vp) return NULL;
478 
479  if (fr_edit_list_insert_pair_tail(lp->el, &parent->vp_group, vp) < 0) {
480  talloc_free(vp);
481  return NULL;
482  }
483 
484  /*
485  * Tell the cursor that we appended a pair. This
486  * function only gets called when we've ran off of the
487  * end of the list, and can't find the thing we're
488  * looking for. So it's safe at set the current one
489  * here.
490  *
491  * @todo - mainly only because we don't allow creating
492  * foo[4] when there's <3 matching entries. i.e. the
493  * "arrays" here are really lists, so we can't create
494  * "holes" in the list.
495  */
496  fr_dcursor_set_current(cursor, vp);
497 
498  lp->vp = vp;
499 
500  return vp;
501 }
502 
503 
504 /** Move a map using the operators from the old pairmove functionality.
505  *
506  */
508 {
509  int16_t num;
510  int err, rcode;
511  bool added = false;
512  fr_pair_t *vp = NULL, *next, *parent;
513  fr_dict_attr_t const *da;
514  fr_pair_list_t *list;
515  TALLOC_CTX *ctx;
516  fr_value_box_t *to_free = NULL;
517  fr_value_box_t const *box;
519  fr_dcursor_t cursor;
520 
521  /*
522  * Find out where this attribute exists, or should exist.
523  */
524  tmpl_pair_list_and_ctx(ctx, list, request, tmpl_request(map->lhs), tmpl_list(map->lhs));
525  if (!ctx) return -1; /* no request or list head exists */
526 
527  da = tmpl_attr_tail_da(map->lhs);
528 
529  /*
530  * These operations are the same for both leaf and structural types.
531  */
532  switch (map->op) {
533  case T_OP_EQ:
534  if (tmpl_find_vp(&vp, request, map->lhs) < -1) return -1;
535  if (vp) return 0;
536  goto add;
537 
538  case T_OP_SET:
539  /*
540  * Set a value. Note that we might do
541  *
542  * &foo[1] := 1
543  *
544  * In which case we don't want to delete the attribute, we just want to replace its
545  * value.
546  *
547  * @todo - we can't do &foo[*].bar[*].baz = 1, as that's an implicit cursor, and we don't
548  * do that.
549  */
550  num = tmpl_attr_tail_num(map->lhs);
551  if (num == NUM_COUNT) {
552  fr_strerror_const("Invalid count in attribute reference");
553  return -1;
554  }
555 
556  vp = tmpl_dcursor_init(&err, ctx, &cc, &cursor, request, map->lhs);
557 
558  /*
559  * We're editing a specific number. It must exist, otherwise the edit does nothing.
560  */
561  if ((num >= 0) || (num == NUM_LAST)) {
562  if (!vp) return 0;
563 
564  if (fr_type_is_leaf(vp->vp_type)) {
565  if (fr_edit_list_save_pair_value(el, vp) < 0) return -1;
566  } else {
567  if (fr_edit_list_free_pair_children(el, vp) < 0) return -1;
568  }
569  break;
570  }
571 
572  /*
573  * We don't delete the main lists, we just modify their contents.
574  */
575  if ((da == request_attr_request) ||
576  (da == request_attr_reply) ||
577  (da == request_attr_control) ||
578  (da == request_attr_state)) {
579  fr_assert(vp != NULL);
580 
581  if (fr_edit_list_free_pair_children(el, vp) < 0) return -1;
582  break;
583  }
584 
585  if (!vp) goto add;
586 
587  /*
588  * Delete the first attribute we found.
589  */
591  fr_assert(parent != NULL);
592 
593  if (fr_edit_list_pair_delete(el, &parent->vp_group, vp) < 0) return -1;
594  tmpl_dcursor_clear(&cc);
595 
596  /*
597  * Delete all existing attributes. Note that we re-initialize the cursor every time,
598  * because creating "&foo := baz" means deleting ALL existing "foo". But we can't use
599  * the tmpl as a cursor, because the tmpl containst NUM_UNSPEC, and the cursor needs
600  * NUM_ALL. So we have to delete all existing attributes, and then add a new one.
601  */
602  while (true) {
603  vp = tmpl_dcursor_init(&err, ctx, &cc, &cursor, request, map->lhs);
604  if (!vp) break;
605 
607  fr_assert(parent != NULL);
608 
609  if (fr_edit_list_pair_delete(el, &parent->vp_group, vp) < 0) return -1;
610  tmpl_dcursor_clear(&cc);
611  }
612  FALL_THROUGH;
613 
614  case T_OP_ADD_EQ:
615  add:
616  {
618  .el = el,
619  .vp = NULL,
620  };
621 
623  vp = tmpl_dcursor_build_init(&err, ctx, &cc, &cursor, request, map->lhs, legacy_pair_build, &lp);
624  tmpl_dcursor_clear(&cc);
625  if (!vp) {
626  RWDEBUG("Failed creating attribute %s", map->lhs->name);
627  return -1;
628  }
629 
630  /*
631  * If we're adding and one already exists, create a new one in the same context.
632  */
633  if ((map->op == T_OP_ADD_EQ) && !lp.vp) {
635  fr_assert(parent != NULL);
636 
637  MEM(vp = fr_pair_afrom_da(parent, da));
638  if (fr_edit_list_insert_pair_tail(el, &parent->vp_group, vp) < 0) return -1;
639  }
640 
641  added = true;
642  }
643  break;
644 
645  case T_OP_LE: /* replace if not <= */
646  case T_OP_GE: /* replace if not >= */
647  if (fr_type_is_structural(da->type)) goto invalid_operator;
648 
649  if (tmpl_find_vp(&vp, request, map->lhs) < -1) return -1;
650  if (!vp) goto add;
651  break;
652 
653  case T_OP_SUB_EQ: /* delete if match, otherwise ignore */
654  if (fr_type_is_structural(da->type)) {
655  invalid_operator:
656  fr_strerror_printf("Invalid operator '%s' for structural type", fr_type_to_str(da->type));
657  return -1;
658  }
659 
660  if (tmpl_find_vp(&vp, request, map->lhs) < -1) return -1;
661  if (!vp) return 0;
662  break;
663 
664  default:
665  fr_strerror_printf("Invalid operator '%s'", fr_tokens[map->op]);
666  return -1;
667  }
668 
669  fr_assert(vp);
670 
671  /*
672  * We don't support operations on structural types. Just creation, and assign values.
673  *
674  * The code above has ensured that the structural type has been either saved or cleared via the
675  * edit list, so the next function doesn't need to do that.
676  */
677  if (fr_type_is_structural(tmpl_attr_tail_da(map->lhs)->type)) {
678  fr_assert(added);
679  return radius_legacy_map_apply_structural(request, map, vp);
680  }
681 
682  /*
683  * We have now found the RHS. Expand it.
684  *
685  * Note that
686  *
687  * &foo := %tolower(&foo)
688  *
689  * works, as we save the value above in the T_OP_SET handler. So we don't delete it.
690  */
691  if (tmpl_is_data(map->rhs)) {
692  box = tmpl_value(map->rhs);
693 
694  } else if (tmpl_is_attr(map->rhs)) {
695  fr_pair_t *rhs;
696 
697  if (tmpl_find_vp(&rhs, request, map->rhs) < 0) return -1;
698 
699  if (rhs->vp_type != da->type) {
700  fr_strerror_const("Incompatible data types");
701  return -1;
702  }
703 
704  if (rhs == vp) {
705  fr_strerror_const("Invalid self-reference");
706  return -1;
707  }
708 
709  box = &rhs->data;
710 
711  } else if (tmpl_is_xlat(map->rhs)) {
712  if (tmpl_aexpand(ctx, &to_free, request, map->rhs, NULL, NULL) < 0) return -1;
713 
714  box = to_free;
715 
716  } else {
717  fr_strerror_const("Unknown RHS");
718  return -1;
719  }
720 
721  /*
722  * We added a VP which hadn't previously existed. Therefore just set the value and return.
723  */
724  if (added) {
725  if (fr_value_box_cast(vp, &vp->data, vp->vp_type, vp->da, box) < 0) {
726  fail:
727  TALLOC_FREE(to_free);
728  return -1;
729  }
730 
731  TALLOC_FREE(to_free);
732  return 0;
733  }
734 
735  while (vp) {
736  next = fr_pair_find_by_da_nested(list, vp, da); /* could be deleted in the loop*/
737 
738  switch (map->op) {
739  case T_OP_LE: /* replace if not <= */
740  case T_OP_GE: /* replace if not >= */
741  rcode = fr_value_box_cmp_op(map->op, &vp->data, box);
742  if (rcode < 0) goto fail;
743 
744  if (rcode != 0) break;
745 
746  if (fr_value_box_cast(vp, &vp->data, vp->vp_type, vp->da, box) < 0) goto fail;
747  break;
748 
749  case T_OP_SUB_EQ: /* delete if match */
750  rcode = fr_value_box_cmp_op(T_OP_CMP_EQ, &vp->data, box);
751  if (rcode < 0) goto fail;
752 
753  if (rcode == 1) {
754  fr_pair_list_t *parent_list = fr_pair_parent_list(vp);
755 
756  if (fr_edit_list_pair_delete(el, parent_list, vp) < 0) goto fail;
757  }
758  break;
759 
760  default:
761  fr_assert(0); /* should have been caught above */
762  return -1;
763  }
764 
765  vp = next;
766  }
767 
768  TALLOC_FREE(to_free);
769  return 0;
770 }
771 
772 int radius_legacy_map_list_apply(request_t *request, map_list_t const *list, fr_edit_list_t *el)
773 {
774  map_t const *map;
775 
776  for (map = map_list_head(list);
777  map != NULL;
778  map = map_list_next(list, map)) {
779  RDEBUG2("&%s %s %s", map->lhs->name, fr_tokens[map->op],
780  map->rhs ? map->rhs->name : "{ ... }");
781 
782  if (radius_legacy_map_apply(request, map, el) < 0) {
783  RPEDEBUG("Failed applying result");
784  return -1;
785  }
786  }
787 
788  return 0;
789 }
790 
791 int radius_legacy_map_cmp(request_t *request, map_t const *map)
792 {
793  int rcode;
794  fr_pair_t *vp;
795  fr_value_box_t const *box;
796  fr_value_box_t *to_free = NULL;
797  fr_value_box_t dst, str;
798  fr_dcursor_t cursor;
800 
801  fr_assert(tmpl_is_attr(map->lhs));
803 
804  vp = tmpl_dcursor_init(NULL, request, &cc, &cursor, request, map->lhs);
805 
806  if (!vp) {
807  tmpl_dcursor_clear(&cc);
808  if (map->op == T_OP_CMP_FALSE) return true;
809  return 0;
810  }
811 
812  if (map->op == T_OP_CMP_TRUE){
813  tmpl_dcursor_clear(&cc);
814  return false;
815  }
816 
817  if (fr_type_is_structural(vp->vp_type)) {
818  fr_strerror_const("Invalid comparison for structural type");
819  error:
820  tmpl_dcursor_clear(&cc);
821  return -1;
822  }
823 
824  if (tmpl_is_data(map->rhs)) {
825  box = tmpl_value(map->rhs);
826 
827  } else if (tmpl_is_attr(map->rhs)) {
828  fr_pair_t *rhs;
829 
830  if (tmpl_find_vp(&rhs, request, map->rhs) < 0) goto error;
831 
832  box = &rhs->data;
833 
834  } else if (tmpl_contains_xlat(map->rhs)) {
835  if (tmpl_aexpand(request, &to_free, request, map->rhs, NULL, NULL) < 0) goto error;
836 
837  box = to_free;
838 
839  } else if (tmpl_is_regex(map->rhs)) {
840  /*
841  * @todo - why box it and parse it again, when we can just run the regex?
842  */
843  fr_value_box_strdup_shallow(&str, NULL, map->rhs->name, false);
844  box = &str;
845 
846  } else {
847  fr_strerror_const("Unknown RHS");
848  goto error;
849  }
850 
851  /*
852  * Check all possible vps matching the lhs
853  * Allows for comparisons such as &foo[*] == "bar" - i.e. true if any instance of &foo has the value "bar"
854  */
855  rcode = 0;
856  while (vp) {
857  /*
858  * Let the calculation code do upcasting as necessary.
859  */
860  rcode = fr_value_calc_binary_op(request, &dst, FR_TYPE_BOOL, &vp->data, map->op, box);
861  if ((rcode >= 0) && dst.vb_bool) break; // Found a "true" result, no need to check any further
862  vp = fr_dcursor_next(&cursor);
863  }
864  TALLOC_FREE(to_free);
865  tmpl_dcursor_clear(&cc);
866 
867  if (rcode < 0) return rcode;
868 
869  return dst.vb_bool;
870 }
#define RCSID(id)
Definition: build.h:444
#define FALL_THROUGH
clang 10 doesn't recognised the FALL-THROUGH comment anymore
Definition: build.h:320
int fr_value_calc_binary_op(TALLOC_CTX *ctx, fr_value_box_t *dst, fr_type_t hint, fr_value_box_t const *a, fr_token_t op, fr_value_box_t const *b)
Calculate DST = A OP B.
Definition: calc.c:1893
static void * fr_dcursor_set_current(fr_dcursor_t *cursor, void *item)
Set the cursor to a specified item.
Definition: dcursor.h:352
static void * fr_dcursor_next(fr_dcursor_t *cursor)
Advanced the cursor to the next item.
Definition: dcursor.h:287
static fr_slen_t err
Definition: dict.h:645
#define RWDEBUG(fmt,...)
Definition: log.h:361
#define RPEDEBUG(fmt,...)
Definition: log.h:376
#define RDEBUG4(fmt,...)
Definition: log.h:344
talloc_free(reap)
@ FR_TYPE_STRING
String of printable characters.
Definition: merged_model.c:83
@ FR_TYPE_BOOL
A truth value.
Definition: merged_model.c:95
int fr_pair_append_by_da(TALLOC_CTX *ctx, fr_pair_t **out, fr_pair_list_t *list, fr_dict_attr_t const *da)
Alloc a new fr_pair_t (and append)
Definition: pair.c:1461
fr_pair_t * fr_pair_parent(fr_pair_t const *vp)
Return a pointer to the parent pair.
Definition: pair.c:937
fr_pair_t * fr_pair_afrom_da(TALLOC_CTX *ctx, fr_dict_attr_t const *da)
Dynamically allocate a new attribute and assign a fr_dict_attr_t.
Definition: pair.c:278
int fr_pair_list_copy(TALLOC_CTX *ctx, fr_pair_list_t *to, fr_pair_list_t const *from)
Duplicate a list of pairs.
Definition: pair.c:2316
int fr_pair_append(fr_pair_list_t *list, fr_pair_t *to_add)
Add a VP to the end of the list.
Definition: pair.c:1340
void fr_pair_replace(fr_pair_list_t *list, fr_pair_t *to_replace, fr_pair_t *vp)
Replace a given VP.
Definition: pair.c:1434
void fr_pair_list_init(fr_pair_list_t *list)
Initialise a pair list header.
Definition: pair.c:46
fr_pair_list_t * fr_pair_parent_list(fr_pair_t const *vp)
Return a pointer to the parent pair list.
Definition: pair.c:922
fr_pair_t * fr_pair_find_by_da_nested(fr_pair_list_t const *list, fr_pair_t const *prev, fr_dict_attr_t const *da)
Find a pair with a matching fr_dict_attr_t, by walking the nested fr_dict_attr_t tree.
Definition: pair.c:765
int fr_pair_prepend(fr_pair_list_t *list, fr_pair_t *to_add)
Add a VP to the start of the list.
Definition: pair.c:1309
fr_slen_t fr_pair_list_afrom_substr(fr_pair_parse_t const *root, fr_pair_parse_t *relative, fr_sbuff_t *in)
Parse a fr_pair_list_t from a substring.
Definition: pair_legacy.c:150
struct fr_pair_parse_s fr_pair_parse_t
TALLOC_CTX * ctx
Definition: pair_legacy.h:43
int paircmp_pairs(UNUSED request_t *request, fr_pair_t const *check, fr_pair_t *vp)
Compares check and vp by value.
Definition: paircmp.c:56
static int radius_legacy_map_to_vp(request_t *request, fr_pair_t *parent, map_t const *map)
Definition: pairmove.c:315
void radius_pairmove(request_t *request, fr_pair_list_t *to, fr_pair_list_t *from)
Definition: pairmove.c:46
int radius_legacy_map_cmp(request_t *request, map_t const *map)
Definition: pairmove.c:791
fr_pair_t * vp
Definition: pairmove.c:464
static fr_pair_t * legacy_pair_build(fr_pair_t *parent, fr_dcursor_t *cursor, fr_dict_attr_t const *da, void *uctx)
Build the relevant pairs at each level.
Definition: pairmove.c:471
int radius_legacy_map_apply(request_t *request, map_t const *map, fr_edit_list_t *el)
Move a map using the operators from the old pairmove functionality.
Definition: pairmove.c:507
static int radius_legacy_map_apply_structural(request_t *request, map_t const *map, fr_pair_t *vp)
Definition: pairmove.c:363
int radius_legacy_map_list_apply(request_t *request, map_list_t const *list, fr_edit_list_t *el)
Definition: pairmove.c:772
fr_edit_list_t * el
Definition: pairmove.c:463
#define RDEBUG2(fmt,...)
Definition: radclient.h:54
#define RDEBUG(fmt,...)
Definition: radclient.h:53
fr_dict_attr_t const * request_attr_request
Definition: request.c:41
fr_dict_attr_t const * request_attr_control
Definition: request.c:43
fr_dict_attr_t const * request_attr_state
Definition: request.c:44
fr_dict_attr_t const * request_attr_reply
Definition: request.c:42
#define FR_SBUFF_IN(_start, _len_or_end)
int tmpl_find_vp(fr_pair_t **out, request_t *request, tmpl_t const *vpt))
Returns the first VP matching a tmpl_t.
Definition: tmpl_eval.c:887
static int16_t tmpl_attr_tail_num(tmpl_t const *vpt)
Return the last attribute reference's attribute number.
Definition: tmpl.h:880
#define tmpl_contains_xlat(vpt)
Definition: tmpl.h:231
#define tmpl_is_xlat(vpt)
Definition: tmpl.h:215
#define NUM_LAST
Definition: tmpl.h:402
#define tmpl_value(_tmpl)
Definition: tmpl.h:932
static fr_dict_attr_t const * tmpl_attr_tail_da(tmpl_t const *vpt)
Return the last attribute reference da.
Definition: tmpl.h:796
#define tmpl_is_attr(vpt)
Definition: tmpl.h:213
#define NUM_COUNT
Definition: tmpl.h:401
#define tmpl_pair_list_and_ctx(_ctx, _head, _request, _ref, _list)
Determine the correct context and list head.
Definition: tmpl.h:985
#define tmpl_is_data(vpt)
Definition: tmpl.h:211
#define tmpl_is_regex(vpt)
Definition: tmpl.h:218
static fr_dict_attr_t const * tmpl_list(tmpl_t const *vpt)
Definition: tmpl.h:899
#define tmpl_aexpand(_ctx, _out, _request, _vpt, _escape, _escape_ctx)
Expand a tmpl to a C type, allocing a new buffer to hold the string.
Definition: tmpl.h:1054
return count
Definition: module.c:175
fr_assert(0)
MEM(pair_append_request(&vp, attr_eap_aka_sim_identity) >=0)
fr_pair_t * vp
Value pair map.
Definition: map.h:77
fr_token_t op
The operator that controls insertion of the dst attribute.
Definition: map.h:82
tmpl_t * lhs
Typically describes the attribute to add, modify or compare.
Definition: map.h:78
map_list_t child
parent map, for nested ones
Definition: map.h:89
tmpl_t * rhs
Typically describes a literal value or a src attribute to copy or compare.
Definition: map.h:79
Stores an attribute, a value and various bits of other data.
Definition: pair.h:68
fr_dict_attr_t const *_CONST da
Dictionary attribute defines the attribute number, vendor and type of the pair.
Definition: pair.h:69
void tmpl_dcursor_clear(tmpl_dcursor_ctx_t *cc)
Clear any temporary state allocations.
Definition: tmpl_dcursor.c:419
#define tmpl_dcursor_build_init(_err, _ctx, _cc, _cursor, _request, _vpt, _build, _uctx)
Definition: tmpl_dcursor.h:102
#define tmpl_dcursor_init(_err, _ctx, _cc, _cursor, _request, _vpt)
Definition: tmpl_dcursor.h:99
Maintains state between cursor calls.
Definition: tmpl_dcursor.h:61
char const * fr_tokens[T_TOKEN_LAST]
Definition: token.c:78
const bool fr_comparison_op[T_TOKEN_LAST]
Definition: token.c:198
@ T_OP_SUB_EQ
Definition: token.h:70
@ T_OP_CMP_TRUE
Definition: token.h:104
@ T_OP_EQ
Definition: token.h:83
@ T_OP_SET
Definition: token.h:84
@ T_OP_ADD_EQ
Definition: token.h:69
@ T_OP_CMP_FALSE
Definition: token.h:105
@ T_OP_CMP_EQ
Definition: token.h:106
@ T_OP_LE
Definition: token.h:100
@ T_OP_GE
Definition: token.h:98
@ T_OP_PREPEND
Definition: token.h:85
static fr_event_list_t * el
int fr_edit_list_pair_delete(fr_edit_list_t *el, fr_pair_list_t *list, fr_pair_t *vp)
Delete a VP.
Definition: edit.c:575
int fr_edit_list_save_pair_value(fr_edit_list_t *el, fr_pair_t *vp)
Record the value of a leaf fr_value_box_t.
Definition: edit.c:622
int fr_edit_list_free_pair_children(fr_edit_list_t *el, fr_pair_t *vp)
Free children of a structural pair.
Definition: edit.c:709
Track a series of edits.
Definition: edit.c:102
#define fr_edit_list_insert_pair_tail(_el, _list, _vp)
Definition: edit.h:51
fr_pair_t * fr_pair_list_head(fr_pair_list_t const *list)
Get the head of a valuepair list.
Definition: pair_inline.c:43
fr_pair_t * fr_pair_remove(fr_pair_list_t *list, fr_pair_t *vp)
Remove fr_pair_t from a list without freeing.
Definition: pair_inline.c:94
fr_pair_t * fr_pair_list_next(fr_pair_list_t const *list, fr_pair_t const *item))
Get the next item in a valuepair list after a specific entry.
Definition: pair_inline.c:70
void fr_pair_list_free(fr_pair_list_t *list)
Free memory used by a valuepair list.
Definition: pair_inline.c:113
void fr_pair_list_append(fr_pair_list_t *dst, fr_pair_list_t *src)
Appends a list of fr_pair_t from a temporary list to a destination list.
Definition: pair_inline.c:182
void fr_pair_list_prepend(fr_pair_list_t *dst, fr_pair_list_t *src)
Move a list of fr_pair_t from a temporary list to the head of a destination list.
Definition: pair_inline.c:195
size_t fr_pair_list_num_elements(fr_pair_list_t const *list)
Get the length of a list of fr_pair_t.
Definition: pair_inline.c:151
static fr_slen_t parent
Definition: pair.h:844
void fr_strerror_clear(void)
Clears all pending messages from the talloc pools.
Definition: strerror.c:577
#define fr_strerror_printf(_fmt,...)
Log to thread local error buffer.
Definition: strerror.h:64
#define fr_strerror_const(_msg)
Definition: strerror.h:223
static char const * fr_type_to_str(fr_type_t type)
Return a static string containing the type name.
Definition: types.h:433
#define fr_type_is_structural(_x)
Definition: types.h:371
#define fr_type_is_leaf(_x)
Definition: types.h:372
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_cmp_op(fr_token_t op, fr_value_box_t const *a, fr_value_box_t const *b)
Compare two attributes using an operator.
Definition: value.c:884
void fr_value_box_strdup_shallow(fr_value_box_t *dst, fr_dict_attr_t const *enumv, char const *src, bool tainted)
Assign a buffer containing a nul terminated string to a box, but don't copy it.
Definition: value.c:3985
int nonnull(2, 5))