The FreeRADIUS server $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
Loading...
Searching...
No Matches
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 */
26RCSID("$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
314static 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
362static 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
461typedef 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;
473 legacy_pair_build_t *lp = uctx;
474
476 if (!vp) return NULL;
477
478 if (fr_edit_list_insert_pair_tail(lp->el, &parent->vp_group, vp) < 0) {
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;
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;
610 }
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);
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
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 */
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
771int 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
790int 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
802
803 vp = tmpl_dcursor_init(NULL, request, &cc, &cursor, request, map->lhs);
804
805 if (!vp) {
807 if (map->op == T_OP_CMP_FALSE) return true;
808 return 0;
809 }
810
811 if (map->op == T_OP_CMP_TRUE){
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:
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);
865
866 if (rcode < 0) return rcode;
867
868 return dst.vb_bool;
869}
#define RCSID(id)
Definition build.h:483
#define FALL_THROUGH
clang 10 doesn't recognised the FALL-THROUGH comment anymore
Definition build.h:322
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_next(fr_dcursor_t *cursor)
Advanced the cursor to the next item.
Definition dcursor.h:288
static void * fr_dcursor_set_current(fr_dcursor_t *cursor, void *item)
Set the cursor to a specified item.
Definition dcursor.h:353
#define MEM(x)
Definition debug.h:36
static fr_slen_t err
Definition dict.h:824
#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.
@ FR_TYPE_BOOL
A truth value.
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
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
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
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
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
fr_pair_t * fr_pair_parent(fr_pair_t const *vp)
Return a pointer to the parent pair.
Definition pair.c:942
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
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
void fr_pair_list_init(fr_pair_list_t *list)
Initialise a pair list header.
Definition pair.c:46
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.
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
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
fr_pair_t * vp
Definition pairmove.c:463
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 fr_assert(_expr)
Definition rad_assert.h:38
#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
#define tmpl_is_attr(vpt)
Definition tmpl.h:213
static fr_dict_attr_t const * tmpl_list(tmpl_t const *vpt)
Definition tmpl.h:915
#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
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_regex(vpt)
Definition tmpl.h:218
#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_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.
#define tmpl_dcursor_build_init(_err, _ctx, _cc, _cursor, _request, _vpt, _build, _uctx)
#define tmpl_dcursor_init(_err, _ctx, _cc, _cursor, _request, _vpt)
Maintains state between cursor calls.
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_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
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
void fr_pair_list_free(fr_pair_list_t *list)
Free memory used by a valuepair list.
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.
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.
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
size_t fr_pair_list_num_elements(fr_pair_list_t const *list)
Get the length of a list of fr_pair_t.
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
#define fr_type_is_structural(_x)
Definition types.h:371
#define fr_type_is_leaf(_x)
Definition types.h:372
static char const * fr_type_to_str(fr_type_t type)
Return a static string containing the type name.
Definition types.h:433
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))