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