2 * evaluate.c Evaluate complex conditions
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20 * Copyright 2007 The FreeRADIUS server project
21 * Copyright 2007 Alan DeKok <aland@deployingradius.com>
26 #include <freeradius-devel/radiusd.h>
27 #include <freeradius-devel/modules.h>
28 #include <freeradius-devel/parser.h>
29 #include <freeradius-devel/rad_assert.h>
34 #ifdef WITH_EVAL_DEBUG
35 # define EVAL_DEBUG(fmt, ...) printf("EVAL: ");printf(fmt, ## __VA_ARGS__);printf("\n");fflush(stdout)
37 # define EVAL_DEBUG(...)
40 FR_NAME_NUMBER const modreturn_table[] = {
41 { "reject", RLM_MODULE_REJECT },
42 { "fail", RLM_MODULE_FAIL },
43 { "ok", RLM_MODULE_OK },
44 { "handled", RLM_MODULE_HANDLED },
45 { "invalid", RLM_MODULE_INVALID },
46 { "userlock", RLM_MODULE_USERLOCK },
47 { "notfound", RLM_MODULE_NOTFOUND },
48 { "noop", RLM_MODULE_NOOP },
49 { "updated", RLM_MODULE_UPDATED },
54 static bool all_digits(char const *string)
56 char const *p = string;
58 rad_assert(p != NULL);
60 if (*p == '\0') return false;
64 while (isdigit((int) *p)) p++;
69 /** Evaluate a template
71 * Converts a vp_tmpl_t to a boolean value.
73 * @param[in] request the REQUEST
74 * @param[in] modreturn the previous module return code
75 * @param[in] depth of the recursion (only used for debugging)
76 * @param[in] vpt the template to evaluate
77 * @return -1 on error, 0 for "no match", 1 for "match".
79 int radius_evaluate_tmpl(REQUEST *request, int modreturn, UNUSED int depth, vp_tmpl_t const *vpt)
86 case TMPL_TYPE_LITERAL:
87 modcode = fr_str2int(modreturn_table, vpt->name, RLM_MODULE_UNKNOWN);
88 if (modcode != RLM_MODULE_UNKNOWN) {
89 rcode = (modcode == modreturn);
94 * Else it's a literal string. Empty string is
95 * false, non-empty string is true.
97 * @todo: Maybe also check for digits?
99 * The VPT *doesn't* have a "bare word" type,
100 * which arguably it should.
102 rcode = (vpt->name != '\0');
107 if (tmpl_find_vp(NULL, request, vpt) == 0) {
114 case TMPL_TYPE_XLAT_STRUCT:
120 if (!*vpt->name) return false;
121 rcode = tmpl_aexpand(request, &p, request, vpt, NULL, NULL);
123 EVAL_DEBUG("FAIL %d", __LINE__);
127 rcode = (data.strvalue && (*data.strvalue != '\0'));
128 talloc_free(data.ptr);
133 * Can't have a bare ... (/foo/) ...
135 case TMPL_TYPE_REGEX:
136 case TMPL_TYPE_REGEX_STRUCT:
141 EVAL_DEBUG("FAIL %d", __LINE__);
150 /** Perform a regular expressions comparison between two operands
152 * @return -1 on error, 0 for "no match", 1 for "match".
154 static int cond_do_regex(REQUEST *request, fr_cond_t const *c,
155 PW_TYPE lhs_type, value_data_t const *lhs, size_t lhs_len,
156 PW_TYPE rhs_type, value_data_t const *rhs, size_t rhs_len)
158 vp_map_t const *map = c->data.map;
163 regex_t *preg, *rreg = NULL;
164 regmatch_t rxmatch[REQUEST_MAX_REGEX + 1]; /* +1 for %{0} (whole match) capture group */
165 size_t nmatch = sizeof(rxmatch) / sizeof(regmatch_t);
167 rad_assert(lhs_type == PW_TYPE_STRING);
168 rad_assert(lhs != NULL);
170 EVAL_DEBUG("CMP WITH REGEX %s %s",
171 map->rhs->tmpl_iflag ? "CASE INSENSITIVE" : "CASE SENSITIVE",
172 map->rhs->tmpl_mflag ? "MULTILINE" : "SINGLELINE");
174 switch (map->rhs->type) {
175 case TMPL_TYPE_REGEX_STRUCT: /* pre-compiled to a regex */
176 preg = map->rhs->tmpl_preg;
180 rad_assert(rhs_type == PW_TYPE_STRING);
181 rad_assert(rhs->strvalue);
182 slen = regex_compile(request, &rreg, rhs->strvalue, rhs_len,
183 map->rhs->tmpl_iflag, map->rhs->tmpl_mflag, true, true);
185 REMARKER(rhs->strvalue, -slen, fr_strerror());
186 EVAL_DEBUG("FAIL %d", __LINE__);
194 ret = regex_exec(preg, lhs->strvalue, lhs_len, rxmatch, &nmatch);
197 EVAL_DEBUG("CLEARING SUBCAPTURES");
198 regex_sub_to_request(request, NULL, NULL, 0, NULL, 0); /* clear out old entries */
202 EVAL_DEBUG("SETTING SUBCAPTURES");
203 regex_sub_to_request(request, &preg, lhs->strvalue, lhs_len, rxmatch, nmatch);
207 EVAL_DEBUG("REGEX ERROR");
208 REDEBUG("regex failed: %s", fr_strerror());
215 if (preg) talloc_free(rreg);
221 #ifdef WITH_EVAL_DEBUG
222 static void cond_print_operands(REQUEST *request,
223 PW_TYPE lhs_type, value_data_t const *lhs, size_t lhs_len,
224 PW_TYPE rhs_type, value_data_t const *rhs, size_t rhs_len)
227 if (lhs_type == PW_TYPE_STRING) {
228 EVAL_DEBUG("LHS: \"%s\" (%zu)" , lhs->strvalue, lhs_len);
232 lhs_hex = talloc_array(request, char, (lhs_len * 2) + 1);
234 if (lhs_type == PW_TYPE_OCTETS) {
235 fr_bin2hex(lhs_hex, lhs->octets, lhs_len);
237 fr_bin2hex(lhs_hex, (uint8_t const *)lhs, lhs_len);
240 EVAL_DEBUG("LHS: 0x%s (%zu)", lhs_hex, lhs_len);
242 talloc_free(lhs_hex);
245 EVAL_DEBUG("LHS: VIRTUAL");
249 if (rhs_type == PW_TYPE_STRING) {
250 EVAL_DEBUG("RHS: \"%s\" (%zu)" , rhs->strvalue, rhs_len);
254 rhs_hex = talloc_array(request, char, (rhs_len * 2) + 1);
256 if (rhs_type == PW_TYPE_OCTETS) {
257 fr_bin2hex(rhs_hex, rhs->octets, rhs_len);
259 fr_bin2hex(rhs_hex, (uint8_t const *)rhs, rhs_len);
262 EVAL_DEBUG("RHS: 0x%s (%zu)", rhs_hex, rhs_len);
264 talloc_free(rhs_hex);
267 EVAL_DEBUG("RHS: COMPILED");
272 /** Call the correct data comparison function for the condition
274 * Deals with regular expression comparisons, virtual attribute
275 * comparisons, and data comparisons.
277 * @return -1 on error, 0 for "no match", 1 for "match".
279 static int cond_cmp_values(REQUEST *request, fr_cond_t const *c,
280 PW_TYPE lhs_type, value_data_t const *lhs, size_t lhs_len,
281 PW_TYPE rhs_type, value_data_t const *rhs, size_t rhs_len)
283 vp_map_t const *map = c->data.map;
286 #ifdef WITH_EVAL_DEBUG
287 EVAL_DEBUG("CMP OPERANDS");
288 cond_print_operands(request, lhs_type, lhs, lhs_len, rhs_type, rhs, rhs_len);
295 if (map->op == T_OP_REG_EQ) {
296 rcode = cond_do_regex(request, c, lhs_type, lhs, lhs_len, rhs_type, rhs, rhs_len);
301 * Virtual attribute comparison.
303 if (c->pass2_fixup == PASS2_PAIRCOMPARE) {
306 EVAL_DEBUG("CMP WITH PAIRCOMPARE");
307 rad_assert(map->lhs->type == TMPL_TYPE_ATTR);
309 vp = fr_pair_afrom_da(request, map->lhs->tmpl_da);
310 vp->op = c->data.map->op;
312 value_data_copy(vp, &vp->data, rhs_type, rhs, rhs_len);
313 vp->vp_length = rhs_len;
315 rcode = paircompare(request, request->packet->vps, vp, NULL);
316 rcode = (rcode == 0) ? 1 : 0;
322 * At this point both operands should have been normalised
323 * to the same type, and there's no special comparisons
326 rad_assert(lhs_type == rhs_type);
328 EVAL_DEBUG("CMP WITH VALUE DATA");
329 rcode = value_data_cmp_op(map->op, lhs_type, lhs, lhs_len, rhs_type, rhs, rhs_len);
341 EVAL_DEBUG("ERROR %i", rcode);
349 static size_t regex_escape(UNUSED REQUEST *request, char *out, size_t outlen, char const *in, UNUSED void *arg)
353 while (*in && (outlen > 2)) {
363 case '[': /* we don't list close braces */
366 if (outlen < 3) goto done;
385 /** Convert both operands to the same type
387 * If casting is successful, we call cond_cmp_values to do the comparison
389 * @return -1 on error, 0 for "no match", 1 for "match".
391 static int cond_normalise_and_cmp(REQUEST *request, fr_cond_t const *c,
392 PW_TYPE lhs_type, DICT_ATTR const *lhs_enumv,
393 value_data_t const *lhs, size_t lhs_len)
395 vp_map_t const *map = c->data.map;
397 DICT_ATTR const *cast = NULL;
398 PW_TYPE cast_type = PW_TYPE_INVALID;
402 PW_TYPE rhs_type = PW_TYPE_INVALID;
403 DICT_ATTR const *rhs_enumv = NULL;
404 value_data_t const *rhs = NULL;
407 value_data_t lhs_cast, rhs_cast;
408 void *lhs_cast_buff = NULL, *rhs_cast_buff = NULL;
410 xlat_escape_t escape = NULL;
413 * Cast operand to correct type.
415 * With hack for strings that look like integers, to cast them
416 * to 64 bit unsigned integers.
418 * @fixme For things like this it'd be useful to have a 64bit signed type.
422 if ((cast_type != PW_TYPE_INVALID) && (_s ## _type != PW_TYPE_INVALID) && (cast_type != _s ## _type)) {\
424 EVAL_DEBUG("CASTING " #_s " FROM %s TO %s",\
425 fr_int2str(dict_attr_types, _s ## _type, "<INVALID>"),\
426 fr_int2str(dict_attr_types, cast_type, "<INVALID>"));\
427 r = value_data_cast(request, &_s ## _cast, cast_type, cast, _s ## _type, _s ## _enumv, _s, _s ## _len);\
429 REDEBUG("Failed casting " #_s " operand: %s", fr_strerror());\
433 if (cast && cast->flags.is_pointer) _s ## _cast_buff = _s ## _cast.ptr;\
434 _s ## _type = cast_type;\
435 _s ## _len = (size_t)r;\
440 #define CHECK_INT_CAST(_l, _r) \
442 if ((cast_type == PW_TYPE_INVALID) &&\
443 _l && (_l ## _type == PW_TYPE_STRING) &&\
444 _r && (_r ## _type == PW_TYPE_STRING) &&\
445 all_digits(lhs->strvalue) && all_digits(rhs->strvalue)) {\
446 cast_type = PW_TYPE_INTEGER64;\
447 EVAL_DEBUG("OPERANDS ARE NUMBER STRINGS, SETTING CAST TO integer64");\
452 * Regular expressions need both operands to be strings
455 if (map->op == T_OP_REG_EQ) {
456 cast_type = PW_TYPE_STRING;
458 if (map->rhs->type == TMPL_TYPE_XLAT_STRUCT) escape = regex_escape;
463 * If it's a pair comparison, data gets cast to the
464 * type of the pair comparison attribute.
466 * Magic attribute is always the LHS.
468 if (c->pass2_fixup == PASS2_PAIRCOMPARE) {
469 rad_assert(!c->cast);
470 rad_assert(map->lhs->type == TMPL_TYPE_ATTR);
472 /* expensive assert */
473 rad_assert((map->rhs->type != TMPL_TYPE_ATTR) || !radius_find_compare(map->rhs->tmpl_da));
475 cast = map->lhs->tmpl_da;
476 cast_type = cast->type;
478 EVAL_DEBUG("NORMALISATION TYPE %s (PAIRCMP TYPE)",
479 fr_int2str(dict_attr_types, cast->type, "<INVALID>"));
481 * Otherwise we use the explicit cast, or implicit
482 * cast (from an attribute reference).
483 * We already have the data for the lhs, so we convert
486 } else if (c->cast) {
488 EVAL_DEBUG("NORMALISATION TYPE %s (EXPLICIT CAST)",
489 fr_int2str(dict_attr_types, cast->type, "<INVALID>"));
490 } else if (map->lhs->type == TMPL_TYPE_ATTR) {
491 cast = map->lhs->tmpl_da;
492 EVAL_DEBUG("NORMALISATION TYPE %s (IMPLICIT FROM LHS REF)",
493 fr_int2str(dict_attr_types, cast->type, "<INVALID>"));
494 } else if (map->rhs->type == TMPL_TYPE_ATTR) {
495 cast = map->rhs->tmpl_da;
496 EVAL_DEBUG("NORMALISATION TYPE %s (IMPLICIT FROM RHS REF)",
497 fr_int2str(dict_attr_types, cast->type, "<INVALID>"));
498 } else if (map->lhs->type == TMPL_TYPE_DATA) {
499 cast_type = map->lhs->tmpl_data_type;
500 EVAL_DEBUG("NORMALISATION TYPE %s (IMPLICIT FROM LHS DATA)",
501 fr_int2str(dict_attr_types, cast_type, "<INVALID>"));
502 } else if (map->rhs->type == TMPL_TYPE_DATA) {
503 cast_type = map->rhs->tmpl_data_type;
504 EVAL_DEBUG("NORMALISATION TYPE %s (IMPLICIT FROM RHS DATA)",
505 fr_int2str(dict_attr_types, cast_type, "<INVALID>"));
508 if (cast) cast_type = cast->type;
510 switch (map->rhs->type) {
516 for (vp = tmpl_cursor_init(&rcode, &cursor, request, map->rhs);
518 vp = tmpl_cursor_next(&cursor, map->rhs)) {
519 rhs_type = vp->da->type;
522 rhs_len = vp->vp_length;
524 CHECK_INT_CAST(lhs, rhs);
528 rcode = cond_cmp_values(request, c, lhs_type, lhs, lhs_len, rhs_type, rhs, rhs_len);
529 if (rcode != 0) break;
531 TALLOC_FREE(rhs_cast_buff);
537 rhs_type = map->rhs->tmpl_data_type;
538 rhs = &map->rhs->tmpl_data_value;
539 rhs_len = map->rhs->tmpl_data_length;
541 CHECK_INT_CAST(lhs, rhs);
545 rcode = cond_cmp_values(request, c, lhs_type, lhs, lhs_len, rhs_type, rhs, rhs_len);
549 * Expanded types start as strings, then get converted
550 * to the type of the attribute or the explicit cast.
552 case TMPL_TYPE_LITERAL:
555 case TMPL_TYPE_XLAT_STRUCT:
560 if (map->rhs->type != TMPL_TYPE_LITERAL) {
563 ret = tmpl_aexpand(request, &p, request, map->rhs, escape, NULL);
565 EVAL_DEBUG("FAIL [%i]", __LINE__);
573 data.strvalue = map->rhs->name;
574 rhs_len = map->rhs->len;
576 rad_assert(data.strvalue);
578 rhs_type = PW_TYPE_STRING;
581 CHECK_INT_CAST(lhs, rhs);
585 rcode = cond_cmp_values(request, c, lhs_type, lhs, lhs_len, rhs_type, rhs, rhs_len);
586 if (map->rhs->type != TMPL_TYPE_LITERAL)talloc_free(data.ptr);
592 * RHS is a compiled regex, we don't need to do anything with it.
594 case TMPL_TYPE_REGEX_STRUCT:
596 rcode = cond_cmp_values(request, c, lhs_type, lhs, lhs_len, PW_TYPE_INVALID, NULL, 0);
599 * Unsupported types (should have been parse errors)
603 case TMPL_TYPE_UNKNOWN:
604 case TMPL_TYPE_ATTR_UNDEFINED:
605 case TMPL_TYPE_REGEX: /* Should now be a TMPL_TYPE_REGEX_STRUCT or TMPL_TYPE_XLAT_STRUCT */
612 talloc_free(lhs_cast_buff);
613 talloc_free(rhs_cast_buff);
621 * @param[in] request the REQUEST
622 * @param[in] modreturn the previous module return code
623 * @param[in] depth of the recursion (only used for debugging)
624 * @param[in] c the condition to evaluate
625 * @return -1 on error, 0 for "no match", 1 for "match".
627 int radius_evaluate_map(REQUEST *request, UNUSED int modreturn, UNUSED int depth, fr_cond_t const *c)
631 vp_map_t const *map = c->data.map;
633 EVAL_DEBUG(">>> MAP TYPES LHS: %s, RHS: %s",
634 fr_int2str(tmpl_names, map->lhs->type, "???"),
635 fr_int2str(tmpl_names, map->rhs->type, "???"));
637 switch (map->lhs->type) {
639 * LHS is an attribute or list
647 * Legacy paircompare call, skip processing the magic attribute
648 * if it's the LHS and cast RHS to the same type.
650 if ((c->pass2_fixup == PASS2_PAIRCOMPARE) && (map->op != T_OP_REG_EQ)) {
652 rad_assert(radius_find_compare(map->lhs->tmpl_da)); /* expensive assert */
654 rcode = cond_normalise_and_cmp(request, c, PW_TYPE_INVALID, NULL, NULL, 0);
657 for (vp = tmpl_cursor_init(&rcode, &cursor, request, map->lhs);
659 vp = tmpl_cursor_next(&cursor, map->lhs)) {
661 * Evaluate all LHS values, condition evaluates to true
662 * if we get at least one set of operands that
665 rcode = cond_normalise_and_cmp(request, c, vp->da->type, vp->da, &vp->data, vp->vp_length);
666 if (rcode != 0) break;
672 rcode = cond_normalise_and_cmp(request, c,
673 map->lhs->tmpl_data_type, NULL, &map->lhs->tmpl_data_value,
674 map->lhs->tmpl_data_length);
677 case TMPL_TYPE_LITERAL:
680 case TMPL_TYPE_XLAT_STRUCT:
685 if (map->lhs->type != TMPL_TYPE_LITERAL) {
688 ret = tmpl_aexpand(request, &p, request, map->lhs, NULL, NULL);
690 EVAL_DEBUG("FAIL [%i]", __LINE__);
695 data.strvalue = map->lhs->name;
698 rad_assert(data.strvalue);
700 rcode = cond_normalise_and_cmp(request, c, PW_TYPE_STRING, NULL, &data, ret);
701 if (map->lhs->type != TMPL_TYPE_LITERAL) talloc_free(data.ptr);
706 * Unsupported types (should have been parse errors)
709 case TMPL_TYPE_ATTR_UNDEFINED:
710 case TMPL_TYPE_UNKNOWN:
711 case TMPL_TYPE_REGEX: /* should now be a TMPL_TYPE_REGEX_STRUCT or TMPL_TYPE_XLAT_STRUCT */
712 case TMPL_TYPE_REGEX_STRUCT: /* not allowed as LHS */
723 /** Evaluate a fr_cond_t;
725 * @param[in] request the REQUEST
726 * @param[in] modreturn the previous module return code
727 * @param[in] depth of the recursion (only used for debugging)
728 * @param[in] c the condition to evaluate
729 * @return -1 on failure, -2 on attribute not found, 0 for "no match", 1 for "match".
731 int radius_evaluate_cond(REQUEST *request, int modreturn, int depth, fr_cond_t const *c)
734 #ifdef WITH_EVAL_DEBUG
737 fr_cond_sprint(buffer, sizeof(buffer), c);
738 EVAL_DEBUG("%s", buffer);
743 case COND_TYPE_EXISTS:
744 rcode = radius_evaluate_tmpl(request, modreturn, depth, c->data.vpt);
745 /* Existence checks are special, because we expect them to fail */
746 if (rcode < 0) rcode = 0;
750 rcode = radius_evaluate_map(request, modreturn, depth, c);
753 case COND_TYPE_CHILD:
754 rcode = radius_evaluate_cond(request, modreturn, depth + 1, c->data.child);
761 case COND_TYPE_FALSE:
765 EVAL_DEBUG("FAIL %d", __LINE__);
769 if (rcode < 0) return rcode;
771 if (c->negate) rcode = !rcode;
776 * FALSE && ... = FALSE
778 if (!rcode && (c->next_op == COND_AND)) return false;
783 if (rcode && (c->next_op == COND_OR)) return true;
789 EVAL_DEBUG("FAIL %d", __LINE__);
797 * The fr_pair_list_move() function in src/lib/valuepair.c does all sorts of
798 * extra magic that we don't want here.
800 * FIXME: integrate this with the code calling it, so that we
801 * only fr_pair_list_copy() those attributes that we're really going to
804 void radius_pairmove(REQUEST *request, VALUE_PAIR **to, VALUE_PAIR *from, bool do_xlat)
806 int i, j, count, from_count, to_count, tailto;
808 VALUE_PAIR *vp, *next, **last;
809 VALUE_PAIR **from_list, **to_list;
810 VALUE_PAIR *append, **append_tail;
813 REQUEST *fixup = NULL;
817 * Set up arrays for editing, to remove some of the
818 * O(N^2) dependencies. This also makes it easier to
819 * insert and remove attributes.
821 * It also means that the operators apply ONLY to the
822 * attributes in the original list. With the previous
823 * implementation of fr_pair_list_move(), adding two attributes
824 * via "+=" and then "=" would mean that the second one
825 * wasn't added, because of the existence of the first
826 * one in the "to" list. This implementation doesn't
829 * Also, the previous implementation did NOT implement
830 * "-=" correctly. If two of the same attributes existed
831 * in the "to" list, and you tried to subtract something
832 * matching the *second* value, then the fr_pair_delete_by_num()
833 * function was called, and the *all* attributes of that
834 * number were deleted. With this implementation, only
835 * the matching attributes are deleted.
838 for (vp = fr_cursor_init(&cursor, &from); vp; vp = fr_cursor_next(&cursor)) count++;
839 from_list = talloc_array(request, VALUE_PAIR *, count);
841 for (vp = fr_cursor_init(&cursor, to); vp; vp = fr_cursor_next(&cursor)) count++;
842 to_list = talloc_array(request, VALUE_PAIR *, count);
845 append_tail = &append;
848 * Move the lists to the arrays, and break the list
852 for (vp = from; vp != NULL; vp = next) {
854 from_list[from_count++] = vp;
859 ctx = talloc_parent(*to);
860 to_copy = fr_pair_list_copy(ctx, *to);
861 for (vp = to_copy; vp != NULL; vp = next) {
863 to_list[to_count++] = vp;
867 edited = talloc_zero_array(request, bool, to_count);
869 RDEBUG4("::: FROM %d TO %d MAX %d", from_count, to_count, count);
872 * Now that we have the lists initialized, start working
875 for (i = 0; i < from_count; i++) {
878 RDEBUG4("::: Examining %s", from_list[i]->da->name);
880 if (do_xlat) radius_xlat_do(request, from_list[i]);
883 * Attribute should be appended, OR the "to" list
884 * is empty, and we're supposed to replace or
885 * "add if not existing".
887 if (from_list[i]->op == T_OP_ADD) goto do_append;
890 for (j = 0; j < to_count; j++) {
891 if (edited[j] || !to_list[j] || !from_list[i]) continue;
894 * Attributes aren't the same, skip them.
896 if (from_list[i]->da != to_list[j]->da) {
901 * We don't use a "switch" statement here
902 * because we want to break out of the
903 * "for" loop over 'j' in most cases.
907 * Over-write the FIRST instance of the
908 * matching attribute name. We free the
909 * one in the "to" list, and move over
910 * the one in the "from" list.
912 if (from_list[i]->op == T_OP_SET) {
913 RDEBUG4("::: OVERWRITING %s FROM %d TO %d",
914 to_list[j]->da->name, i, j);
915 fr_pair_list_free(&to_list[j]);
916 to_list[j] = from_list[i];
923 * Add the attribute only if it does not
924 * exist... but it exists, so we stop
927 if (from_list[i]->op == T_OP_EQ) {
933 * Delete every attribute, independent
936 if (from_list[i]->op == T_OP_CMP_FALSE) {
941 * Delete all matching attributes from
944 if ((from_list[i]->op == T_OP_SUB) ||
945 (from_list[i]->op == T_OP_CMP_EQ) ||
946 (from_list[i]->op == T_OP_LE) ||
947 (from_list[i]->op == T_OP_GE)) {
949 int old_op = from_list[i]->op;
952 * Check for equality.
954 from_list[i]->op = T_OP_CMP_EQ;
957 * If equal, delete the one in
960 rcode = radius_compare_vps(NULL, from_list[i],
963 * We may want to do more
964 * subtractions, so we re-set the
965 * operator back to it's original
968 from_list[i]->op = old_op;
972 if (rcode != 0) goto delete;
978 RDEBUG4("::: DELETING %s FROM %d TO %d",
979 from_list[i]->da->name, i, j);
980 fr_pair_list_free(&to_list[j]);
986 * Enforce <=. If it's
991 RDEBUG4("::: REPLACING %s FROM %d TO %d",
992 from_list[i]->da->name, i, j);
993 fr_pair_list_free(&to_list[j]);
994 to_list[j] = from_list[i];
1002 RDEBUG4("::: REPLACING %s FROM %d TO %d",
1003 from_list[i]->da->name, i, j);
1004 fr_pair_list_free(&to_list[j]);
1005 to_list[j] = from_list[i];
1006 from_list[i] = NULL;
1015 rad_assert(0 == 1); /* panic! */
1019 * We were asked to add it if it didn't exist,
1020 * and it doesn't exist. Move it over to the
1021 * tail of the "to" list, UNLESS it was already
1022 * moved by another operator.
1024 if (!found && from_list[i]) {
1025 if ((from_list[i]->op == T_OP_EQ) ||
1026 (from_list[i]->op == T_OP_LE) ||
1027 (from_list[i]->op == T_OP_GE) ||
1028 (from_list[i]->op == T_OP_SET)) {
1030 RDEBUG4("::: APPENDING %s FROM %d TO %d",
1031 from_list[i]->da->name, i, tailto);
1032 *append_tail = from_list[i];
1033 from_list[i]->op = T_OP_EQ;
1034 from_list[i] = NULL;
1035 append_tail = &(*append_tail)->next;
1041 * Delete attributes in the "from" list.
1043 for (i = 0; i < from_count; i++) {
1044 if (!from_list[i]) continue;
1046 fr_pair_list_free(&from_list[i]);
1048 talloc_free(from_list);
1050 RDEBUG4("::: TO in %d out %d", to_count, tailto);
1053 * Re-chain the "to" list.
1055 fr_pair_list_free(to);
1058 if (to == &request->packet->vps) {
1060 } else if (request->parent && (to == &request->parent->packet->vps)) {
1061 fixup = request->parent;
1064 for (i = 0; i < tailto; i++) {
1065 if (!to_list[i]) continue;
1068 RDEBUG4("::: to[%d] = %s", i, vp->da->name);
1071 * Mash the operator to a simple '='. The
1072 * operators in the "to" list aren't used for
1073 * anything. BUT they're used in the "detail"
1074 * file and debug output, where we don't want to
1075 * see the operators.
1080 last = &(*last)->next;
1084 * And finally add in the attributes we're appending to
1085 * the tail of the "to" list.
1090 * Fix dumb cache issues
1093 fixup->username = NULL;
1094 fixup->password = NULL;
1096 for (vp = fixup->packet->vps; vp != NULL; vp = vp->next) {
1097 if (vp->da->vendor) continue;
1099 if ((vp->da->attr == PW_USER_NAME) && !fixup->username) {
1100 fixup->username = vp;
1102 } else if (vp->da->attr == PW_STRIPPED_USER_NAME) {
1103 fixup->username = vp;
1105 } else if (vp->da->attr == PW_USER_PASSWORD) {
1106 fixup->password = vp;
1111 rad_assert(request->packet != NULL);
1113 talloc_free(to_list);
1114 talloc_free(edited);