The FreeRADIUS server $Id: a4e7f8c94e8038b7627bcf52356b7ed15955c1d7 $
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: df1b35e839bb2733cead6c359bd5ec28eb75f095 $
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: df1b35e839bb2733cead6c359bd5ec28eb75f095 $")
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/** Find the next unedited pair with a matching attribute
40 *
41 * This is like fr_pair_find_by_da(), but it also looks at the "edited" array
42 * which radius_pairmove() builds from the "to" list.
43 *
44 * Entry "i" of "edited" is what the pair at position "i" of the list has
45 * become. It is the same as the pair when nothing has changed the pair. If
46 * it differs, then the pair has been replaced (or deleted, if the entry is
47 * NULL), and the function skips the pair. Operators which come later in the
48 * "from" list therefore don't see the pairs changed by earlier operators.
49 *
50 * The search starts at the pair "*vp_p", which is at position "*index_p" of
51 * the list. On a match, the function updates both to point to the matching
52 * pair. To find every match, move past the current match before calling the
53 * function again:
54 *
55 * vp = fr_pair_list_head(to);
56 * i = 0;
57 * while (pair_find_next_by_da(to, &vp, &i, edited, da)) {
58 * ...
59 * vp = fr_pair_list_next(to, vp);
60 * i++;
61 * }
62 *
63 * @param[in] list the "to" list.
64 * @param[in,out] vp_p the pair to start at. Set to the matching pair,
65 * or to NULL if there are no more matches.
66 * @param[in,out] index_p the position of "*vp_p" in the list. Set to the
67 * position of the matching pair.
68 * @param[in] edited what each pair of the list has become.
69 * @param[in] da to match.
70 * @return
71 * - true if there is a matching pair which has not been edited.
72 * - false if there is no such pair.
73 */
74static bool pair_find_next_by_da(fr_pair_list_t const *list, fr_pair_t **vp_p, size_t *index_p,
75 fr_pair_t * const *edited, fr_dict_attr_t const *da)
76{
77 size_t i = *index_p;
79
80 for (vp = *vp_p; vp != NULL; vp = fr_pair_list_next(list, vp)) {
81 if ((edited[i] == vp) && (vp->da == da)) {
82 *vp_p = vp;
83 *index_p = i;
84 return true;
85 }
86 i++;
87 }
88
89 *vp_p = NULL;
90 *index_p = i;
91 return false;
92}
93
94/** Find the first unedited pair with a matching attribute
95 *
96 * @param[out] index_p the position of the matching pair.
97 * @param[in] list the "to" list.
98 * @param[in] edited what each pair of the list has become.
99 * @param[in] da to match.
100 * @return
101 * - true if there is a matching pair which has not been edited.
102 * - false if there is no such pair.
103 */
104static bool pair_find_by_da(size_t *index_p, fr_pair_list_t const *list, fr_pair_t * const *edited,
105 fr_dict_attr_t const *da)
106{
108
109 *index_p = 0;
110
111 return pair_find_next_by_da(list, &vp, index_p, edited, da);
112}
113
114/** Update the "to" list with the changes which radius_pairmove() has made
115 *
116 * This walks down the "to" list, and the "edited" array, in parallel.
117 * Entry "i" of the array corresponds to the pair at position "i" of the
118 * list, as the list was when radius_pairmove() created the array.
119 *
120 * - If the entry is the same as the pair, then the pair is left alone.
121 * - If the entry is NULL, then the pair is removed from the list, and freed.
122 * - Otherwise the entry has replaced the pair. The pair is removed from the
123 * list and freed, and the entry takes its place.
124 *
125 * @param[in,out] to list to update.
126 * @param[in] edited what each pair of the list has become.
127 */
128static void pair_relink(fr_pair_list_t *to, fr_pair_t * const *edited)
129{
130 size_t i = 0;
131 fr_pair_t *vp, *next;
132
133 for (vp = fr_pair_list_head(to); vp != NULL; vp = next, i++) {
134 next = fr_pair_list_next(to, vp);
135
136 if (edited[i] == vp) continue;
137
138 if (edited[i]) {
139 fr_pair_replace(to, vp, edited[i]);
140 continue;
141 }
142
143 fr_pair_remove(to, vp);
145 }
146}
147
148/** Merge the "from" list into the "to" list, using the operator of each pair
149 *
150 * Every pair of the "from" list is either moved into the "to" list, or freed.
151 * The "from" list is empty when the function returns.
152 *
153 * Operators apply only to the pairs which were in the "to" list when the
154 * function was called. They do not apply to the pairs which the same call
155 * adds to the "to" list, or to the pairs which an earlier operator has
156 * already replaced or removed.
157 *
158 * Unlike the previous implementation, "-=" removes only the pairs which have
159 * the matching value. If the "to" list has two instances of the attribute,
160 * and the value matches only the second one, then only the second one is
161 * removed.
162 *
163 * @param[in] request the request, used for debug output and as the talloc
164 * context for the temporary array.
165 * @param[in,out] to list to update.
166 * @param[in,out] from list of pairs to merge. Emptied.
167 */
169{
170 size_t j, to_count;
171 fr_pair_t *vp, *next, *old;
172 fr_pair_t **edited = NULL;
173 fr_pair_list_t append, prepend;
174 fr_token_t op;
175 bool relink = false, found;
176
177 fr_pair_list_init(&append);
178 fr_pair_list_init(&prepend);
179
180 /*
181 * Get the pairs of the "to" list into an array, so that we
182 * can mark them as replaced or removed without changing the
183 * list.
184 */
185 to_count = fr_pair_list_num_elements(to);
186 if (to_count > 0) {
187 edited = talloc_array(request, fr_pair_t *, to_count);
188
189 j = 0;
190 for (vp = fr_pair_list_head(to); vp != NULL; vp = fr_pair_list_next(to, vp)) edited[j++] = vp;
191 }
192
193/*
194 * Take the pair out of the "from" list. The operator in the "to" list
195 * is always '=', because it is used in the "detail" file and in debug
196 * output, where we don't want to see the operator.
197 */
198#define REMOVE(_vp) \
199 do { \
200 fr_pair_remove(from, (_vp)); \
201 (_vp)->op = T_OP_EQ; \
202 } while (0)
203
204/*
205 * Visit every unedited instance of the attribute of "vp" in the "to"
206 * list. "old" is the instance, and "j" is its position.
207 */
208#define LOOP \
209 for (old = fr_pair_list_head(to), j = 0; \
210 pair_find_next_by_da(to, &old, &j, edited, vp->da); \
211 old = fr_pair_list_next(to, old), j++)
212
213 /*
214 * Do one loop over the "from" list.
215 */
216 for (vp = fr_pair_list_head(from); vp != NULL; vp = next) {
217 /*
218 * Find the next pair before the current pair leaves the list.
219 */
220 next = fr_pair_list_next(from, vp);
221
222 switch (vp->op) {
223 case T_OP_PREPEND:
224 REMOVE(vp);
225 fr_pair_prepend(&prepend, vp);
226 continue;
227
228 case T_OP_CMP_FALSE:
229 if (!edited) goto do_remove;
230
231 /*
232 * Remove every unedited instance of the
233 * attribute, whatever its value.
234 */
235 LOOP {
236 edited[j] = NULL;
237 relink = true;
238 }
239
240 do_remove:
241 REMOVE(vp);
243 continue;
244
245 case T_OP_SUB_EQ:
246 case T_OP_CMP_EQ:
247 if (!edited) goto do_remove;
248
249 /*
250 * "-=" removes every unedited instance of the
251 * attribute which has the same value. "=="
252 * does the opposite, and removes every one which
253 * has a different value.
254 */
255 op = vp->op;
256 vp->op = T_OP_CMP_EQ;
257
258 LOOP {
259 int rcode = paircmp_pairs(NULL, vp, old);
260
261 if ((rcode == 0) == (op == T_OP_SUB_EQ)) {
262 edited[j] = NULL;
263 relink = true;
264 }
265 }
266
267 goto do_remove;
268
269 case T_OP_LE:
270 case T_OP_GE:
271 if (!edited) goto do_add;
272
273 /*
274 * "<=" replaces the first unedited instance
275 * which has a larger value, and ">=" the first
276 * which has a smaller value. If there is an
277 * instance, but the instance doesn't need to
278 * be replaced, then we are done with the pair.
279 * If there is no instance, then we add the pair.
280 */
281 op = vp->op;
282 vp->op = T_OP_CMP_EQ;
283
284 found = false;
285 LOOP {
286 int rcode = paircmp_pairs(NULL, vp, old);
287
288 found = true;
289
290 if (((op == T_OP_LE) && (rcode > 0)) ||
291 ((op == T_OP_GE) && (rcode < 0))) {
292 goto do_set;
293 }
294 }
295
296 if (found) goto do_remove;
297 goto do_add;
298
299 case T_OP_EQ:
300 if (!edited) goto do_add;
301
302 /*
303 * Add the pair only if the "to" list does not
304 * have an instance of the attribute.
305 */
306 if (pair_find_by_da(&j, to, edited, vp->da)) goto do_remove;
307 goto do_add;
308
309 case T_OP_SET:
310 if (!edited) goto do_add;
311
312 /*
313 * Replace the first existing instance. If
314 * there isn't one, add the new pair.
315 */
316 if (!pair_find_by_da(&j, to, edited, vp->da)) goto do_add;
317
318 do_set:
319 REMOVE(vp);
320 edited[j] = vp;
321 relink = true;
322 continue;
323
324 case T_OP_ADD_EQ:
325 do_add:
326 REMOVE(vp);
327 fr_pair_append(&append, vp);
328 continue;
329
330 default:
331 fr_assert(0 == 1); /* panic! */
332 goto do_remove;
333 }
334 }
335
336#undef REMOVE
337#undef LOOP
338
339 /*
340 * Update the "to" list with the pairs which we replaced or
341 * removed.
342 */
343 if (relink) pair_relink(to, edited);
344
345 TALLOC_FREE(edited);
346
347 /*
348 * Prepend the pairs of the "prepend" list to the "to" list, and
349 * append the pairs of the "append" list.
350 */
351 fr_pair_list_prepend(to, &prepend);
352 fr_pair_list_append(to, &append);
353
354 /*
355 * We must have processed the entire "from" list.
356 */
358}
359
361{
362 fr_pair_t *vp;
363 fr_dict_attr_t const *da;
364 fr_value_box_t *box, *to_free = NULL;
365
366 RDEBUG(" %s %s %s", map->lhs->name, fr_tokens[map->op], map->rhs->name);
367
368 da = tmpl_attr_tail_da(map->lhs);
369 fr_assert(fr_type_is_leaf(da->type));
370
371 if (tmpl_is_data(map->rhs)) {
372 box = tmpl_value(map->rhs);
373
374 } else if (tmpl_is_attr(map->rhs)) {
375 fr_pair_t *rhs;
376
377 if (tmpl_find_vp(&rhs, request, map->rhs) < 0) return -1;
378
379 if (rhs->vp_type != da->type) {
380 fr_strerror_const("Incompatible data types");
381 return -1;
382 }
383
384 box = &rhs->data;
385
386 } else if (tmpl_is_xlat(map->rhs)) {
387 if (tmpl_aexpand(parent, &to_free, request, map->rhs, NULL, NULL) < 0) return -1;
388
389 box = to_free;
390
391 } else {
392 fr_strerror_const("Unknown RHS");
393 return -1;
394 }
395
396 if (fr_pair_append_by_da(parent, &vp, &parent->vp_group, da) < 0) return -1;
397
398 if (fr_value_box_cast(vp, &vp->data, vp->vp_type, vp->da, box) < 0) {
399 TALLOC_FREE(to_free);
400 return -1;
401 }
402
403 TALLOC_FREE(to_free);
404 return 0;
405}
406
407
409{
410 fr_value_box_t *box, *to_free = NULL;
411
412 /*
413 * No RHS map, but we have children. Create them, and add them to the list.
414 */
415 if (!map->rhs) {
416 map_t *child;
417
418 /*
419 * Convert the child maps to VPs. We know that
420 * we just created the pair, so there's no reason
421 * to apply operators to the children.
422 */
423 for (child = map_list_next(&map->child, NULL);
424 child != NULL;
425 child = map_list_next(&map->child, child)) {
426 fr_assert(child->op == T_OP_EQ);
427 if (radius_legacy_map_to_vp(request, vp, child) < 0) return -1;
428 }
429
430 return 0;
431 }
432
433 /*
434 * Copy an existing attribute.
435 */
436 if (tmpl_is_attr(map->rhs)) {
437 fr_pair_t *rhs;
438
439 if (tmpl_find_vp(&rhs, request, map->rhs) < 0) return -1;
440
441 if (rhs->vp_type != vp->vp_type) {
442 fr_strerror_const("Incompatible data types");
443 return -1;
444 }
445
446 if (rhs == vp) {
447 fr_strerror_const("Invalid self-reference");
448 return -1;
449 }
450
451 return fr_pair_list_copy(vp, &vp->vp_group, &rhs->vp_group);
452 }
453
454 /*
455 * RHS is a string or an xlat expansion.
456 */
457 if (tmpl_is_data(map->rhs)) {
458 box = tmpl_value(map->rhs);
459
460 } else if (tmpl_is_xlat(map->rhs)) {
461 if (tmpl_aexpand(request, &to_free, request, map->rhs, NULL, NULL) < 0) return -1;
462
463 box = to_free;
464
465 } else {
466 fr_strerror_const("Unknown RHS");
467 return -1;
468 }
469
470 if (box->type != FR_TYPE_STRING) {
471 fr_strerror_const("Cannot parse child list");
472 TALLOC_FREE(to_free);
473 return -1;
474 }
475
476 /*
477 * If there's no value, just leave the list alone.
478 *
479 * Otherwise parse the children in the context of the parent.
480 */
481 if (box->vb_strvalue[0]) {
482 fr_pair_parse_t root, relative;
483
484 /*
485 * Parse the string as a list of pairs.
486 */
487 root = (fr_pair_parse_t) {
488 .ctx = vp,
489 .da = vp->da,
490 .list = &vp->vp_group,
491 .dict = vp->da->dict,
492 .internal = fr_dict_internal(),
493 .allow_compare = false,
494 .tainted = box->tainted,
495 };
496 relative = (fr_pair_parse_t) { };
497
498 if (fr_pair_list_afrom_substr(&root, &relative, &FR_SBUFF_IN(box->vb_strvalue, box->vb_length)) < 0) {
499 RPEDEBUG("Failed parsing string '%pV' as attribute list", box);
500 TALLOC_FREE(to_free);
501 return -1;
502 }
503 }
504
505 TALLOC_FREE(to_free);
506 return 0;
507}
508
509typedef struct {
511 fr_pair_t *vp; /* the one we created */
513
514/** Build the relevant pairs at each level.
515 *
516 * See edit_list_pair_build() for similar code.
517 */
519{
520 fr_pair_t *vp;
521 legacy_pair_build_t *lp = uctx;
522
524 if (!vp) return NULL;
525
526 if (fr_edit_list_insert_pair_tail(lp->el, &parent->vp_group, vp) < 0) {
528 return NULL;
529 }
530
531 /*
532 * Tell the cursor that we appended a pair. This
533 * function only gets called when we've ran off of the
534 * end of the list, and can't find the thing we're
535 * looking for. So it's safe at set the current one
536 * here.
537 *
538 * @todo - mainly only because we don't allow creating
539 * foo[4] when there's <3 matching entries. i.e. the
540 * "arrays" here are really lists, so we can't create
541 * "holes" in the list.
542 */
543 fr_dcursor_set_current(cursor, vp);
544
545 lp->vp = vp;
546
547 return vp;
548}
549
550
551/** Move a map using the operators from the old pairmove functionality.
552 *
553 */
555{
556 int16_t num;
557 int err, rcode;
558 bool added = false;
559 fr_pair_t *vp = NULL, *next, *parent;
560 fr_dict_attr_t const *da;
561 fr_pair_list_t *list;
562 TALLOC_CTX *ctx;
563 fr_value_box_t *to_free = NULL;
564 fr_value_box_t const *box;
566 fr_dcursor_t cursor;
567
568 /*
569 * Find out where this attribute exists, or should exist.
570 */
571 tmpl_pair_list_and_ctx(ctx, list, request, tmpl_request(map->lhs), tmpl_list(map->lhs));
572 if (!ctx) return -1; /* no request or list head exists */
573
574 da = tmpl_attr_tail_da(map->lhs);
575
576 /*
577 * These operations are the same for both leaf and structural types.
578 */
579 switch (map->op) {
580 case T_OP_EQ:
581 if (tmpl_find_vp(&vp, request, map->lhs) < -1) return -1;
582 if (vp) return 0;
583 goto add;
584
585 case T_OP_SET:
586 /*
587 * Set a value. Note that we might do
588 *
589 * &foo[1] := 1
590 *
591 * In which case we don't want to delete the attribute, we just want to replace its
592 * value.
593 *
594 * @todo - we can't do &foo[*].bar[*].baz = 1, as that's an implicit cursor, and we don't
595 * do that.
596 */
597 num = tmpl_attr_tail_num(map->lhs);
598 if (num == NUM_COUNT) {
599 fr_strerror_const("Invalid count in attribute reference");
600 return -1;
601 }
602
603 vp = tmpl_dcursor_init(&err, ctx, &cc, &cursor, request, map->lhs);
604
605 /*
606 * We're editing a specific number. It must exist, otherwise the edit does nothing.
607 */
608 if ((num >= 0) || (num == NUM_LAST)) {
609 if (!vp) return 0;
610
611 if (fr_type_is_leaf(vp->vp_type)) {
612 if (fr_edit_list_save_pair_value(el, vp) < 0) return -1;
613 } else {
615
616 if (fr_edit_list_free_pair_children(el, vp) < 0) return -1;
617 }
618 break;
619 }
620
621 /*
622 * We don't delete the main lists, we just modify their contents.
623 */
624 if (request_attr_is_list(da)) {
625 fr_assert(vp != NULL);
626
627 if (fr_edit_list_free_pair_children(el, vp) < 0) return -1;
628 break;
629 }
630
631 if (!vp) goto add;
632
633 /*
634 * Delete the first attribute we found.
635 */
637 fr_assert(parent != NULL);
638
639 if (fr_edit_list_pair_delete(el, &parent->vp_group, vp) < 0) return -1;
641
642 /*
643 * Delete all existing attributes. Note that we re-initialize the cursor every time,
644 * because creating "foo := baz" means deleting ALL existing "foo". But we can't use
645 * the tmpl as a cursor, because the tmpl containst NUM_UNSPEC, and the cursor needs
646 * NUM_ALL. So we have to delete all existing attributes, and then add a new one.
647 */
648 while (true) {
649 vp = tmpl_dcursor_init(&err, ctx, &cc, &cursor, request, map->lhs);
650 if (!vp) break;
651
653 fr_assert(parent != NULL);
654
655 if (fr_edit_list_pair_delete(el, &parent->vp_group, vp) < 0) return -1;
657 }
659
660 case T_OP_ADD_EQ:
661 add:
662 {
664 .el = el,
665 .vp = NULL,
666 };
667
669 vp = tmpl_dcursor_build_init(&err, ctx, &cc, &cursor, request, map->lhs, legacy_pair_build, &lp);
671 if (!vp) {
672 RWDEBUG("Failed creating attribute %s", map->lhs->name);
673 return -1;
674 }
675
676 /*
677 * If we're adding and one already exists, create a new one in the same context.
678 */
679 if ((map->op == T_OP_ADD_EQ) && !lp.vp) {
681 fr_assert(parent != NULL);
682
684 if (fr_edit_list_insert_pair_tail(el, &parent->vp_group, vp) < 0) return -1;
685 }
686
687 added = true;
688 }
689 break;
690
691 case T_OP_LE: /* replace if not <= */
692 case T_OP_GE: /* replace if not >= */
693 if (fr_type_is_structural(da->type)) goto invalid_operator;
694
695 if (tmpl_find_vp(&vp, request, map->lhs) < -1) return -1;
696 if (!vp) goto add;
697 break;
698
699 case T_OP_SUB_EQ: /* delete if match, otherwise ignore */
700 if (fr_type_is_structural(da->type)) {
701 invalid_operator:
702 fr_strerror_printf("Invalid operator '%s' for structural type", fr_type_to_str(da->type));
703 return -1;
704 }
705
706 if (tmpl_find_vp(&vp, request, map->lhs) < -1) return -1;
707 if (!vp) return 0;
708 break;
709
710 default:
711 fr_strerror_printf("Invalid operator '%s'", fr_tokens[map->op]);
712 return -1;
713 }
714
715 fr_assert(vp);
716
717 /*
718 * We don't support operations on structural types. Just creation, and assign values.
719 *
720 * The code above has ensured that the structural type has been either saved or cleared via the
721 * edit list, so the next function doesn't need to do that.
722 */
724 fr_assert(added);
726 }
727
728 /*
729 * We have now found the RHS. Expand it.
730 *
731 * Note that
732 *
733 * &foo := %tolower(&foo)
734 *
735 * works, as we save the value above in the T_OP_SET handler. So we don't delete it.
736 */
737 if (tmpl_is_data(map->rhs)) {
738 box = tmpl_value(map->rhs);
739
740 } else if (tmpl_is_attr(map->rhs)) {
741 fr_pair_t *rhs;
742
743 if (tmpl_find_vp(&rhs, request, map->rhs) < 0) return -1;
744
745 if (rhs->vp_type != da->type) {
746 fr_strerror_const("Incompatible data types");
747 return -1;
748 }
749
750 if (rhs == vp) {
751 fr_strerror_const("Invalid self-reference");
752 return -1;
753 }
754
755 box = &rhs->data;
756
757 } else if (tmpl_is_xlat(map->rhs)) {
758 if (tmpl_aexpand(ctx, &to_free, request, map->rhs, NULL, NULL) < 0) return -1;
759
760 box = to_free;
761
762 } else {
763 fr_strerror_const("Unknown RHS");
764 return -1;
765 }
766
767 /*
768 * We added a VP which hadn't previously existed. Therefore just set the value and return.
769 */
770 if (added) {
771 if (fr_value_box_cast(vp, &vp->data, vp->vp_type, vp->da, box) < 0) {
772 fail:
773 TALLOC_FREE(to_free);
774 return -1;
775 }
776
777 if (vp->da->flags.unsafe) fr_value_box_mark_unsafe(&vp->data);
778 TALLOC_FREE(to_free);
779 return 0;
780 }
781
782 while (vp) {
783 /*
784 * The tmpl calls above use map->lhs to get to both the
785 * correct list, and da. So from now on, we don't need
786 * to do any more nested finds.
787 */
788 fr_assert(vp->da == da);
789 next = fr_pair_find_by_da(list, vp, da); /* could be deleted in the loop*/
790
791 switch (map->op) {
792 case T_OP_LE: /* replace if not <= */
793 case T_OP_GE: /* replace if not >= */
794 rcode = fr_value_box_cmp_op(map->op, &vp->data, box);
795 if (rcode < 0) goto fail;
796
797 if (rcode != 0) break;
798
799 if (fr_value_box_cast(vp, &vp->data, vp->vp_type, vp->da, box) < 0) goto fail;
800 break;
801
802 case T_OP_SUB_EQ: /* delete if match */
803 rcode = fr_value_box_cmp_op(T_OP_CMP_EQ, &vp->data, box);
804 if (rcode < 0) goto fail;
805
806 if (rcode == 1) {
807 fr_pair_list_t *parent_list = fr_pair_parent_list(vp);
808
809 if (fr_edit_list_pair_delete(el, parent_list, vp) < 0) goto fail;
810 }
811 break;
812
813 default:
814 fr_assert(0); /* should have been caught above */
815 return -1;
816 }
817
818 vp = next;
819 }
820
821 TALLOC_FREE(to_free);
822 return 0;
823}
824
826{
827 map_t const *map;
828
829 for (map = map_list_head(list);
830 map != NULL;
831 map = map_list_next(list, map)) {
832 RDEBUG2("%s %s %s", map->lhs->name, fr_tokens[map->op],
833 map->rhs ? map->rhs->name : "{ ... }");
834
835 if (radius_legacy_map_apply(request, map, el) < 0) {
836 RPEDEBUG("Failed applying result");
837 return -1;
838 }
839 }
840
841 return 0;
842}
843
845{
846 int rcode;
847 fr_pair_t *vp;
848 fr_value_box_t const *box;
849 fr_value_box_t *to_free = NULL;
850 fr_value_box_t dst, str;
851 fr_dcursor_t cursor;
853
856
857 vp = tmpl_dcursor_init(NULL, request, &cc, &cursor, request, map->lhs);
858
859 if (!vp) {
861 if (map->op == T_OP_CMP_FALSE) return true;
862 return 0;
863 }
864
865 if (map->op == T_OP_CMP_TRUE){
867 return false;
868 }
869
870 if (fr_type_is_structural(vp->vp_type)) {
871 fr_strerror_const("Invalid comparison for structural type");
872 error:
874 return -1;
875 }
876
877 if (tmpl_is_data(map->rhs)) {
878 box = tmpl_value(map->rhs);
879
880 } else if (tmpl_is_attr(map->rhs)) {
881 fr_pair_t *rhs;
882
883 if (tmpl_find_vp(&rhs, request, map->rhs) < 0) goto error;
884
885 box = &rhs->data;
886
887 } else if (tmpl_contains_xlat(map->rhs)) {
888 if (tmpl_aexpand(request, &to_free, request, map->rhs, NULL, NULL) < 0) goto error;
889
890 box = to_free;
891
892 } else if (tmpl_is_regex(map->rhs)) {
893 /*
894 * @todo - why box it and parse it again, when we can just run the regex?
895 */
896 fr_value_box_strdup_shallow(&str, NULL, map->rhs->name, false);
897 box = &str;
898
899 } else {
900 fr_strerror_const("Unknown RHS");
901 goto error;
902 }
903
904 /*
905 * Check all possible vps matching the lhs
906 * Allows for comparisons such as &foo[*] == "bar" - i.e. true if any instance of &foo has the value "bar"
907 */
908 rcode = 0;
909 while (vp) {
910 /*
911 * Let the calculation code do upcasting as necessary.
912 */
913 rcode = fr_value_calc_binary_op(request, &dst, FR_TYPE_BOOL, &vp->data, map->op, box);
914 if ((rcode >= 0) && dst.vb_bool) break; // Found a "true" result, no need to check any further
915 vp = fr_dcursor_next(&cursor);
916 }
917 TALLOC_FREE(to_free);
919
920 if (rcode < 0) return rcode;
921
922 return dst.vb_bool;
923}
static uint8_t request[4]
#define RCSID(id)
Definition build.h:603
#define FALL_THROUGH
clang 10 doesn't recognised the FALL-THROUGH comment anymore
Definition build.h:422
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:2013
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:38
fr_dict_t const * fr_dict_internal(void)
Definition dict_util.c:5065
talloc_free(hp)
#define RWDEBUG(fmt,...)
Definition log.h:378
#define RPEDEBUG(fmt,...)
Definition log.h:393
@ 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:1416
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:2303
fr_pair_list_t * fr_pair_parent_list(fr_pair_t const *vp)
Return a pointer to the parent pair list.
Definition pair.c:906
fr_pair_t * fr_pair_find_by_da(fr_pair_list_t const *list, fr_pair_t const *prev, fr_dict_attr_t const *da)
Find the first pair with a matching da.
Definition pair.c:707
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:1297
fr_pair_t * fr_pair_parent(fr_pair_t const *vp)
Return a pointer to the parent pair.
Definition pair.c:914
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:1389
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:284
void fr_pair_list_init(fr_pair_list_t *list)
Initialise a pair list header.
Definition pair.c:47
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:1266
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.
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 void pair_relink(fr_pair_list_t *to, fr_pair_t *const *edited)
Update the "to" list with the changes which radius_pairmove() has made.
Definition pairmove.c:128
static int radius_legacy_map_to_vp(request_t *request, fr_pair_t *parent, map_t const *map)
Definition pairmove.c:360
static bool pair_find_next_by_da(fr_pair_list_t const *list, fr_pair_t **vp_p, size_t *index_p, fr_pair_t *const *edited, fr_dict_attr_t const *da)
Find the next unedited pair with a matching attribute.
Definition pairmove.c:74
void radius_pairmove(request_t *request, fr_pair_list_t *to, fr_pair_list_t *from)
Merge the "from" list into the "to" list, using the operator of each pair.
Definition pairmove.c:168
int radius_legacy_map_cmp(request_t *request, map_t const *map)
Definition pairmove.c:844
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:518
fr_pair_t * vp
Definition pairmove.c:511
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:554
static bool pair_find_by_da(size_t *index_p, fr_pair_list_t const *list, fr_pair_t *const *edited, fr_dict_attr_t const *da)
Find the first unedited pair with a matching attribute.
Definition pairmove.c:104
#define LOOP
static int radius_legacy_map_apply_structural(request_t *request, map_t const *map, fr_pair_t *vp)
Definition pairmove.c:408
#define REMOVE(_vp)
int radius_legacy_map_list_apply(request_t *request, map_list_t const *list, fr_edit_list_t *el)
Definition pairmove.c:825
fr_edit_list_t * el
Definition pairmove.c:510
#define fr_assert(_expr)
Definition rad_assert.h:37
#define RDEBUG2(fmt,...)
#define RDEBUG(fmt,...)
static bool request_attr_is_list(fr_dict_attr_t const *da)
Definition request.h:363
#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:777
static int16_t tmpl_attr_tail_num(tmpl_t const *vpt)
Return the last attribute reference's attribute number.
Definition tmpl.h:910
#define tmpl_contains_xlat(vpt)
Definition tmpl.h:234
#define tmpl_is_xlat(vpt)
Definition tmpl.h:217
#define NUM_LAST
Definition tmpl.h:420
#define tmpl_value(_tmpl)
Definition tmpl.h:962
#define tmpl_is_attr(vpt)
Definition tmpl.h:215
static fr_dict_attr_t const * tmpl_list(tmpl_t const *vpt)
Definition tmpl.h:929
#define NUM_COUNT
Definition tmpl.h:419
#define tmpl_pair_list_and_ctx(_ctx, _head, _request, _ref, _list)
Determine the correct context and list head.
Definition tmpl.h:1018
#define tmpl_is_data(vpt)
Definition tmpl.h:213
static fr_dict_attr_t const * tmpl_attr_tail_da(tmpl_t const *vpt)
Return the last attribute reference da.
Definition tmpl.h:826
#define tmpl_is_regex(vpt)
Definition tmpl.h:220
#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:1089
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:146
const bool fr_comparison_op[T_TOKEN_LAST]
Definition token.c:264
@ T_OP_SUB_EQ
Definition token.h:68
@ T_OP_CMP_TRUE
Definition token.h:102
@ T_OP_EQ
Definition token.h:81
@ T_OP_SET
Definition token.h:82
@ T_OP_ADD_EQ
Definition token.h:67
@ T_OP_CMP_FALSE
Definition token.h:103
@ T_OP_CMP_EQ
Definition token.h:104
@ T_OP_LE
Definition token.h:98
@ T_OP_GE
Definition token.h:96
@ T_OP_PREPEND
Definition token.h:83
enum fr_token fr_token_t
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:608
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:646
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:723
Track a series of edits.
Definition edit.c:101
#define fr_edit_list_insert_pair_tail(_el, _list, _vp)
Definition edit.h:51
bool fr_pair_list_empty(fr_pair_list_t const *list)
Is a valuepair list empty.
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_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:864
#define add(_type, _out, _in)
Definition stats.c:187
void fr_strerror_clear(void)
Clears all pending messages from the talloc pools.
Definition strerror.c:581
#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:392
#define fr_type_is_leaf(_x)
Definition types.h:393
static char const * fr_type_to_str(fr_type_t type)
Return a static string containing the type name.
Definition types.h:454
void fr_value_box_mark_unsafe(fr_value_box_t *vb)
Mark a value-box as "unsafe".
Definition value.c:7528
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:4071
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:1017
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:4873
int nonnull(2, 5))