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,
84 radlog(L_ERR, "Failed to execute program in string expansion: %s",
90 * The result should be ASCII.
92 for (p = buffer; *p != '\0'; p++) {
100 case T_DOUBLE_QUOTED_STRING:
101 if (!strchr(value, '%')) return value;
103 radius_xlat(buffer, sizeof_buffer, value, request, NULL);
111 static FR_TOKEN getregex(const char **ptr, char *buffer, size_t buflen,
114 const char *p = *ptr;
117 if (*p != '/') return T_OP_INVALID;
119 *pcflags = REG_EXTENDED;
123 if (buflen <= 1) break;
129 * Check for case insensitivity
133 *pcflags |= REG_ICASE;
163 * FIXME: add 'x' and 'u'
167 if ((p[1] >= '0') && (p[1] <= '9') &&
168 (sscanf(p + 1, "%3o", &x) == 1)) {
187 return T_DOUBLE_QUOTED_STRING;
191 static const FR_NAME_NUMBER modreturn_table[] = {
192 { "reject", RLM_MODULE_REJECT },
193 { "fail", RLM_MODULE_FAIL },
194 { "ok", RLM_MODULE_OK },
195 { "handled", RLM_MODULE_HANDLED },
196 { "invalid", RLM_MODULE_INVALID },
197 { "userlock", RLM_MODULE_USERLOCK },
198 { "notfound", RLM_MODULE_NOTFOUND },
199 { "noop", RLM_MODULE_NOOP },
200 { "updated", RLM_MODULE_UPDATED },
205 int radius_get_vp(REQUEST *request, const char *name, VALUE_PAIR **vp_p)
207 const char *vp_name = name;
208 REQUEST *myrequest = request;
210 VALUE_PAIR *vps = NULL;
215 * Allow for tunneled sessions.
217 if (strncmp(vp_name, "outer.", 6) == 0) {
218 if (!myrequest->parent) return TRUE;
220 myrequest = myrequest->parent;
223 if (strncmp(vp_name, "request:", 8) == 0) {
225 vps = myrequest->packet->vps;
227 } else if (strncmp(vp_name, "reply:", 6) == 0) {
229 vps = myrequest->reply->vps;
232 } else if (strncmp(vp_name, "proxy-request:", 14) == 0) {
234 if (request->proxy) vps = myrequest->proxy->vps;
236 } else if (strncmp(vp_name, "proxy-reply:", 12) == 0) {
238 if (request->proxy_reply) vps = myrequest->proxy_reply->vps;
241 } else if (strncmp(vp_name, "config:", 7) == 0) {
243 vps = myrequest->config_items;
245 } else if (strncmp(vp_name, "control:", 8) == 0) {
247 vps = myrequest->config_items;
250 } else if (strncmp(vp_name, "coa:", 4) == 0) {
253 if (myrequest->coa &&
254 (myrequest->coa->proxy->code == PW_COA_REQUEST)) {
255 vps = myrequest->coa->proxy->vps;
258 } else if (strncmp(vp_name, "coa-reply:", 10) == 0) {
261 if (myrequest->coa && /* match reply with request */
262 (myrequest->coa->proxy->code == PW_COA_REQUEST) &&
263 (myrequest->coa->proxy_reply)) {
264 vps = myrequest->coa->proxy_reply->vps;
267 } else if (strncmp(vp_name, "disconnect:", 11) == 0) {
270 if (myrequest->coa &&
271 (myrequest->coa->proxy->code == PW_DISCONNECT_REQUEST)) {
272 vps = myrequest->coa->proxy->vps;
275 } else if (strncmp(vp_name, "disconnect-reply:", 17) == 0) {
278 if (myrequest->coa && /* match reply with request */
279 (myrequest->coa->proxy->code == PW_DISCONNECT_REQUEST) &&
280 (myrequest->coa->proxy_reply)) {
281 vps = myrequest->coa->proxy_reply->vps;
286 vps = myrequest->packet->vps;
289 da = dict_attrbyname(vp_name);
290 if (!da) return FALSE; /* not a dictionary name */
293 * May not may not be found, but it *is* a known name.
295 *vp_p = pairfind(vps, da->attr);
301 * *presult is "did comparison match or not"
303 static int radius_do_cmp(REQUEST *request, int *presult,
304 FR_TOKEN lt, const char *pleft, FR_TOKEN token,
305 FR_TOKEN rt, const char *pright,
306 int cflags, int modreturn)
310 VALUE_PAIR *vp = NULL;
314 cflags = cflags; /* -Wunused */
317 rt = rt; /* -Wunused */
319 if (lt == T_BARE_WORD) {
321 * Maybe check the last return code.
323 if (token == T_OP_CMP_TRUE) {
327 * Looks like a return code, treat is as such.
329 isreturn = fr_str2int(modreturn_table, pleft, -1);
330 if (isreturn != -1) {
331 *presult = (modreturn == isreturn);
337 * Bare words on the left can be attribute names.
339 if (radius_get_vp(request, pleft, &vp)) {
343 * VP exists, and that's all we're looking for.
345 if (token == T_OP_CMP_TRUE) {
346 *presult = (vp != NULL);
354 * The attribute on the LHS may
355 * have been a dynamically
356 * registered callback. i.e. it
357 * doesn't exist as a VALUE_PAIR.
358 * If so, try looking for it.
360 da = dict_attrbyname(pleft);
361 if (da && radius_find_compare(da->attr)) {
362 VALUE_PAIR *check = pairmake(pleft, pright, token);
363 *presult = (radius_callback_compare(request, NULL, check, NULL, NULL) == 0);
364 RDEBUG3(" Callback returns %d",
370 RDEBUG2(" (Attribute %s was not found)",
378 * Regex comparisons treat everything as
381 if ((token == T_OP_REG_EQ) ||
382 (token == T_OP_REG_NE)) {
383 vp_prints_value(buffer, sizeof(buffer), vp, 0);
389 memcpy(&myvp, vp, sizeof(myvp));
390 if (!pairparsevalue(&myvp, pright)) {
391 RDEBUG2("Failed parsing \"%s\": %s",
392 pright, fr_strerror());
396 myvp.operator = token;
397 *presult = paircmp(&myvp, vp);
398 RDEBUG3(" paircmp -> %d", *presult);
400 } /* else it's not a VP in a list */
411 if (!all_digits(pright)) {
412 RDEBUG2(" (Right field is not a number at: %s)", pright);
415 rint = strtoul(pright, NULL, 0);
416 if (!all_digits(pleft)) {
417 RDEBUG2(" (Left field is not a number at: %s)", pleft);
420 lint = strtoul(pleft, NULL, 0);
424 lint = rint = 0; /* quiet the compiler */
431 * Check for truth or falsehood.
433 if (all_digits(pleft)) {
434 lint = strtoul(pleft, NULL, 0);
435 result = (lint != 0);
438 result = (*pleft != '\0');
444 result = (strcmp(pleft, pright) == 0);
448 result = (strcmp(pleft, pright) != 0);
452 result = (lint >= rint);
456 result = (lint > rint);
460 result = (lint <= rint);
464 result = (lint < rint);
471 regmatch_t rxmatch[REQUEST_MAX_REGEX + 1];
474 * Include substring matches.
476 compare = regcomp(®, pright, cflags);
481 regerror(compare, ®, errbuf, sizeof(errbuf));
482 DEBUG("ERROR: Failed compiling regular expression: %s", errbuf);
487 compare = regexec(®, pleft,
488 REQUEST_MAX_REGEX + 1,
493 * Add new %{0}, %{1}, etc.
495 if (compare == 0) for (i = 0; i <= REQUEST_MAX_REGEX; i++) {
498 free(request_data_get(request, request,
499 REQUEST_DATA_REGEX | i));
503 * We MAY have %{2} without %{1}.
505 if (rxmatch[i].rm_so == -1) continue;
508 * Copy substring into allocated buffer
510 r = rad_malloc(rxmatch[i].rm_eo -rxmatch[i].rm_so + 1);
511 memcpy(r, pleft + rxmatch[i].rm_so,
512 rxmatch[i].rm_eo - rxmatch[i].rm_so);
513 r[rxmatch[i].rm_eo - rxmatch[i].rm_so] = '\0';
515 request_data_add(request, request,
516 REQUEST_DATA_REGEX | i,
519 result = (compare == 0);
526 regmatch_t rxmatch[REQUEST_MAX_REGEX + 1];
529 * Include substring matches.
531 compare = regcomp(®, pright, cflags);
536 regerror(compare, ®, errbuf, sizeof(errbuf));
537 DEBUG("ERROR: Failed compiling regular expression: %s", errbuf);
542 compare = regexec(®, pleft,
543 REQUEST_MAX_REGEX + 1,
547 result = (compare != 0);
553 DEBUG("ERROR: Comparison operator %s is not supported",
554 fr_token_name(token));
564 int radius_evaluate_condition(REQUEST *request, int modreturn, int depth,
565 const char **ptr, int evaluate_it, int *presult)
567 int found_condition = FALSE;
570 int evaluate_next_condition = evaluate_it;
572 const char *q, *start;
573 FR_TOKEN token, lt, rt;
574 char left[1024], right[1024], comp[4];
575 const char *pleft, *pright;
576 char xleft[1024], xright[1024];
579 if (!ptr || !*ptr || (depth >= 64)) {
580 radlog(L_ERR, "Internal sanity check failed in evaluate condition");
589 while ((*p == ' ') || (*p == '\t')) p++;
594 if (!found_condition && (*p == '!')) {
596 * Don't change the results if we're not
597 * evaluating the condition.
599 if (evaluate_next_condition) {
600 RDEBUG4(">>> INVERT");
605 while ((*p == ' ') || (*p == '\t')) p++;
611 if (!found_condition && (*p == '(')) {
612 const char *end = p + 1;
615 * Evaluate the condition, bailing out on
618 RDEBUG4(">>> RECURSING WITH ... %s", end);
619 if (!radius_evaluate_condition(request, modreturn,
621 evaluate_next_condition,
627 if (evaluate_next_condition) {
628 RDEBUG2("%.*s Converting !%s -> %s",
630 (result != FALSE) ? "TRUE" : "FALSE",
631 (result == FALSE) ? "TRUE" : "FALSE");
632 result = (result == FALSE);
638 * Start from the end of the previous
642 RDEBUG4(">>> AFTER RECURSION ... %s", end);
644 while ((*p == ' ') || (*p == '\t')) p++;
647 radlog(L_ERR, "No closing brace");
651 if (*p == ')') p++; /* eat closing brace */
652 found_condition = TRUE;
654 while ((*p == ' ') || (*p == '\t')) p++;
658 * At EOL or closing brace, update && return.
660 if (found_condition && (!*p || (*p == ')'))) break;
670 if (found_condition) {
672 * (A && B) means "evaluate B
673 * only if A was true"
675 if ((p[0] == '&') && (p[1] == '&')) {
676 if (!result) evaluate_next_condition = FALSE;
678 found_condition = FALSE;
679 continue; /* go back to the start */
683 * (A || B) means "evaluate B
684 * only if A was false"
686 if ((p[0] == '|') && (p[1] == '|')) {
687 if (result) evaluate_next_condition = FALSE;
689 found_condition = FALSE;
701 if (found_condition) {
702 radlog(L_ERR, "Consecutive conditions at %s", p);
706 RDEBUG4(">>> LOOKING AT %s", p);
710 * Look for common errors.
712 if ((p[0] == '%') && (p[1] == '{')) {
713 radlog(L_ERR, "Bare %%{...} is invalid in condition at: %s", p);
718 * Look for WORD1 op WORD2
720 lt = gettoken(&p, left, sizeof(left));
721 if ((lt != T_BARE_WORD) &&
722 (lt != T_DOUBLE_QUOTED_STRING) &&
723 (lt != T_SINGLE_QUOTED_STRING) &&
724 (lt != T_BACK_QUOTED_STRING)) {
725 radlog(L_ERR, "Expected string or numbers at: %s", p);
730 if (evaluate_next_condition) {
731 pleft = expand_string(xleft, sizeof(xleft), request,
739 * Peek ahead, to see if it's:
743 * or something else, such as
751 while ((*q == ' ') || (*q == '\t')) q++;
754 * If the next thing is:
761 * Then WORD is just a test for existence.
762 * Remember that and skip ahead.
764 if (!*q || (*q == ')') ||
765 ((q[0] == '&') && (q[1] == '&')) ||
766 ((q[0] == '|') && (q[1] == '|'))) {
767 token = T_OP_CMP_TRUE;
778 token = gettoken(&p, comp, sizeof(comp));
779 if ((token < T_OP_NE) || (token > T_OP_CMP_EQ) ||
780 (token == T_OP_CMP_TRUE)) {
781 radlog(L_ERR, "Expected comparison at: %s", comp);
786 * Look for common errors.
788 if ((p[0] == '%') && (p[1] == '{')) {
789 radlog(L_ERR, "Bare %%{...} is invalid in condition at: %s", p);
797 if ((token == T_OP_REG_EQ) ||
798 (token == T_OP_REG_NE)) {
799 rt = getregex(&p, right, sizeof(right), &cflags);
800 if (rt != T_DOUBLE_QUOTED_STRING) {
801 radlog(L_ERR, "Expected regular expression at: %s", p);
806 rt = gettoken(&p, right, sizeof(right));
808 if ((rt != T_BARE_WORD) &&
809 (rt != T_DOUBLE_QUOTED_STRING) &&
810 (rt != T_SINGLE_QUOTED_STRING) &&
811 (rt != T_BACK_QUOTED_STRING)) {
812 radlog(L_ERR, "Expected string or numbers at: %s", p);
817 if (evaluate_next_condition) {
818 pright = expand_string(xright, sizeof(xright), request,
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->attribute == PW_USER_NAME) &&
886 !request->username) {
887 request->username = vp;
889 } else if (vp->attribute == PW_STRIPPED_USER_NAME) {
890 request->username = vp;
892 } else if (vp->attribute == PW_USER_PASSWORD) {
893 request->password = vp;
900 * The pairmove() function in src/lib/valuepair.c does all sorts of
901 * extra magic that we don't want here.
903 * FIXME: integrate this with the code calling it, so that we
904 * only paircopy() those attributes that we're really going to
907 void radius_pairmove(REQUEST *request, VALUE_PAIR **to, VALUE_PAIR *from)
909 int i, j, count, from_count, to_count, tailto;
910 VALUE_PAIR *vp, *next, **last;
911 VALUE_PAIR **from_list, **to_list;
915 * Set up arrays for editing, to remove some of the
916 * O(N^2) dependencies. This also makes it easier to
917 * insert and remove attributes.
919 * It also means that the operators apply ONLY to the
920 * attributes in the original list. With the previous
921 * implementation of pairmove(), adding two attributes
922 * via "+=" and then "=" would mean that the second one
923 * wasn't added, because of the existence of the first
924 * one in the "to" list. This implementation doesn't
927 * Also, the previous implementation did NOT implement
928 * "-=" correctly. If two of the same attributes existed
929 * in the "to" list, and you tried to subtract something
930 * matching the *second* value, then the pairdelete()
931 * function was called, and the *all* attributes of that
932 * number were deleted. With this implementation, only
933 * the matching attributes are deleted.
936 for (vp = from; vp != NULL; vp = vp->next) count++;
937 from_list = rad_malloc(sizeof(*from_list) * count);
939 for (vp = *to; vp != NULL; vp = vp->next) count++;
940 to_list = rad_malloc(sizeof(*to_list) * count);
943 * Move the lists to the arrays, and break the list
947 for (vp = from; vp != NULL; vp = next) {
949 from_list[from_count++] = vp;
954 for (vp = *to; vp != NULL; vp = next) {
956 to_list[to_count++] = vp;
960 edited = rad_malloc(sizeof(*edited) * to_count);
961 memset(edited, 0, sizeof(*edited) * to_count);
963 RDEBUG4("::: FROM %d TO %d MAX %d", from_count, to_count, count);
966 * Now that we have the lists initialized, start working
969 for (i = 0; i < from_count; i++) {
972 RDEBUG4("::: Examining %s", from_list[i]->name);
975 * Attribute should be appended, OR the "to" list
976 * is empty, and we're supposed to replace or
977 * "add if not existing".
979 if (from_list[i]->operator == T_OP_ADD) goto append;
982 for (j = 0; j < to_count; j++) {
983 if (edited[j] || !to_list[j] || !from_list[i]) continue;
986 * Attributes aren't the same, skip them.
988 if (from_list[i]->attribute != to_list[j]->attribute) {
993 * We don't use a "switch" statement here
994 * because we want to break out of the
995 * "for" loop over 'j' in most cases.
999 * Over-write the FIRST instance of the
1000 * matching attribute name. We free the
1001 * one in the "to" list, and move over
1002 * the one in the "from" list.
1004 if (from_list[i]->operator == T_OP_SET) {
1005 RDEBUG4("::: OVERWRITING %s FROM %d TO %d",
1006 to_list[j]->name, i, j);
1007 pairfree(&to_list[j]);
1008 to_list[j] = from_list[i];
1009 from_list[i] = NULL;
1015 * Add the attribute only if it does not
1016 * exist... but it exists, so we stop
1019 if (from_list[i]->operator == T_OP_EQ) {
1025 * Delete every attribute, independent
1028 if (from_list[i]->operator == T_OP_CMP_FALSE) {
1033 * Delete all matching attributes from
1036 if ((from_list[i]->operator == T_OP_SUB) ||
1037 (from_list[i]->operator == T_OP_CMP_EQ) ||
1038 (from_list[i]->operator == T_OP_LE) ||
1039 (from_list[i]->operator == T_OP_GE)) {
1041 int old_op = from_list[i]->operator;
1044 * Check for equality.
1046 from_list[i]->operator = T_OP_CMP_EQ;
1049 * If equal, delete the one in
1052 rcode = radius_compare_vps(NULL, from_list[i],
1055 * We may want to do more
1056 * subtractions, so we re-set the
1057 * operator back to it's original
1060 from_list[i]->operator = old_op;
1064 if (rcode != 0) goto delete;
1070 RDEBUG4("::: DELETING %s FROM %d TO %d",
1071 from_list[i]->name, i, j);
1072 pairfree(&to_list[j]);
1078 * Enforce <=. If it's
1083 RDEBUG4("::: REPLACING %s FROM %d TO %d",
1084 from_list[i]->name, i, j);
1085 pairfree(&to_list[j]);
1086 to_list[j] = from_list[i];
1087 from_list[i] = NULL;
1094 RDEBUG4("::: REPLACING %s FROM %d TO %d",
1095 from_list[i]->name, i, j);
1096 pairfree(&to_list[j]);
1097 to_list[j] = from_list[i];
1098 from_list[i] = NULL;
1107 rad_assert(0 == 1); /* panic! */
1111 * We were asked to add it if it didn't exist,
1112 * and it doesn't exist. Move it over to the
1113 * tail of the "to" list, UNLESS it was already
1114 * moved by another operator.
1116 if (!found && from_list[i]) {
1117 if ((from_list[i]->operator == T_OP_EQ) ||
1118 (from_list[i]->operator == T_OP_LE) ||
1119 (from_list[i]->operator == T_OP_GE) ||
1120 (from_list[i]->operator == T_OP_SET)) {
1122 RDEBUG4("::: APPENDING %s FROM %d TO %d",
1123 from_list[i]->name, i, tailto);
1124 to_list[tailto++] = from_list[i];
1125 from_list[i] = NULL;
1131 * Delete attributes in the "from" list.
1133 for (i = 0; i < from_count; i++) {
1134 if (!from_list[i]) continue;
1136 pairfree(&from_list[i]);
1140 RDEBUG4("::: TO in %d out %d", to_count, tailto);
1143 * Re-chain the "to" list.
1148 for (i = 0; i < tailto; i++) {
1149 if (!to_list[i]) continue;
1151 RDEBUG4("::: to[%d] = %s", i, to_list[i]->name);
1154 * Mash the operator to a simple '='. The
1155 * operators in the "to" list aren't used for
1156 * anything. BUT they're used in the "detail"
1157 * file and debug output, where we don't want to
1158 * see the operators.
1160 to_list[i]->operator = T_OP_EQ;
1163 last = &(*last)->next;
1166 rad_assert(request != NULL);
1167 rad_assert(request->packet != NULL);
1170 * Fix dumb cache issues
1172 if (to == &request->packet->vps) {
1174 } else if (request->parent && (to == &request->parent->packet->vps)) {
1175 fix_up(request->parent);
1185 * Copied shamelessly from conffile.c, to simplify the API for
1188 typedef enum conf_type {
1189 CONF_ITEM_INVALID = 0,
1196 struct conf_item *next;
1197 struct conf_part *parent;
1199 const char *filename;
1200 CONF_ITEM_TYPE type;
1207 FR_TOKEN value_type;
1211 * Add attributes to a list.
1213 int radius_update_attrlist(REQUEST *request, CONF_SECTION *cs,
1214 VALUE_PAIR *input_vps, const char *name)
1217 VALUE_PAIR *newlist, *vp;
1218 VALUE_PAIR **output_vps = NULL;
1219 REQUEST *myrequest = request;
1221 if (!request || !cs) return RLM_MODULE_INVALID;
1224 * If we are an inner tunnel session, permit the
1225 * policy to update the outer lists directly.
1227 if (strncmp(name, "outer.", 6) == 0) {
1228 if (!request->parent) return RLM_MODULE_NOOP;
1230 myrequest = request->parent;
1234 if (strcmp(name, "request") == 0) {
1235 output_vps = &myrequest->packet->vps;
1237 } else if (strcmp(name, "reply") == 0) {
1238 output_vps = &myrequest->reply->vps;
1241 } else if (strcmp(name, "proxy-request") == 0) {
1242 if (request->proxy) output_vps = &myrequest->proxy->vps;
1244 } else if (strcmp(name, "proxy-reply") == 0) {
1245 if (request->proxy_reply) output_vps = &request->proxy_reply->vps;
1248 } else if (strcmp(name, "config") == 0) {
1249 output_vps = &myrequest->config_items;
1251 } else if (strcmp(name, "control") == 0) {
1252 output_vps = &myrequest->config_items;
1255 } else if (strcmp(name, "coa") == 0) {
1256 if (!myrequest->coa) {
1257 request_alloc_coa(myrequest);
1258 myrequest->coa->proxy->code = PW_COA_REQUEST;
1261 if (myrequest->coa &&
1262 (myrequest->coa->proxy->code == PW_COA_REQUEST)) {
1263 output_vps = &myrequest->coa->proxy->vps;
1266 } else if (strcmp(name, "coa-reply") == 0) {
1267 if (myrequest->coa && /* match reply with request */
1268 (myrequest->coa->proxy->code == PW_COA_REQUEST) &&
1269 (myrequest->coa->proxy_reply)) {
1270 output_vps = &myrequest->coa->proxy_reply->vps;
1273 } else if (strcmp(name, "disconnect") == 0) {
1274 if (!myrequest->coa) {
1275 request_alloc_coa(myrequest);
1276 if (myrequest->coa) myrequest->coa->proxy->code = PW_DISCONNECT_REQUEST;
1279 if (myrequest->coa &&
1280 (myrequest->coa->proxy->code == PW_DISCONNECT_REQUEST)) {
1281 output_vps = &myrequest->coa->proxy->vps;
1284 } else if (strcmp(name, "disconnect-reply") == 0) {
1285 if (myrequest->coa && /* match reply with request */
1286 (myrequest->coa->proxy->code == PW_DISCONNECT_REQUEST) &&
1287 (myrequest->coa->proxy_reply)) {
1288 output_vps = &myrequest->coa->proxy_reply->vps;
1293 return RLM_MODULE_INVALID;
1296 if (!output_vps) return RLM_MODULE_NOOP; /* didn't update the list */
1298 newlist = paircopy(input_vps);
1300 RDEBUG2("Out of memory");
1301 return RLM_MODULE_FAIL;
1305 for (ci=cf_item_find_next(cs, NULL);
1307 ci=cf_item_find_next(cs, ci)) {
1311 * This should never happen.
1313 if (cf_item_is_section(ci)) {
1315 return RLM_MODULE_INVALID;
1318 cp = cf_itemtopair(ci);
1321 if (debug_flag && radius_find_compare(vp->attribute)) {
1322 DEBUG("WARNING: You are modifying the value of virtual attribute %s. This is not supported.", vp->name);
1327 * The VP && CF lists should be in sync. If they're
1330 if (vp->flags.do_xlat) {
1334 value = expand_string(buffer, sizeof(buffer), request,
1335 cp->value_type, cp->value);
1338 return RLM_MODULE_INVALID;
1341 if (!pairparsevalue(vp, value)) {
1342 RDEBUG2("ERROR: Failed parsing value \"%s\" for attribute %s: %s",
1343 value, vp->name, fr_strerror());
1345 return RLM_MODULE_FAIL;
1347 vp->flags.do_xlat = 0;
1352 radius_pairmove(request, output_vps, newlist);
1354 return RLM_MODULE_UPDATED;