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>
24 #include <freeradius-devel/ident.h>
27 #include <freeradius-devel/radiusd.h>
28 #include <freeradius-devel/modules.h>
29 #include <freeradius-devel/rad_assert.h>
37 * For POSIX Regular expressions.
38 * (0) Means no extended regular expressions.
39 * REG_EXTENDED means use extended regular expressions.
42 #define REG_EXTENDED (0)
52 static int all_digits(const char *string)
54 const char *p = string;
58 while (isdigit((int) *p)) p++;
63 static const char *filler = "????????????????????????????????????????????????????????????????";
65 static const char *expand_string(char *buffer, size_t sizeof_buffer,
67 FR_TOKEN value_type, const char *value)
75 case T_SINGLE_QUOTED_STRING:
78 case T_BACK_QUOTED_STRING:
79 result = radius_exec_program(value, request, 1,
80 buffer, sizeof_buffer, NULL,
87 * The result should be ASCII.
89 for (p = buffer; *p != '\0'; p++) {
97 case T_DOUBLE_QUOTED_STRING:
98 if (!strchr(value, '%')) return value;
100 radius_xlat(buffer, sizeof_buffer, value, request, NULL);
108 static FR_TOKEN getregex(const char **ptr, char *buffer, size_t buflen,
111 const char *p = *ptr;
114 if (*p != '/') return T_OP_INVALID;
116 *pcflags = REG_EXTENDED;
120 if (buflen <= 1) break;
126 * Check for case insensitivity
130 *pcflags |= REG_ICASE;
160 * FIXME: add 'x' and 'u'
164 if ((p[1] >= '0') && (p[1] <= '9') &&
165 (sscanf(p + 1, "%3o", &x) == 1)) {
184 return T_DOUBLE_QUOTED_STRING;
188 static const FR_NAME_NUMBER modreturn_table[] = {
189 { "reject", RLM_MODULE_REJECT },
190 { "fail", RLM_MODULE_FAIL },
191 { "ok", RLM_MODULE_OK },
192 { "handled", RLM_MODULE_HANDLED },
193 { "invalid", RLM_MODULE_INVALID },
194 { "userlock", RLM_MODULE_USERLOCK },
195 { "notfound", RLM_MODULE_NOTFOUND },
196 { "noop", RLM_MODULE_NOOP },
197 { "updated", RLM_MODULE_UPDATED },
202 int radius_get_vp(REQUEST *request, const char *name, VALUE_PAIR **vp_p)
204 const char *vp_name = name;
205 REQUEST *myrequest = request;
207 VALUE_PAIR *vps = NULL;
212 * Allow for tunneled sessions.
214 if (strncmp(vp_name, "outer.", 6) == 0) {
215 if (!myrequest->parent) return TRUE;
217 myrequest = myrequest->parent;
220 if (strncmp(vp_name, "request:", 8) == 0) {
222 vps = myrequest->packet->vps;
224 } else if (strncmp(vp_name, "reply:", 6) == 0) {
226 vps = myrequest->reply->vps;
229 } else if (strncmp(vp_name, "proxy-request:", 14) == 0) {
231 if (request->proxy) vps = myrequest->proxy->vps;
233 } else if (strncmp(vp_name, "proxy-reply:", 12) == 0) {
235 if (request->proxy_reply) vps = myrequest->proxy_reply->vps;
238 } else if (strncmp(vp_name, "config:", 7) == 0) {
240 vps = myrequest->config_items;
242 } else if (strncmp(vp_name, "control:", 8) == 0) {
244 vps = myrequest->config_items;
247 } else if (strncmp(vp_name, "coa:", 4) == 0) {
250 if (myrequest->coa &&
251 (myrequest->coa->proxy->code == PW_COA_REQUEST)) {
252 vps = myrequest->coa->proxy->vps;
255 } else if (strncmp(vp_name, "coa-reply:", 10) == 0) {
258 if (myrequest->coa && /* match reply with request */
259 (myrequest->coa->proxy->code == PW_COA_REQUEST) &&
260 (myrequest->coa->proxy_reply)) {
261 vps = myrequest->coa->proxy_reply->vps;
264 } else if (strncmp(vp_name, "disconnect:", 11) == 0) {
267 if (myrequest->coa &&
268 (myrequest->coa->proxy->code == PW_DISCONNECT_REQUEST)) {
269 vps = myrequest->coa->proxy->vps;
272 } else if (strncmp(vp_name, "disconnect-reply:", 17) == 0) {
275 if (myrequest->coa && /* match reply with request */
276 (myrequest->coa->proxy->code == PW_DISCONNECT_REQUEST) &&
277 (myrequest->coa->proxy_reply)) {
278 vps = myrequest->coa->proxy_reply->vps;
283 vps = myrequest->packet->vps;
286 da = dict_attrbyname(vp_name);
287 if (!da) return FALSE; /* not a dictionary name */
290 * May not may not be found, but it *is* a known name.
292 *vp_p = pairfind(vps, da->attr, da->vendor);
298 * *presult is "did comparison match or not"
300 static int radius_do_cmp(REQUEST *request, int *presult,
301 FR_TOKEN lt, const char *pleft, FR_TOKEN token,
302 FR_TOKEN rt, const char *pright,
303 int cflags, int modreturn)
307 VALUE_PAIR *vp = NULL;
311 cflags = cflags; /* -Wunused */
314 rt = rt; /* -Wunused */
316 if (lt == T_BARE_WORD) {
318 * Maybe check the last return code.
320 if (token == T_OP_CMP_TRUE) {
324 * Looks like a return code, treat is as such.
326 isreturn = fr_str2int(modreturn_table, pleft, -1);
327 if (isreturn != -1) {
328 *presult = (modreturn == isreturn);
334 * Bare words on the left can be attribute names.
336 if (radius_get_vp(request, pleft, &vp)) {
340 * VP exists, and that's all we're looking for.
342 if (token == T_OP_CMP_TRUE) {
343 *presult = (vp != NULL);
351 * The attribute on the LHS may
352 * have been a dynamically
353 * registered callback. i.e. it
354 * doesn't exist as a VALUE_PAIR.
355 * If so, try looking for it.
357 da = dict_attrbyname(pleft);
358 if (da && (da->vendor == 0) && radius_find_compare(da->attr)) {
359 VALUE_PAIR *check = pairmake(pleft, pright, token);
360 *presult = (radius_callback_compare(request, NULL, check, NULL, NULL) == 0);
361 RDEBUG3(" Callback returns %d",
367 RDEBUG2(" (Attribute %s was not found)",
375 * Regex comparisons treat everything as
378 if ((token == T_OP_REG_EQ) ||
379 (token == T_OP_REG_NE)) {
380 vp_prints_value(buffer, sizeof(buffer), vp, 0);
386 memcpy(&myvp, vp, sizeof(myvp));
387 if (!pairparsevalue(&myvp, pright)) {
388 RDEBUG2("Failed parsing \"%s\": %s",
389 pright, fr_strerror());
393 myvp.operator = token;
394 *presult = paircmp(&myvp, vp);
395 RDEBUG3(" paircmp -> %d", *presult);
397 } /* else it's not a VP in a list */
408 if (!all_digits(pright)) {
409 RDEBUG2(" (Right field is not a number at: %s)", pright);
412 rint = strtoul(pright, NULL, 0);
413 if (!all_digits(pleft)) {
414 RDEBUG2(" (Left field is not a number at: %s)", pleft);
417 lint = strtoul(pleft, NULL, 0);
421 lint = rint = 0; /* quiet the compiler */
428 * Check for truth or falsehood.
430 if (all_digits(pleft)) {
431 lint = strtoul(pleft, NULL, 0);
432 result = (lint != 0);
435 result = (*pleft != '\0');
441 result = (strcmp(pleft, pright) == 0);
445 result = (strcmp(pleft, pright) != 0);
449 result = (lint >= rint);
453 result = (lint > rint);
457 result = (lint <= rint);
461 result = (lint < rint);
468 regmatch_t rxmatch[REQUEST_MAX_REGEX + 1];
471 * Include substring matches.
473 compare = regcomp(®, pright, cflags);
478 regerror(compare, ®, errbuf, sizeof(errbuf));
479 DEBUG("ERROR: Failed compiling regular expression: %s", errbuf);
484 compare = regexec(®, pleft,
485 REQUEST_MAX_REGEX + 1,
490 * Add new %{0}, %{1}, etc.
492 if (compare == 0) for (i = 0; i <= REQUEST_MAX_REGEX; i++) {
495 free(request_data_get(request, request,
496 REQUEST_DATA_REGEX | i));
500 * We MAY have %{2} without %{1}.
502 if (rxmatch[i].rm_so == -1) continue;
505 * Copy substring into allocated buffer
507 r = rad_malloc(rxmatch[i].rm_eo -rxmatch[i].rm_so + 1);
508 memcpy(r, pleft + rxmatch[i].rm_so,
509 rxmatch[i].rm_eo - rxmatch[i].rm_so);
510 r[rxmatch[i].rm_eo - rxmatch[i].rm_so] = '\0';
512 request_data_add(request, request,
513 REQUEST_DATA_REGEX | i,
516 result = (compare == 0);
523 regmatch_t rxmatch[REQUEST_MAX_REGEX + 1];
526 * Include substring matches.
528 compare = regcomp(®, pright, cflags);
533 regerror(compare, ®, errbuf, sizeof(errbuf));
534 DEBUG("ERROR: Failed compiling regular expression: %s", errbuf);
539 compare = regexec(®, pleft,
540 REQUEST_MAX_REGEX + 1,
544 result = (compare != 0);
550 RDEBUG4(">>> NOT IMPLEMENTED %d", token);
560 int radius_evaluate_condition(REQUEST *request, int modreturn, int depth,
561 const char **ptr, int evaluate_it, int *presult)
563 int found_condition = FALSE;
566 int evaluate_next_condition = evaluate_it;
568 const char *q, *start;
569 FR_TOKEN token, lt, rt;
570 char left[1024], right[1024], comp[4];
571 const char *pleft, *pright;
572 char xleft[1024], xright[1024];
575 if (!ptr || !*ptr || (depth >= 64)) {
576 radlog(L_ERR, "Internal sanity check failed in evaluate condition");
585 while ((*p == ' ') || (*p == '\t')) p++;
590 if (!found_condition && (*p == '!')) {
592 * Don't change the results if we're not
593 * evaluating the condition.
595 if (evaluate_next_condition) {
596 RDEBUG4(">>> INVERT");
601 while ((*p == ' ') || (*p == '\t')) p++;
607 if (!found_condition && (*p == '(')) {
608 const char *end = p + 1;
611 * Evaluate the condition, bailing out on
614 RDEBUG4(">>> RECURSING WITH ... %s", end);
615 if (!radius_evaluate_condition(request, modreturn,
617 evaluate_next_condition,
623 if (evaluate_next_condition) {
624 RDEBUG2("%.*s Converting !%s -> %s",
626 (result != FALSE) ? "TRUE" : "FALSE",
627 (result == FALSE) ? "TRUE" : "FALSE");
628 result = (result == FALSE);
634 * Start from the end of the previous
638 RDEBUG4(">>> AFTER RECURSION ... %s", end);
640 while ((*p == ' ') || (*p == '\t')) p++;
643 radlog(L_ERR, "No closing brace");
647 if (*p == ')') p++; /* eat closing brace */
648 found_condition = TRUE;
650 while ((*p == ' ') || (*p == '\t')) p++;
654 * At EOL or closing brace, update && return.
656 if (found_condition && (!*p || (*p == ')'))) break;
666 if (found_condition) {
668 * (A && B) means "evaluate B
669 * only if A was true"
671 if ((p[0] == '&') && (p[1] == '&')) {
672 if (!result) evaluate_next_condition = FALSE;
674 found_condition = FALSE;
675 continue; /* go back to the start */
679 * (A || B) means "evaluate B
680 * only if A was false"
682 if ((p[0] == '|') && (p[1] == '|')) {
683 if (result) evaluate_next_condition = FALSE;
685 found_condition = FALSE;
697 if (found_condition) {
698 radlog(L_ERR, "Consecutive conditions at %s", p);
702 RDEBUG4(">>> LOOKING AT %s", p);
706 * Look for common errors.
708 if ((p[0] == '%') && (p[1] == '{')) {
709 radlog(L_ERR, "Bare %%{...} is invalid in condition at: %s", p);
714 * Look for WORD1 op WORD2
716 lt = gettoken(&p, left, sizeof(left));
717 if ((lt != T_BARE_WORD) &&
718 (lt != T_DOUBLE_QUOTED_STRING) &&
719 (lt != T_SINGLE_QUOTED_STRING) &&
720 (lt != T_BACK_QUOTED_STRING)) {
721 radlog(L_ERR, "Expected string or numbers at: %s", p);
726 if (evaluate_next_condition) {
727 pleft = expand_string(xleft, sizeof(xleft), request,
730 radlog(L_ERR, "Failed expanding string at: %s",
737 * Peek ahead, to see if it's:
741 * or something else, such as
749 while ((*q == ' ') || (*q == '\t')) q++;
752 * If the next thing is:
759 * Then WORD is just a test for existence.
760 * Remember that and skip ahead.
762 if (!*q || (*q == ')') ||
763 ((q[0] == '&') && (q[1] == '&')) ||
764 ((q[0] == '|') && (q[1] == '|'))) {
765 token = T_OP_CMP_TRUE;
776 token = gettoken(&p, comp, sizeof(comp));
777 if ((token < T_OP_NE) || (token > T_OP_CMP_EQ) ||
778 (token == T_OP_CMP_TRUE)) {
779 radlog(L_ERR, "Expected comparison at: %s", comp);
784 * Look for common errors.
786 if ((p[0] == '%') && (p[1] == '{')) {
787 radlog(L_ERR, "Bare %%{...} is invalid in condition at: %s", p);
795 if ((token == T_OP_REG_EQ) ||
796 (token == T_OP_REG_NE)) {
797 rt = getregex(&p, right, sizeof(right), &cflags);
798 if (rt != T_DOUBLE_QUOTED_STRING) {
799 radlog(L_ERR, "Expected regular expression at: %s", p);
804 rt = gettoken(&p, right, sizeof(right));
806 if ((rt != T_BARE_WORD) &&
807 (rt != T_DOUBLE_QUOTED_STRING) &&
808 (rt != T_SINGLE_QUOTED_STRING) &&
809 (rt != T_BACK_QUOTED_STRING)) {
810 radlog(L_ERR, "Expected string or numbers at: %s", p);
815 if (evaluate_next_condition) {
816 pright = expand_string(xright, sizeof(xright), request,
819 radlog(L_ERR, "Failed expanding string at: %s",
825 RDEBUG4(">>> %d:%s %d %d:%s",
826 lt, pleft, token, rt, pright);
829 if (evaluate_next_condition) {
833 if (!radius_do_cmp(request, &result, lt, pleft, token,
834 rt, pright, cflags, modreturn)) {
837 RDEBUG4(">>> Comparison returned %d", result);
840 RDEBUG4(">>> INVERTING result");
841 result = (result == FALSE);
844 RDEBUG2("%.*s Evaluating %s(%.*s) -> %s",
846 invert ? "!" : "", p - start, start,
847 (result != FALSE) ? "TRUE" : "FALSE");
850 RDEBUG4(">>> GOT result %d", result);
853 * Not evaluating it. We may be just
856 } else if (request) {
857 RDEBUG2("%.*s Skipping %s(%.*s)",
859 invert ? "!" : "", p - start, start);
862 found_condition = TRUE;
863 } /* loop over the input condition */
865 if (!found_condition) {
866 radlog(L_ERR, "Syntax error. Expected condition at %s", p);
870 RDEBUG4(">>> AT EOL -> %d", result);
872 if (evaluate_it) *presult = result;
877 static void fix_up(REQUEST *request)
881 request->username = NULL;
882 request->password = NULL;
884 for (vp = request->packet->vps; vp != NULL; vp = vp->next) {
885 if (vp->vendor != 0) continue;
887 if ((vp->attribute == PW_USER_NAME) &&
888 !request->username) {
889 request->username = vp;
891 } else if (vp->attribute == PW_STRIPPED_USER_NAME) {
892 request->username = vp;
894 } else if (vp->attribute == PW_USER_PASSWORD) {
895 request->password = vp;
898 if (request->username && request->password) break;
904 * The pairmove() function in src/lib/valuepair.c does all sorts of
905 * extra magic that we don't want here.
907 * FIXME: integrate this with the code calling it, so that we
908 * only paircopy() those attributes that we're really going to
911 void radius_pairmove(REQUEST *request, VALUE_PAIR **to, VALUE_PAIR *from)
913 int i, j, count, from_count, to_count, tailto;
914 VALUE_PAIR *vp, *next, **last;
915 VALUE_PAIR **from_list, **to_list;
919 * Set up arrays for editing, to remove some of the
920 * O(N^2) dependencies. This also makes it easier to
921 * insert and remove attributes.
923 * It also means that the operators apply ONLY to the
924 * attributes in the original list. With the previous
925 * implementation of pairmove(), adding two attributes
926 * via "+=" and then "=" would mean that the second one
927 * wasn't added, because of the existence of the first
928 * one in the "to" list. This implementation doesn't
931 * Also, the previous implementation did NOT implement
932 * "-=" correctly. If two of the same attributes existed
933 * in the "to" list, and you tried to subtract something
934 * matching the *second* value, then the pairdelete()
935 * function was called, and the *all* attributes of that
936 * number were deleted. With this implementation, only
937 * the matching attributes are deleted.
940 for (vp = from; vp != NULL; vp = vp->next) count++;
941 from_list = rad_malloc(sizeof(*from_list) * count);
943 for (vp = *to; vp != NULL; vp = vp->next) count++;
944 to_list = rad_malloc(sizeof(*to_list) * count);
947 * Move the lists to the arrays, and break the list
951 for (vp = from; vp != NULL; vp = next) {
953 from_list[from_count++] = vp;
958 for (vp = *to; vp != NULL; vp = next) {
960 to_list[to_count++] = vp;
964 edited = rad_malloc(sizeof(*edited) * to_count);
965 memset(edited, 0, sizeof(*edited) * to_count);
967 RDEBUG4("::: FROM %d TO %d MAX %d", from_count, to_count, count);
970 * Now that we have the lists initialized, start working
973 for (i = 0; i < from_count; i++) {
976 RDEBUG4("::: Examining %s", from_list[i]->name);
979 * Attribute should be appended, OR the "to" list
980 * is empty, and we're supposed to replace or
981 * "add if not existing".
983 if (from_list[i]->operator == T_OP_ADD) goto append;
986 for (j = 0; j < to_count; j++) {
987 if (edited[j] || !to_list[j] || !from_list[i]) continue;
990 * Attributes aren't the same, skip them.
992 if ((from_list[i]->attribute != to_list[j]->attribute) ||
993 (from_list[i]->vendor != to_list[j]->vendor)) {
998 * We don't use a "switch" statement here
999 * because we want to break out of the
1000 * "for" loop over 'j' in most cases.
1004 * Over-write the FIRST instance of the
1005 * matching attribute name. We free the
1006 * one in the "to" list, and move over
1007 * the one in the "from" list.
1009 if (from_list[i]->operator == T_OP_SET) {
1010 RDEBUG4("::: OVERWRITING %s FROM %d TO %d",
1011 to_list[j]->name, i, j);
1012 pairfree(&to_list[j]);
1013 to_list[j] = from_list[i];
1014 from_list[i] = NULL;
1020 * Add the attribute only if it does not
1021 * exist... but it exists, so we stop
1024 if (from_list[i]->operator == T_OP_EQ) {
1030 * Delete every attribute, independent
1033 if (from_list[i]->operator == T_OP_CMP_FALSE) {
1038 * Delete all matching attributes from
1041 if ((from_list[i]->operator == T_OP_SUB) ||
1042 (from_list[i]->operator == T_OP_CMP_EQ) ||
1043 (from_list[i]->operator == T_OP_LE) ||
1044 (from_list[i]->operator == T_OP_GE)) {
1046 int old_op = from_list[i]->operator;
1049 * Check for equality.
1051 from_list[i]->operator = T_OP_CMP_EQ;
1054 * If equal, delete the one in
1057 rcode = radius_compare_vps(NULL, from_list[i],
1060 * We may want to do more
1061 * subtractions, so we re-set the
1062 * operator back to it's original
1065 from_list[i]->operator = old_op;
1069 if (rcode != 0) goto delete;
1075 RDEBUG4("::: DELETING %s FROM %d TO %d",
1076 from_list[i]->name, i, j);
1077 pairfree(&to_list[j]);
1083 * Enforce <=. If it's
1088 RDEBUG4("::: REPLACING %s FROM %d TO %d",
1089 from_list[i]->name, i, j);
1090 pairfree(&to_list[j]);
1091 to_list[j] = from_list[i];
1092 from_list[i] = NULL;
1099 RDEBUG4("::: REPLACING %s FROM %d TO %d",
1100 from_list[i]->name, i, j);
1101 pairfree(&to_list[j]);
1102 to_list[j] = from_list[i];
1103 from_list[i] = NULL;
1112 rad_assert(0 == 1); /* panic! */
1116 * We were asked to add it if it didn't exist,
1117 * and it doesn't exist. Move it over to the
1118 * tail of the "to" list, UNLESS it was already
1119 * moved by another operator.
1121 if (!found && from_list[i]) {
1122 if ((from_list[i]->operator == T_OP_EQ) ||
1123 (from_list[i]->operator == T_OP_LE) ||
1124 (from_list[i]->operator == T_OP_GE) ||
1125 (from_list[i]->operator == T_OP_SET)) {
1127 RDEBUG4("::: APPENDING %s FROM %d TO %d",
1128 from_list[i]->name, i, tailto);
1129 to_list[tailto++] = from_list[i];
1130 from_list[i] = NULL;
1136 * Delete attributes in the "from" list.
1138 for (i = 0; i < from_count; i++) {
1139 if (!from_list[i]) continue;
1141 pairfree(&from_list[i]);
1145 RDEBUG4("::: TO in %d out %d", to_count, tailto);
1148 * Re-chain the "to" list.
1153 for (i = 0; i < tailto; i++) {
1154 if (!to_list[i]) continue;
1156 RDEBUG4("::: to[%d] = %s", i, to_list[i]->name);
1159 * Mash the operator to a simple '='. The
1160 * operators in the "to" list aren't used for
1161 * anything. BUT they're used in the "detail"
1162 * file and debug output, where we don't want to
1163 * see the operators.
1165 to_list[i]->operator = T_OP_EQ;
1168 last = &(*last)->next;
1171 rad_assert(request != NULL);
1172 rad_assert(request->packet != NULL);
1175 * Fix dumb cache issues
1177 if (to == &request->packet->vps) {
1179 } else if (request->parent && (to == &request->parent->packet->vps)) {
1180 fix_up(request->parent);
1190 * Copied shamelessly from conffile.c, to simplify the API for
1193 typedef enum conf_type {
1194 CONF_ITEM_INVALID = 0,
1201 struct conf_item *next;
1202 struct conf_part *parent;
1204 const char *filename;
1205 CONF_ITEM_TYPE type;
1212 FR_TOKEN value_type;
1216 * Add attributes to a list.
1218 int radius_update_attrlist(REQUEST *request, CONF_SECTION *cs,
1219 VALUE_PAIR *input_vps, const char *name)
1222 VALUE_PAIR *newlist, *vp;
1223 VALUE_PAIR **output_vps = NULL;
1224 REQUEST *myrequest = request;
1226 if (!request || !cs) return RLM_MODULE_INVALID;
1229 * If we are an inner tunnel session, permit the
1230 * policy to update the outer lists directly.
1232 if (strncmp(name, "outer.", 6) == 0) {
1233 if (!request->parent) return RLM_MODULE_NOOP;
1235 myrequest = request->parent;
1239 if (strcmp(name, "request") == 0) {
1240 output_vps = &myrequest->packet->vps;
1242 } else if (strcmp(name, "reply") == 0) {
1243 output_vps = &myrequest->reply->vps;
1246 } else if (strcmp(name, "proxy-request") == 0) {
1247 if (request->proxy) output_vps = &myrequest->proxy->vps;
1249 } else if (strcmp(name, "proxy-reply") == 0) {
1250 if (request->proxy_reply) output_vps = &request->proxy_reply->vps;
1253 } else if (strcmp(name, "config") == 0) {
1254 output_vps = &myrequest->config_items;
1256 } else if (strcmp(name, "control") == 0) {
1257 output_vps = &myrequest->config_items;
1260 } else if (strcmp(name, "coa") == 0) {
1261 if (!myrequest->coa) {
1262 request_alloc_coa(myrequest);
1263 myrequest->coa->proxy->code = PW_COA_REQUEST;
1266 if (myrequest->coa &&
1267 (myrequest->coa->proxy->code == PW_COA_REQUEST)) {
1268 output_vps = &myrequest->coa->proxy->vps;
1271 } else if (strcmp(name, "coa-reply") == 0) {
1272 if (myrequest->coa && /* match reply with request */
1273 (myrequest->coa->proxy->code == PW_COA_REQUEST) &&
1274 (myrequest->coa->proxy_reply)) {
1275 output_vps = &myrequest->coa->proxy_reply->vps;
1278 } else if (strcmp(name, "disconnect") == 0) {
1279 if (!myrequest->coa) {
1280 request_alloc_coa(myrequest);
1281 if (myrequest->coa) myrequest->coa->proxy->code = PW_DISCONNECT_REQUEST;
1284 if (myrequest->coa &&
1285 (myrequest->coa->proxy->code == PW_DISCONNECT_REQUEST)) {
1286 output_vps = &myrequest->coa->proxy->vps;
1289 } else if (strcmp(name, "disconnect-reply") == 0) {
1290 if (myrequest->coa && /* match reply with request */
1291 (myrequest->coa->proxy->code == PW_DISCONNECT_REQUEST) &&
1292 (myrequest->coa->proxy_reply)) {
1293 output_vps = &myrequest->coa->proxy_reply->vps;
1298 return RLM_MODULE_INVALID;
1301 if (!output_vps) return RLM_MODULE_NOOP; /* didn't update the list */
1303 newlist = paircopy(input_vps);
1305 RDEBUG2("Out of memory");
1306 return RLM_MODULE_FAIL;
1310 for (ci=cf_item_find_next(cs, NULL);
1312 ci=cf_item_find_next(cs, ci)) {
1316 * This should never happen.
1318 if (cf_item_is_section(ci)) {
1320 return RLM_MODULE_INVALID;
1323 cp = cf_itemtopair(ci);
1326 if (debug_flag && (vp->vendor == 0) &&
1327 radius_find_compare(vp->attribute)) {
1328 DEBUG("WARNING: You are modifying the value of virtual attribute %s. This is not supported.", vp->name);
1333 * The VP && CF lists should be in sync. If they're
1336 if (vp->flags.do_xlat) {
1340 value = expand_string(buffer, sizeof(buffer), request,
1341 cp->value_type, cp->value);
1344 return RLM_MODULE_INVALID;
1347 if (!pairparsevalue(vp, value)) {
1348 RDEBUG2("ERROR: Failed parsing value \"%s\" for attribute %s: %s",
1349 value, vp->name, fr_strerror());
1351 return RLM_MODULE_FAIL;
1353 vp->flags.do_xlat = 0;
1358 radius_pairmove(request, output_vps, newlist);
1360 return RLM_MODULE_UPDATED;