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: 0a9b4cc39b9d5d970bed3c2e75bca9ab0a85d815 $
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: 0a9b4cc39b9d5d970bed3c2e75bca9ab0a85d815 $")
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 .allow_compare = false,
445 .tainted = box->tainted,
446 };
447 relative = (fr_pair_parse_t) { };
448
449 if (fr_pair_list_afrom_substr(&root, &relative, &FR_SBUFF_IN(box->vb_strvalue, box->vb_length)) < 0) {
450 RPEDEBUG("Failed parsing string '%pV' as attribute list", box);
451 TALLOC_FREE(to_free);
452 return -1;
453 }
454 }
455
456 TALLOC_FREE(to_free);
457 return 0;
458}
459
460typedef struct {
462 fr_pair_t *vp; /* the one we created */
464
465/** Build the relevant pairs at each level.
466 *
467 * See edit_list_pair_build() for similar code.
468 */
470{
471 fr_pair_t *vp;
472 legacy_pair_build_t *lp = uctx;
473
475 if (!vp) return NULL;
476
477 if (fr_edit_list_insert_pair_tail(lp->el, &parent->vp_group, vp) < 0) {
479 return NULL;
480 }
481
482 /*
483 * Tell the cursor that we appended a pair. This
484 * function only gets called when we've ran off of the
485 * end of the list, and can't find the thing we're
486 * looking for. So it's safe at set the current one
487 * here.
488 *
489 * @todo - mainly only because we don't allow creating
490 * foo[4] when there's <3 matching entries. i.e. the
491 * "arrays" here are really lists, so we can't create
492 * "holes" in the list.
493 */
494 fr_dcursor_set_current(cursor, vp);
495
496 lp->vp = vp;
497
498 return vp;
499}
500
501
502/** Move a map using the operators from the old pairmove functionality.
503 *
504 */
506{
507 int16_t num;
508 int err, rcode;
509 bool added = false;
510 fr_pair_t *vp = NULL, *next, *parent;
511 fr_dict_attr_t const *da;
512 fr_pair_list_t *list;
513 TALLOC_CTX *ctx;
514 fr_value_box_t *to_free = NULL;
515 fr_value_box_t const *box;
517 fr_dcursor_t cursor;
518
519 /*
520 * Find out where this attribute exists, or should exist.
521 */
522 tmpl_pair_list_and_ctx(ctx, list, request, tmpl_request(map->lhs), tmpl_list(map->lhs));
523 if (!ctx) return -1; /* no request or list head exists */
524
525 da = tmpl_attr_tail_da(map->lhs);
526
527 /*
528 * These operations are the same for both leaf and structural types.
529 */
530 switch (map->op) {
531 case T_OP_EQ:
532 if (tmpl_find_vp(&vp, request, map->lhs) < -1) return -1;
533 if (vp) return 0;
534 goto add;
535
536 case T_OP_SET:
537 /*
538 * Set a value. Note that we might do
539 *
540 * &foo[1] := 1
541 *
542 * In which case we don't want to delete the attribute, we just want to replace its
543 * value.
544 *
545 * @todo - we can't do &foo[*].bar[*].baz = 1, as that's an implicit cursor, and we don't
546 * do that.
547 */
548 num = tmpl_attr_tail_num(map->lhs);
549 if (num == NUM_COUNT) {
550 fr_strerror_const("Invalid count in attribute reference");
551 return -1;
552 }
553
554 vp = tmpl_dcursor_init(&err, ctx, &cc, &cursor, request, map->lhs);
555
556 /*
557 * We're editing a specific number. It must exist, otherwise the edit does nothing.
558 */
559 if ((num >= 0) || (num == NUM_LAST)) {
560 if (!vp) return 0;
561
562 if (fr_type_is_leaf(vp->vp_type)) {
563 if (fr_edit_list_save_pair_value(el, vp) < 0) return -1;
564 } else {
565 if (fr_edit_list_free_pair_children(el, vp) < 0) return -1;
566 }
567 break;
568 }
569
570 /*
571 * We don't delete the main lists, we just modify their contents.
572 */
573 if ((da == request_attr_request) ||
574 (da == request_attr_reply) ||
575 (da == request_attr_control) ||
576 (da == request_attr_state)) {
577 fr_assert(vp != NULL);
578
579 if (fr_edit_list_free_pair_children(el, vp) < 0) return -1;
580 break;
581 }
582
583 if (!vp) goto add;
584
585 /*
586 * Delete the first attribute we found.
587 */
589 fr_assert(parent != NULL);
590
591 if (fr_edit_list_pair_delete(el, &parent->vp_group, vp) < 0) return -1;
593
594 /*
595 * Delete all existing attributes. Note that we re-initialize the cursor every time,
596 * because creating "foo := baz" means deleting ALL existing "foo". But we can't use
597 * the tmpl as a cursor, because the tmpl containst NUM_UNSPEC, and the cursor needs
598 * NUM_ALL. So we have to delete all existing attributes, and then add a new one.
599 */
600 while (true) {
601 vp = tmpl_dcursor_init(&err, ctx, &cc, &cursor, request, map->lhs);
602 if (!vp) break;
603
605 fr_assert(parent != NULL);
606
607 if (fr_edit_list_pair_delete(el, &parent->vp_group, vp) < 0) return -1;
609 }
611
612 case T_OP_ADD_EQ:
613 add:
614 {
616 .el = el,
617 .vp = NULL,
618 };
619
621 vp = tmpl_dcursor_build_init(&err, ctx, &cc, &cursor, request, map->lhs, legacy_pair_build, &lp);
623 if (!vp) {
624 RWDEBUG("Failed creating attribute %s", map->lhs->name);
625 return -1;
626 }
627
628 /*
629 * If we're adding and one already exists, create a new one in the same context.
630 */
631 if ((map->op == T_OP_ADD_EQ) && !lp.vp) {
633 fr_assert(parent != NULL);
634
636 if (fr_edit_list_insert_pair_tail(el, &parent->vp_group, vp) < 0) return -1;
637 }
638
639 added = true;
640 }
641 break;
642
643 case T_OP_LE: /* replace if not <= */
644 case T_OP_GE: /* replace if not >= */
645 if (fr_type_is_structural(da->type)) goto invalid_operator;
646
647 if (tmpl_find_vp(&vp, request, map->lhs) < -1) return -1;
648 if (!vp) goto add;
649 break;
650
651 case T_OP_SUB_EQ: /* delete if match, otherwise ignore */
652 if (fr_type_is_structural(da->type)) {
653 invalid_operator:
654 fr_strerror_printf("Invalid operator '%s' for structural type", fr_type_to_str(da->type));
655 return -1;
656 }
657
658 if (tmpl_find_vp(&vp, request, map->lhs) < -1) return -1;
659 if (!vp) return 0;
660 break;
661
662 default:
663 fr_strerror_printf("Invalid operator '%s'", fr_tokens[map->op]);
664 return -1;
665 }
666
667 fr_assert(vp);
668
669 /*
670 * We don't support operations on structural types. Just creation, and assign values.
671 *
672 * The code above has ensured that the structural type has been either saved or cleared via the
673 * edit list, so the next function doesn't need to do that.
674 */
676 fr_assert(added);
677 return radius_legacy_map_apply_structural(request, map, vp);
678 }
679
680 /*
681 * We have now found the RHS. Expand it.
682 *
683 * Note that
684 *
685 * &foo := %tolower(&foo)
686 *
687 * works, as we save the value above in the T_OP_SET handler. So we don't delete it.
688 */
689 if (tmpl_is_data(map->rhs)) {
690 box = tmpl_value(map->rhs);
691
692 } else if (tmpl_is_attr(map->rhs)) {
693 fr_pair_t *rhs;
694
695 if (tmpl_find_vp(&rhs, request, map->rhs) < 0) return -1;
696
697 if (rhs->vp_type != da->type) {
698 fr_strerror_const("Incompatible data types");
699 return -1;
700 }
701
702 if (rhs == vp) {
703 fr_strerror_const("Invalid self-reference");
704 return -1;
705 }
706
707 box = &rhs->data;
708
709 } else if (tmpl_is_xlat(map->rhs)) {
710 if (tmpl_aexpand(ctx, &to_free, request, map->rhs, NULL, NULL) < 0) return -1;
711
712 box = to_free;
713
714 } else {
715 fr_strerror_const("Unknown RHS");
716 return -1;
717 }
718
719 /*
720 * We added a VP which hadn't previously existed. Therefore just set the value and return.
721 */
722 if (added) {
723 if (fr_value_box_cast(vp, &vp->data, vp->vp_type, vp->da, box) < 0) {
724 fail:
725 TALLOC_FREE(to_free);
726 return -1;
727 }
728
729 if (vp->da->flags.unsafe) fr_value_box_mark_unsafe(&vp->data);
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: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: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:833
#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:1468
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:2321
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:772
fr_pair_list_t * fr_pair_parent_list(fr_pair_t const *vp)
Return a pointer to the parent pair list.
Definition pair.c:929
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:1347
fr_pair_t * fr_pair_parent(fr_pair_t const *vp)
Return a pointer to the parent pair.
Definition pair.c:944
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:1441
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:285
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:1316
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: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:469
fr_pair_t * vp
Definition pairmove.c:462
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:505
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:771
fr_edit_list_t * el
Definition pairmove.c:461
#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:879
static int16_t tmpl_attr_tail_num(tmpl_t const *vpt)
Return the last attribute reference's attribute number.
Definition tmpl.h:885
#define tmpl_contains_xlat(vpt)
Definition tmpl.h:227
#define tmpl_is_xlat(vpt)
Definition tmpl.h:210
#define NUM_LAST
Definition tmpl.h:393
#define tmpl_value(_tmpl)
Definition tmpl.h:937
#define tmpl_is_attr(vpt)
Definition tmpl.h:208
static fr_dict_attr_t const * tmpl_list(tmpl_t const *vpt)
Definition tmpl.h:904
#define NUM_COUNT
Definition tmpl.h:392
#define tmpl_pair_list_and_ctx(_ctx, _head, _request, _ref, _list)
Determine the correct context and list head.
Definition tmpl.h:993
#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:801
#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:1062
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:77
const bool fr_comparison_op[T_TOKEN_LAST]
Definition token.c:197
@ 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:845
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: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
void fr_value_box_mark_unsafe(fr_value_box_t *vb)
Mark a value-box as "unsafe".
Definition value.c:6484
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:3574
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:975
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:4270
int nonnull(2, 5))