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