Pulled fix from branch_1_1
[freeradius.git] / src / main / evaluate.c
1 /*
2  * evaluate.c   Evaluate complex conditions
3  *
4  * Version:     $Id$
5  *
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.
10  *
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.
15  *
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
19  *
20  * Copyright 2007  The FreeRADIUS server project
21  * Copyright 2007  Alan DeKok <aland@deployingradius.com>
22  */
23
24 #include <freeradius-devel/ident.h>
25 RCSID("$Id$")
26
27 #include <freeradius-devel/radiusd.h>
28 #include <freeradius-devel/modules.h>
29 #include <freeradius-devel/rad_assert.h>
30
31 #include <ctype.h>
32
33 #ifdef HAVE_REGEX_H
34 #include <regex.h>
35
36 /*
37  *  For POSIX Regular expressions.
38  *  (0) Means no extended regular expressions.
39  *  REG_EXTENDED means use extended regular expressions.
40  */
41 #ifndef REG_EXTENDED
42 #define REG_EXTENDED (0)
43 #endif
44
45 #ifndef REG_NOSUB
46 #define REG_NOSUB (0)
47 #endif
48 #endif
49
50
51 static int all_digits(const char *string)
52 {
53         const char *p = string;
54
55         if (*p == '-') p++;
56
57         while (isdigit((int) *p)) p++;
58
59         return (*p == '\0');
60 }
61
62 #ifndef NDEBUG
63 #ifndef DEBUG4
64 #define DEBUG4  if (debug_flag > 4)log_debug
65 #endif
66 #else
67 #define DEBUG4 if (0) log_debug
68 #endif
69
70 static const char *filler = "????????????????????????????????????????????????????????????????";
71
72 static const char *expand_string(char *buffer, size_t sizeof_buffer,
73                                  REQUEST *request,
74                                  LRAD_TOKEN value_type, const char *value)
75 {
76         int result;
77         char *p;
78
79         switch (value_type) {
80         default:
81         case T_BARE_WORD:
82         case T_SINGLE_QUOTED_STRING:
83                 return value;
84
85         case T_BACK_QUOTED_STRING:
86                 result = radius_exec_program(value, request, 1,
87                                              buffer, sizeof_buffer, NULL,
88                                              NULL, 0);
89                 if (result != 0) {
90                         return NULL;
91                 }
92
93                 /*
94                  *      The result should be ASCII.
95                  */
96                 for (p = buffer; *p != '\0'; p++) {
97                         if (*p < ' ' ) {
98                                 *p = '\0';
99                                 return buffer;
100                         }
101                 }
102                 return buffer;
103
104
105         case T_DOUBLE_QUOTED_STRING:
106                 if (!strchr(value, '%')) return value;
107
108                 radius_xlat(buffer, sizeof_buffer, value, request, NULL);
109                 return buffer;
110         }
111
112         return NULL;
113 }
114
115 #ifdef HAVE_REGEX_H
116 static LRAD_TOKEN getregex(char **ptr, char *buffer, size_t buflen, int *pcflags)
117 {
118         char *p = *ptr;
119         char *q = buffer;
120
121         if (*p != '/') return T_OP_INVALID;
122
123         *pcflags = REG_EXTENDED;
124
125         p++;
126         while (*p) {
127                 if (buflen <= 1) break;
128
129                 if (*p == '/') {
130                         p++;
131
132                         /*
133                          *      Check for case insensitivity
134                          */
135                         if (*p == 'i') {
136                                 p++;
137                                 *pcflags |= REG_ICASE;
138                         }
139
140                         break;
141                 }
142
143                 if (*p == '\\') {
144                         int x;
145                         
146                         switch (p[1]) {
147                         case 'r':
148                                 *q++ = '\r';
149                                 break;
150                         case 'n':
151                                 *q++ = '\n';
152                                 break;
153                         case 't':
154                                 *q++ = '\t';
155                                 break;
156                         case '"':
157                                 *q++ = '"';
158                                 break;
159                         case '\'':
160                                 *q++ = '\'';
161                                 break;
162                         case '`':
163                                 *q++ = '`';
164                                 break;
165                                 
166                                 /*
167                                  *      FIXME: add 'x' and 'u'
168                                  */
169
170                         default:
171                                 if ((p[1] >= '0') && (p[1] <= '9') &&
172                                     (sscanf(p, "%3o", &x) == 1)) {
173                                         *q++ = x;
174                                         p += 2;
175                                 } else {
176                                         *q++ = p[1];
177                                 }
178                                 break;
179                         }
180                         p += 2;
181                         buflen--;
182                         continue;
183                 }
184
185                 *(q++) = *(p++);
186                 buflen--;
187         }
188         *q = '\0';
189         *ptr = p;
190
191         return T_BARE_WORD;
192 }
193 #endif
194
195 static const LRAD_NAME_NUMBER modreturn_table[] = {
196         { "reject",     RLM_MODULE_REJECT       },
197         { "fail",       RLM_MODULE_FAIL         },
198         { "ok",         RLM_MODULE_OK           },
199         { "handled",    RLM_MODULE_HANDLED      },
200         { "invalid",    RLM_MODULE_INVALID      },
201         { "userlock",   RLM_MODULE_USERLOCK     },
202         { "notfound",   RLM_MODULE_NOTFOUND     },
203         { "noop",       RLM_MODULE_NOOP         },
204         { "updated",    RLM_MODULE_UPDATED      },
205         { NULL, 0 }
206 };
207
208 int radius_evaluate_condition(REQUEST *request, int modreturn, int depth,
209                               const char **ptr, int evaluate_it, int *presult)
210 {
211         int found_condition = FALSE;
212         int result = TRUE;
213         int invert = FALSE;
214         int evaluate_next_condition = evaluate_it;
215         const char *p = *ptr;
216         const char *q, *start;
217         LRAD_TOKEN token, lt, rt;
218         char left[1024], right[1024], comp[4];
219         const char *pleft, *pright;
220         char  xleft[1024], xright[1024];
221         int lint, rint;
222 #ifdef HAVE_REGEX_H
223         int cflags = 0;
224 #endif
225
226         if (!ptr || !*ptr || (depth >= 64)) {
227                 radlog(L_ERR, "Internal sanity check failed in evaluate condition");
228                 return FALSE;
229         }
230
231         while (*p) {
232                 while ((*p == ' ') || (*p == '\t')) p++;
233
234                 if (*p == '!') {
235                         DEBUG4(">>> INVERT");
236                         invert = TRUE;
237                         p++;
238                 }
239
240                 /*
241                  *      It's a subcondition.
242                  */
243                 if (*p == '(') {
244                         const char *end = p + 1;
245
246                         /*
247                          *      Evaluate the condition, bailing out on
248                          *      parse error.
249                          */
250                         DEBUG4(">>> CALLING EVALUATE %s", end);
251                         if (!radius_evaluate_condition(request, modreturn,
252                                                        depth + 1, &end,
253                                                        evaluate_next_condition,
254                                                        &result)) {
255                                 return FALSE;
256                         }
257
258                         if (invert && evaluate_next_condition) {
259                                 DEBUG2("%.*s Converting !%s -> %s",
260                                        depth, filler,
261                                        (result != FALSE) ? "TRUE" : "FALSE",
262                                        (result == FALSE) ? "TRUE" : "FALSE");
263
264                                        
265                                 result = (result == FALSE);
266                                 invert = FALSE;
267                         }
268
269                         /*
270                          *      Start from the end of the previous
271                          *      condition
272                          */
273                         p = end;
274                         DEBUG4(">>> EVALUATE RETURNED ::%s::", end);
275                         
276                         if (!((*p == ')') || (*p == '!') ||
277                               ((p[0] == '&') && (p[1] == '&')) ||
278                               ((p[0] == '|') && (p[1] == '|')))) {
279
280                                 radlog(L_ERR, "Parse error in condition at: %s", p);
281                                 return FALSE;
282                         }
283                         if (*p == ')') p++;     /* skip it */
284                         found_condition = TRUE;
285                         
286                         while ((*p == ' ') || (*p == '\t')) p++;
287
288                         /*
289                          *      EOL.  It's OK.
290                          */
291                         if (!*p) {
292                                 DEBUG4(">>> AT EOL");
293                                 *ptr = p;
294                                 *presult = result;
295                                 return TRUE;
296                                 
297                                 /*
298                                  *      (A && B) means "evaluate B
299                                  *      only if A was true"
300                                  */
301                         } else if ((p[0] == '&') && (p[1] == '&')) {
302                                 if (result == TRUE) {
303                                         evaluate_next_condition = evaluate_it;
304                                 } else {
305                                         evaluate_next_condition = FALSE;
306                                 }
307                                 p += 2;
308                                 
309                                 /*
310                                  *      (A || B) means "evaluate B
311                                  *      only if A was false"
312                                  */
313                         } else if ((p[0] == '|') && (p[1] == '|')) {
314                                 if (result == FALSE) {
315                                         evaluate_next_condition = evaluate_it;
316                                 } else {
317                                         evaluate_next_condition = FALSE;
318                                 }
319                                 p += 2;
320
321                         } else if (*p == ')') {
322                                 DEBUG4(">>> CLOSING BRACE");
323                                 *ptr = p;
324                                 *presult = result;
325                                 return TRUE;
326
327                         } else {
328                                 /*
329                                  *      Parse error
330                                  */
331                                 radlog(L_ERR, "Unexpected trailing text at: %s", p);
332                                 return FALSE;
333                         }
334                 } /* else it wasn't an opening brace */
335
336                 while ((*p == ' ') || (*p == '\t')) p++;
337
338                 /*
339                  *      More conditions, keep going.
340                  */
341                 if ((*p == '(') || (p[0] == '!')) continue;
342
343                 DEBUG4(">>> LOOKING AT %s", p);
344                 start = p;
345
346                 /*
347                  *      Look for common errors.
348                  */
349                 if ((p[0] == '%') && (p[1] == '{')) {
350                         radlog(L_ERR, "Bare %%{...} is invalid in condition at: %s", p);
351                         return FALSE;
352                 }
353
354                 /*
355                  *      Look for word == value
356                  */
357                 lt = gettoken(&p, left, sizeof(left));
358                 if ((lt != T_BARE_WORD) &&
359                     (lt != T_DOUBLE_QUOTED_STRING) &&
360                     (lt != T_SINGLE_QUOTED_STRING) &&
361                     (lt != T_BACK_QUOTED_STRING)) {
362                         radlog(L_ERR, "Expected string or numbers at: %s", p);
363                         return FALSE;
364                 }
365
366                 pleft = left;
367                 if (evaluate_next_condition) {
368                         pleft = expand_string(xleft, sizeof(xleft), request,
369                                               lt, left);
370                         if (!pleft) {
371                                 radlog(L_ERR, "Failed expanding string at: %s",
372                                        left);
373                                 return FALSE;
374                         }
375                 }
376
377                 /*
378                  *      Peek ahead.  Maybe it's just a check for
379                  *      existence.  If so, there shouldn't be anything
380                  *      else.
381                  */
382                 q = p;
383                 while ((*q == ' ') || (*q == '\t')) q++;
384
385                 /*
386                  *      End of condition, 
387                  */
388                 if (!*q || (*q == ')') ||
389                     ((*q == '!') && (q[1] != '=') && (q[1] != '~')) ||
390                     ((q[0] == '&') && (q[1] == '&')) ||
391                     ((q[0] == '|') && (q[1] == '|'))) {
392                         /*
393                          *      Check for truth or falsehood.
394                          */
395                         if (all_digits(pleft)) {
396                                 lint = atoi(pleft);
397                                 result = (lint != 0);
398
399                         } else if (lt == T_BARE_WORD) {
400                                 result = (modreturn == lrad_str2int(modreturn_table, pleft, -1));
401                         } else {
402                                 result = (*pleft != '\0');
403                         }
404
405                         if (invert) {
406                                 DEBUG4(">>> INVERTING result");
407                                 result = (result == FALSE);
408                                 invert = FALSE;
409                         }
410
411                         if (evaluate_next_condition) {
412                                 DEBUG2("%.*s Evaluating %s\"%s\" -> %s",
413                                        depth, filler,
414                                        invert ? "!" : "", pleft,
415                                        (result != FALSE) ? "TRUE" : "FALSE");
416
417                         } else if (request) {
418                                 DEBUG2("%.*s Skipping %s\"%s\"",
419                                        depth, filler,
420                                        invert ? "!" : "", pleft);
421                         }
422
423                         DEBUG4(">>> I%d %d:%s", invert,
424                                lt, left);
425                         goto end_of_condition;
426                 }
427
428                 /*
429                  *      Else it's a full "foo == bar" thingy.
430                  */
431                 token = gettoken(&p, comp, sizeof(comp));
432                 if ((token < T_OP_NE) || (token > T_OP_CMP_EQ) ||
433                     (token == T_OP_CMP_TRUE) ||
434                     (token == T_OP_CMP_FALSE)) {
435                         radlog(L_ERR, "Expected comparison at: %s", comp);
436                         return FALSE;
437                 }
438                 
439                 /*
440                  *      Look for common errors.
441                  */
442                 if ((p[0] == '%') && (p[1] == '{')) {
443                         radlog(L_ERR, "Bare %%{...} is invalid in condition at: %s", p);
444                         return FALSE;
445                 }
446                 
447                 /*
448                  *      Validate strings.
449                  */
450 #ifdef HAVE_REGEX_H
451                 if ((token == T_OP_REG_EQ) ||
452                     (token == T_OP_REG_NE)) {
453                         rt = getregex(&p, right, sizeof(right), &cflags);
454                 } else
455 #endif
456                         rt = gettoken(&p, right, sizeof(right));
457
458                 if ((rt != T_BARE_WORD) &&
459                     (rt != T_DOUBLE_QUOTED_STRING) &&
460                     (rt != T_SINGLE_QUOTED_STRING) &&
461                     (rt != T_BACK_QUOTED_STRING)) {
462                         radlog(L_ERR, "Expected string or numbers at: %s", p);
463                         return FALSE;
464                 }
465                 
466                 pright = right;
467                 if (evaluate_next_condition) {
468                         pright = expand_string(xright, sizeof(xright), request,
469                                                rt, right);
470                         if (!pright) {
471                                 radlog(L_ERR, "Failed expanding string at: %s",
472                                        right);
473                                 return FALSE;
474                         }
475                 }
476                 
477                 DEBUG4(">>> %d:%s %d %d:%s",
478                        lt, pleft, token, rt, pright);
479                 
480                 if (evaluate_next_condition) {
481                         switch (token) {
482                         case T_OP_GT:
483                         case T_OP_LE:
484                         case T_OP_LT:
485                                 if (!all_digits(pleft)) {
486                                         radlog(L_ERR, "Left field is not a number at: %s", pleft);
487                                         return FALSE;
488                                 }
489                                 if (!all_digits(pright)) {
490                                         radlog(L_ERR, "Right field is not a number at: %s", pright);
491                                         return FALSE;
492                                 }
493                                 lint = atoi(pleft);
494                                 rint = atoi(pright);
495                                 break;
496                                 
497                         default:
498                                 break;
499                         }
500
501                         switch (token) {
502                         case T_OP_CMP_EQ:
503                                 result = (strcmp(pleft, pright) == 0);
504                                 break;
505
506                         case T_OP_NE:
507                                 result = (strcmp(pleft, pright) != 0);
508                                 break;
509
510                         case T_OP_GE:
511                                 result = (lint >= rint);
512                                 break;
513
514                         case T_OP_GT:
515                                 result = (lint > rint);
516                                 break;
517
518                         case T_OP_LE:
519                                 result = (lint <= rint);
520                                 break;
521
522                         case T_OP_LT:
523                                 result = (lint < rint);
524                                 break;
525
526 #ifdef HAVE_REGEX_H
527                         case T_OP_REG_EQ: {
528                                 int i, compare;
529                                 regex_t reg;
530                                 regmatch_t rxmatch[REQUEST_MAX_REGEX + 1];
531                                 
532                                 /*
533                                  *      Include substring matches.
534                                  */
535                                 regcomp(&reg, pright, cflags);
536                                 compare = regexec(&reg, pleft,
537                                                   REQUEST_MAX_REGEX + 1,
538                                                   rxmatch, 0);
539                                 regfree(&reg);
540                                 
541                                 /*
542                                  *      Free old %{0}, etc.
543                                  */
544                                 for (i = 0; i <= REQUEST_MAX_REGEX; i++) {
545                                         free(request_data_get(request, request,
546                                                               REQUEST_DATA_REGEX | i));
547                                 }
548
549                                 /*
550                                  *      Add new %{0}, %{1}, etc.
551                                  */
552                                 for (i = 0; i <= REQUEST_MAX_REGEX; i++) {
553                                         char *r;
554                                         char buffer[1024];
555                                         
556                                         /*
557                                          *      No %{i}, skip it.
558                                          *      We MAY have %{2} without %{1}.
559                                          */
560                                         if (rxmatch[i].rm_so == -1) continue;
561
562                                         /*
563                                          *      Copy substring into buffer.
564                                          */
565                                         memcpy(buffer, pleft + rxmatch[i].rm_so,
566                                                rxmatch[i].rm_eo - rxmatch[i].rm_so);
567                                         buffer[rxmatch[i].rm_eo - rxmatch[i].rm_so] = '\0';
568                                         
569                                         /*
570                                          *      Copy substring, and add it to
571                                          *      the request.
572                                          *
573                                          *      Note that we don't check
574                                          *      for out of memory, which is
575                                          *      the only error we can get...
576                                          */
577                                         r = strdup(buffer);
578                                         request_data_add(request, request,
579                                                          REQUEST_DATA_REGEX | i,
580                                                          r, free);
581                                 }
582                                 result = (compare == 0);
583                         }
584                                 break;
585
586                         case T_OP_REG_NE: {
587                                 int compare;
588                                 regex_t reg;
589                                 regmatch_t rxmatch[REQUEST_MAX_REGEX + 1];
590                                 
591                                 /*
592                                  *      Include substring matches.
593                                  */
594                                 regcomp(&reg, pright, cflags);
595                                 compare = regexec(&reg, pleft,
596                                                   REQUEST_MAX_REGEX + 1,
597                                                   rxmatch, 0);
598                                 regfree(&reg);
599                                 
600                                 result = (compare != 0);
601                         }
602                                 break;
603 #endif
604
605                         default:
606                                 DEBUG4(">>> NOT IMPLEMENTED %d", token);
607                                 break;
608                         }
609
610                         DEBUG2("%.*s Evaluating %s(%.*s) -> %s",
611                                depth, filler,
612                                invert ? "!" : "", p - start, start,
613                                (result != FALSE) ? "TRUE" : "FALSE");
614
615                         DEBUG4(">>> GOT result %d", result);
616
617                         /*
618                          *      Not evaluating it.  We may be just
619                          *      parsing it.
620                          */
621                 } else if (request) {
622                         DEBUG2("%.*s Skipping %s(\"%s\" %s \"%s\")",
623                                depth, filler,
624                                invert ? "!" : "", pleft, comp, pright);
625                 }
626
627                 end_of_condition:
628                 if (invert) {
629                         DEBUG4(">>> INVERTING result");
630                         result = (result == FALSE);
631                         invert = FALSE;
632                 }
633
634                 /*
635                  *      Don't evaluate it.
636                  */
637                 DEBUG4(">>> EVALUATE %d ::%s::",
638                         evaluate_next_condition, p);
639
640                 while ((*p == ' ') || (*p == '\t')) p++;
641
642                 /*
643                  *      Closing brace or EOL, return.
644                  */
645                 if (!*p || (*p == ')') ||
646                     ((*p == '!') && (p[1] != '=') && (p[1] != '~')) ||
647                     ((p[0] == '&') && (p[1] == '&')) ||
648                     ((p[0] == '|') && (p[1] == '|'))) {
649                         DEBUG4(">>> AT EOL2a");
650                         *ptr = p;
651                         if (evaluate_next_condition) *presult = result;
652                         return TRUE;
653                 }
654         } /* loop over the input condition */
655
656         DEBUG4(">>> AT EOL2b");
657         *ptr = p;
658         if (evaluate_next_condition) *presult = result;
659         return TRUE;
660 }
661
662 /*
663  *      The pairmove() function in src/lib/valuepair.c does all sorts of
664  *      extra magic that we don't want here.
665  *
666  *      FIXME: integrate this with the code calling it, so that we
667  *      only paircopy() those attributes that we're really going to
668  *      use.
669  */
670 static void my_pairmove(REQUEST *request, VALUE_PAIR **to, VALUE_PAIR *from)
671 {
672         int i, j, count, from_count, to_count, tailto;
673         VALUE_PAIR *vp, *next, **last;
674         VALUE_PAIR **from_list, **to_list;
675         int *edited;
676
677         /*
678          *      Set up arrays for editing, to remove some of the
679          *      O(N^2) dependencies.  This also makes it easier to
680          *      insert and remove attributes.
681          *
682          *      It also means that the operators apply ONLY to the
683          *      attributes in the original list.  With the previous
684          *      implementation of pairmove(), adding two attributes
685          *      via "+=" and then "=" would mean that the second one
686          *      wasn't added, because of the existence of the first
687          *      one in the "to" list.  This implementation doesn't
688          *      have that bug.
689          *
690          *      Also, the previous implementation did NOT implement
691          *      "-=" correctly.  If two of the same attributes existed
692          *      in the "to" list, and you tried to subtract something
693          *      matching the *second* value, then the pairdelete()
694          *      function was called, and the *all* attributes of that
695          *      number were deleted.  With this implementation, only
696          *      the matching attributes are deleted.
697          */
698         count = 0;
699         for (vp = from; vp != NULL; vp = vp->next) count++;
700         from_list = rad_malloc(sizeof(*from_list) * count);
701
702         for (vp = *to; vp != NULL; vp = vp->next) count++;
703         to_list = rad_malloc(sizeof(*to_list) * count);
704
705         /*
706          *      Move the lists to the arrays, and break the list
707          *      chains.
708          */
709         from_count = 0;
710         for (vp = from; vp != NULL; vp = next) {
711                 next = vp->next;
712                 from_list[from_count++] = vp;
713                 vp->next = NULL;
714         }
715
716         to_count = 0;
717         for (vp = *to; vp != NULL; vp = next) {
718                 next = vp->next;
719                 to_list[to_count++] = vp;
720                 vp->next = NULL;
721         }
722         tailto = to_count;
723         edited = rad_malloc(sizeof(*edited) * to_count);
724         memset(edited, 0, sizeof(*edited) * to_count);
725
726         DEBUG4("::: FROM %d TO %d MAX %d", from_count, to_count, count);
727
728         /*
729          *      Now that we have the lists initialized, start working
730          *      over them.
731          */
732         for (i = 0; i < from_count; i++) {
733                 int found;
734
735                 DEBUG4("::: Examining %s", from_list[i]->name);
736
737                 /*
738                  *      Attribute should be appended, OR the "to" list
739                  *      is empty, and we're supposed to replace or
740                  *      "add if not existing".
741                  */
742                 if (from_list[i]->operator == T_OP_ADD) goto append;
743
744                 found = FALSE;
745                 for (j = 0; j < to_count; j++) {
746                         if (edited[j]) continue;
747
748                         /*
749                          *      Attributes aren't the same, skip them.
750                          */
751                         if (from_list[i]->attribute != to_list[j]->attribute) {
752                                 continue;
753                         }
754
755                         /*
756                          *      We don't use a "switch" statement here
757                          *      because we want to break out of the
758                          *      "for" loop over 'j' in most cases.
759                          */
760
761                         /*
762                          *      Over-write the FIRST instance of the
763                          *      matching attribute name.  We free the
764                          *      one in the "to" list, and move over
765                          *      the one in the "from" list.
766                          */
767                         if (from_list[i]->operator == T_OP_SET) {
768                                 DEBUG4("::: OVERWRITING %s FROM %d TO %d",
769                                        to_list[j]->name, i, j);
770                                 pairfree(&to_list[j]);
771                                 to_list[j] = from_list[i];
772                                 from_list[i] = NULL;
773                                 edited[j] = TRUE;
774                                 break;
775                         }
776
777                         /*
778                          *      Add the attribute only if it does not
779                          *      exist... but it exists, so we stop
780                          *      looking.
781                          */
782                         if (from_list[i]->operator == T_OP_EQ) {
783                                 found = TRUE;
784                                 break;
785                         }
786
787                         /*
788                          *      Delete all matching attributes from
789                          *      "to"
790                          */
791                         if ((from_list[i]->operator == T_OP_SUB) ||
792                             (from_list[i]->operator == T_OP_LE) ||
793                             (from_list[i]->operator == T_OP_GE)) {
794                                 int rcode;
795                                 int old_op = from_list[i]->operator;
796
797                                 /*
798                                  *      Check for equality.
799                                  */
800                                 from_list[i]->operator = T_OP_CMP_EQ;
801
802                                 /*
803                                  *      If equal, delete the one in
804                                  *      the "to" list.
805                                  */
806                                 rcode = radius_compare_vps(NULL, from_list[i],
807                                                            to_list[j]);
808                                 /*
809                                  *      We may want to do more
810                                  *      subtractions, so we re-set the
811                                  *      operator back to it's original
812                                  *      value.
813                                  */
814                                 from_list[i]->operator = old_op;
815
816                                 switch (old_op) {
817                                 case T_OP_SUB:
818                                         if (rcode == 0) {
819                                                 DEBUG4("::: DELETING %s FROM %d TO %d",
820                                                        from_list[i]->name, i, j);
821                                                 pairfree(&to_list[j]);
822                                                 to_list[j] = NULL;
823                                         }
824                                         break;
825
826                                         /*
827                                          *      Enforce <=.  If it's
828                                          *      >, replace it.
829                                          */
830                                 case T_OP_LE:
831                                         if (rcode > 0) {
832                                                 DEBUG4("::: REPLACING %s FROM %d TO %d",
833                                                        from_list[i]->name, i, j);
834                                                 pairfree(&to_list[j]);
835                                                 to_list[j] = from_list[i];
836                                                 from_list[i] = NULL;
837                                                 edited[j] = TRUE;
838                                         }
839                                         break;
840
841                                 case T_OP_GE:
842                                         if (rcode < 0) {
843                                                 DEBUG4("::: REPLACING %s FROM %d TO %d",
844                                                        from_list[i]->name, i, j);
845                                                 pairfree(&to_list[j]);
846                                                 to_list[j] = from_list[i];
847                                                 from_list[i] = NULL;
848                                                 edited[j] = TRUE;
849                                         }
850                                         break;
851                                 }
852
853                                 continue;
854                         }
855
856                         rad_assert(0 == 1); /* panic! */
857                 }
858
859                 /*
860                  *      We were asked to add it if it didn't exist,
861                  *      and it doesn't exist.  Move it over to the
862                  *      tail of the "to" list, UNLESS it was already
863                  *      moved by another operator.
864                  */
865                 if (!found && from_list[i]) {
866                         if ((from_list[i]->operator == T_OP_EQ) ||
867                             (from_list[i]->operator == T_OP_LE) ||
868                             (from_list[i]->operator == T_OP_GE) ||
869                             (from_list[i]->operator == T_OP_SET)) {
870                         append:
871                                 DEBUG4("::: APPENDING %s FROM %d TO %d",
872                                        from_list[i]->name, i, tailto);
873                                 to_list[tailto++] = from_list[i];
874                                 from_list[i] = NULL;
875                         }
876                 }
877         }
878
879         /*
880          *      Delete attributes in the "from" list.
881          */
882         for (i = 0; i < from_count; i++) {
883                 if (!from_list[i]) continue;
884
885                 pairfree(&from_list[i]);
886         }
887         free(from_list);
888
889         DEBUG4("::: TO in %d out %d", to_count, tailto);
890
891         /*
892          *      Re-chain the "to" list.
893          */
894         *to = NULL;
895         last = to;
896         for (i = 0; i < tailto; i++) {
897                 if (!to_list[i]) continue;
898                 
899                 DEBUG4("::: to[%d] = %s", i, to_list[i]->name);
900
901                 /*
902                  *      Mash the operator to a simple '='.  The
903                  *      operators in the "to" list aren't used for
904                  *      anything.  BUT they're used in the "detail"
905                  *      file and debug output, where we don't want to
906                  *      see the operators.
907                  */
908                 to_list[i]->operator = T_OP_EQ;
909
910                 *last = to_list[i];
911                 last = &(*last)->next;
912
913                 /*
914                  *      Fix dumb cache issues
915                  */
916                 if ((i >= to_count) || edited[i]) {
917                         if (to_list[i]->attribute == PW_USER_NAME) {
918                                 request->username = to_list[i];
919                                 
920                         } else if (to_list[i]->attribute == PW_USER_PASSWORD) {
921                                 request->password = to_list[i];
922                         }
923                 }
924         }
925         free(to_list);
926 }
927
928
929 /*
930  *     Copied shamelessly from conffile.c, to simplify the API for
931  *     now...
932  */
933 typedef enum conf_type {
934         CONF_ITEM_INVALID = 0,
935         CONF_ITEM_PAIR,
936         CONF_ITEM_SECTION,
937         CONF_ITEM_DATA
938 } CONF_ITEM_TYPE;
939
940 struct conf_item {
941         struct conf_item *next;
942         struct conf_part *parent;
943         int lineno;
944         const char *filename;
945         CONF_ITEM_TYPE type;
946 };
947 struct conf_pair {
948         CONF_ITEM item;
949         char *attr;
950         char *value;
951         LRAD_TOKEN operator;
952         LRAD_TOKEN value_type;
953 };
954
955 /*
956  *      Add attributes to a list.
957  */
958 int radius_update_attrlist(REQUEST *request, CONF_SECTION *cs,
959                            VALUE_PAIR *input_vps, const char *name)
960 {
961         CONF_ITEM *ci;
962         VALUE_PAIR *newlist, *vp;
963         VALUE_PAIR **output_vps = NULL;
964
965         if (!request || !cs) return RLM_MODULE_INVALID;
966
967         if (strcmp(name, "request") == 0) {
968                 output_vps = &request->packet->vps;
969
970         } else if (strcmp(name, "reply") == 0) {
971                 output_vps = &request->reply->vps;
972
973         } else if (strcmp(name, "proxy-request") == 0) {
974                 if (request->proxy) output_vps = &request->proxy->vps;
975
976         } else if (strcmp(name, "proxy-reply") == 0) {
977                 if (request->proxy_reply) output_vps = &request->proxy_reply->vps;
978
979         } else if (strcmp(name, "config") == 0) {
980                 output_vps = &request->config_items;
981
982         } else if (strcmp(name, "control") == 0) {
983                 output_vps = &request->config_items;
984
985         } else {
986                 return RLM_MODULE_INVALID;
987         }
988
989         if (!output_vps) return RLM_MODULE_NOOP; /* didn't update the list */
990
991         newlist = paircopy(input_vps);
992         if (!newlist) {
993                 DEBUG2("Out of memory");
994                 return RLM_MODULE_FAIL;
995         }
996
997         vp = newlist;
998         for (ci=cf_item_find_next(cs, NULL);
999              ci != NULL;
1000              ci=cf_item_find_next(cs, ci)) {
1001                 CONF_PAIR *cp;
1002
1003                 /*
1004                  *      This should never happen.
1005                  */
1006                 if (cf_item_is_section(ci)) {
1007                         pairfree(&newlist);
1008                         return RLM_MODULE_INVALID;
1009                 }
1010
1011                 cp = cf_itemtopair(ci);
1012
1013                 /*
1014                  *      The VP && CF lists should be in sync.  If they're
1015                  *      not, panic.
1016                  */
1017                 if (vp->flags.do_xlat) {
1018                         const char *value;
1019                         char buffer[2048];
1020
1021                         value = expand_string(buffer, sizeof(buffer), request,
1022                                               cp->value_type, cp->value);
1023                         if (!value) {
1024                                 pairfree(&newlist);
1025                                 return RLM_MODULE_INVALID;
1026                         }
1027
1028                         if (!pairparsevalue(vp, value)) {
1029                                 DEBUG2("ERROR: Failed parsing value \"%s\" for attribute %s: %s",
1030                                        value, vp->name, librad_errstr);
1031                                 pairfree(&newlist);
1032                                 return RLM_MODULE_FAIL;
1033                         }
1034                         vp->flags.do_xlat = 0;
1035                 }
1036                 vp = vp->next;
1037         }
1038
1039         my_pairmove(request, output_vps, newlist);
1040
1041         return RLM_MODULE_UPDATED;
1042 }