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: 4b6b4dcee4cba7ae35b8b48c7dc2d3fab014b9bd $
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: 4b6b4dcee4cba7ae35b8b48c7dc2d3fab014b9bd $")
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 {
566
567 if (fr_edit_list_free_pair_children(el, vp) < 0) return -1;
568 }
569 break;
570 }
571
572 /*
573 * We don't delete the main lists, we just modify their contents.
574 */
575 if ((da == request_attr_request) ||
576 (da == request_attr_reply) ||
577 (da == request_attr_control) ||
578 (da == request_attr_state)) {
579 fr_assert(vp != NULL);
580
581 if (fr_edit_list_free_pair_children(el, vp) < 0) return -1;
582 break;
583 }
584
585 if (!vp) goto add;
586
587 /*
588 * Delete the first attribute we found.
589 */
591 fr_assert(parent != NULL);
592
593 if (fr_edit_list_pair_delete(el, &parent->vp_group, vp) < 0) return -1;
595
596 /*
597 * Delete all existing attributes. Note that we re-initialize the cursor every time,
598 * because creating "foo := baz" means deleting ALL existing "foo". But we can't use
599 * the tmpl as a cursor, because the tmpl containst NUM_UNSPEC, and the cursor needs
600 * NUM_ALL. So we have to delete all existing attributes, and then add a new one.
601 */
602 while (true) {
603 vp = tmpl_dcursor_init(&err, ctx, &cc, &cursor, request, map->lhs);
604 if (!vp) break;
605
607 fr_assert(parent != NULL);
608
609 if (fr_edit_list_pair_delete(el, &parent->vp_group, vp) < 0) return -1;
611 }
613
614 case T_OP_ADD_EQ:
615 add:
616 {
618 .el = el,
619 .vp = NULL,
620 };
621
623 vp = tmpl_dcursor_build_init(&err, ctx, &cc, &cursor, request, map->lhs, legacy_pair_build, &lp);
625 if (!vp) {
626 RWDEBUG("Failed creating attribute %s", map->lhs->name);
627 return -1;
628 }
629
630 /*
631 * If we're adding and one already exists, create a new one in the same context.
632 */
633 if ((map->op == T_OP_ADD_EQ) && !lp.vp) {
635 fr_assert(parent != NULL);
636
638 if (fr_edit_list_insert_pair_tail(el, &parent->vp_group, vp) < 0) return -1;
639 }
640
641 added = true;
642 }
643 break;
644
645 case T_OP_LE: /* replace if not <= */
646 case T_OP_GE: /* replace if not >= */
647 if (fr_type_is_structural(da->type)) goto invalid_operator;
648
649 if (tmpl_find_vp(&vp, request, map->lhs) < -1) return -1;
650 if (!vp) goto add;
651 break;
652
653 case T_OP_SUB_EQ: /* delete if match, otherwise ignore */
654 if (fr_type_is_structural(da->type)) {
655 invalid_operator:
656 fr_strerror_printf("Invalid operator '%s' for structural type", fr_type_to_str(da->type));
657 return -1;
658 }
659
660 if (tmpl_find_vp(&vp, request, map->lhs) < -1) return -1;
661 if (!vp) return 0;
662 break;
663
664 default:
665 fr_strerror_printf("Invalid operator '%s'", fr_tokens[map->op]);
666 return -1;
667 }
668
669 fr_assert(vp);
670
671 /*
672 * We don't support operations on structural types. Just creation, and assign values.
673 *
674 * The code above has ensured that the structural type has been either saved or cleared via the
675 * edit list, so the next function doesn't need to do that.
676 */
678 fr_assert(added);
679 return radius_legacy_map_apply_structural(request, map, vp);
680 }
681
682 /*
683 * We have now found the RHS. Expand it.
684 *
685 * Note that
686 *
687 * &foo := %tolower(&foo)
688 *
689 * works, as we save the value above in the T_OP_SET handler. So we don't delete it.
690 */
691 if (tmpl_is_data(map->rhs)) {
692 box = tmpl_value(map->rhs);
693
694 } else if (tmpl_is_attr(map->rhs)) {
695 fr_pair_t *rhs;
696
697 if (tmpl_find_vp(&rhs, request, map->rhs) < 0) return -1;
698
699 if (rhs->vp_type != da->type) {
700 fr_strerror_const("Incompatible data types");
701 return -1;
702 }
703
704 if (rhs == vp) {
705 fr_strerror_const("Invalid self-reference");
706 return -1;
707 }
708
709 box = &rhs->data;
710
711 } else if (tmpl_is_xlat(map->rhs)) {
712 if (tmpl_aexpand(ctx, &to_free, request, map->rhs, NULL, NULL) < 0) return -1;
713
714 box = to_free;
715
716 } else {
717 fr_strerror_const("Unknown RHS");
718 return -1;
719 }
720
721 /*
722 * We added a VP which hadn't previously existed. Therefore just set the value and return.
723 */
724 if (added) {
725 if (fr_value_box_cast(vp, &vp->data, vp->vp_type, vp->da, box) < 0) {
726 fail:
727 TALLOC_FREE(to_free);
728 return -1;
729 }
730
731 if (vp->da->flags.unsafe) fr_value_box_mark_unsafe(&vp->data);
732 TALLOC_FREE(to_free);
733 return 0;
734 }
735
736 while (vp) {
737 next = fr_pair_find_by_da_nested(list, vp, da); /* could be deleted in the loop*/
738
739 switch (map->op) {
740 case T_OP_LE: /* replace if not <= */
741 case T_OP_GE: /* replace if not >= */
742 rcode = fr_value_box_cmp_op(map->op, &vp->data, box);
743 if (rcode < 0) goto fail;
744
745 if (rcode != 0) break;
746
747 if (fr_value_box_cast(vp, &vp->data, vp->vp_type, vp->da, box) < 0) goto fail;
748 break;
749
750 case T_OP_SUB_EQ: /* delete if match */
751 rcode = fr_value_box_cmp_op(T_OP_CMP_EQ, &vp->data, box);
752 if (rcode < 0) goto fail;
753
754 if (rcode == 1) {
755 fr_pair_list_t *parent_list = fr_pair_parent_list(vp);
756
757 if (fr_edit_list_pair_delete(el, parent_list, vp) < 0) goto fail;
758 }
759 break;
760
761 default:
762 fr_assert(0); /* should have been caught above */
763 return -1;
764 }
765
766 vp = next;
767 }
768
769 TALLOC_FREE(to_free);
770 return 0;
771}
772
773int radius_legacy_map_list_apply(request_t *request, map_list_t const *list, fr_edit_list_t *el)
774{
775 map_t const *map;
776
777 for (map = map_list_head(list);
778 map != NULL;
779 map = map_list_next(list, map)) {
780 RDEBUG2("%s %s %s", map->lhs->name, fr_tokens[map->op],
781 map->rhs ? map->rhs->name : "{ ... }");
782
783 if (radius_legacy_map_apply(request, map, el) < 0) {
784 RPEDEBUG("Failed applying result");
785 return -1;
786 }
787 }
788
789 return 0;
790}
791
792int radius_legacy_map_cmp(request_t *request, map_t const *map)
793{
794 int rcode;
795 fr_pair_t *vp;
796 fr_value_box_t const *box;
797 fr_value_box_t *to_free = NULL;
798 fr_value_box_t dst, str;
799 fr_dcursor_t cursor;
801
804
805 vp = tmpl_dcursor_init(NULL, request, &cc, &cursor, request, map->lhs);
806
807 if (!vp) {
809 if (map->op == T_OP_CMP_FALSE) return true;
810 return 0;
811 }
812
813 if (map->op == T_OP_CMP_TRUE){
815 return false;
816 }
817
818 if (fr_type_is_structural(vp->vp_type)) {
819 fr_strerror_const("Invalid comparison for structural type");
820 error:
822 return -1;
823 }
824
825 if (tmpl_is_data(map->rhs)) {
826 box = tmpl_value(map->rhs);
827
828 } else if (tmpl_is_attr(map->rhs)) {
829 fr_pair_t *rhs;
830
831 if (tmpl_find_vp(&rhs, request, map->rhs) < 0) goto error;
832
833 box = &rhs->data;
834
835 } else if (tmpl_contains_xlat(map->rhs)) {
836 if (tmpl_aexpand(request, &to_free, request, map->rhs, NULL, NULL) < 0) goto error;
837
838 box = to_free;
839
840 } else if (tmpl_is_regex(map->rhs)) {
841 /*
842 * @todo - why box it and parse it again, when we can just run the regex?
843 */
844 fr_value_box_strdup_shallow(&str, NULL, map->rhs->name, false);
845 box = &str;
846
847 } else {
848 fr_strerror_const("Unknown RHS");
849 goto error;
850 }
851
852 /*
853 * Check all possible vps matching the lhs
854 * Allows for comparisons such as &foo[*] == "bar" - i.e. true if any instance of &foo has the value "bar"
855 */
856 rcode = 0;
857 while (vp) {
858 /*
859 * Let the calculation code do upcasting as necessary.
860 */
861 rcode = fr_value_calc_binary_op(request, &dst, FR_TYPE_BOOL, &vp->data, map->op, box);
862 if ((rcode >= 0) && dst.vb_bool) break; // Found a "true" result, no need to check any further
863 vp = fr_dcursor_next(&cursor);
864 }
865 TALLOC_FREE(to_free);
867
868 if (rcode < 0) return rcode;
869
870 return dst.vb_bool;
871}
#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:840
#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:1472
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:2329
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:774
fr_pair_list_t * fr_pair_parent_list(fr_pair_t const *vp)
Return a pointer to the parent pair list.
Definition pair.c:931
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:1351
fr_pair_t * fr_pair_parent(fr_pair_t const *vp)
Return a pointer to the parent pair.
Definition pair.c:946
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:1445
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:287
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:1320
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:792
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:773
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:768
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: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: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:388
#define fr_type_is_leaf(_x)
Definition types.h:389
static char const * fr_type_to_str(fr_type_t type)
Return a static string containing the type name.
Definition types.h:450
void fr_value_box_mark_unsafe(fr_value_box_t *vb)
Mark a value-box as "unsafe".
Definition value.c:6497
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:4267
int nonnull(2, 5))