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