5da6645d65acf95fb9b7ee5eed2455957e48ce75
[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 #ifdef WITH_UNLANG
51
52 static int all_digits(const char *string)
53 {
54         const char *p = string;
55
56         if (*p == '-') p++;
57
58         while (isdigit((int) *p)) p++;
59
60         return (*p == '\0');
61 }
62
63 static const char *filler = "????????????????????????????????????????????????????????????????";
64
65 static const char *expand_string(char *buffer, size_t sizeof_buffer,
66                                  REQUEST *request,
67                                  FR_TOKEN value_type, const char *value)
68 {
69         int result;
70         char *p;
71
72         switch (value_type) {
73         default:
74         case T_BARE_WORD:
75         case T_SINGLE_QUOTED_STRING:
76                 return value;
77
78         case T_BACK_QUOTED_STRING:
79                 result = radius_exec_program(value, request, 1,
80                                              buffer, sizeof_buffer, NULL,
81                                              NULL, 0);
82                 if (result != 0) {
83                         return NULL;
84                 }
85
86                 /*
87                  *      The result should be ASCII.
88                  */
89                 for (p = buffer; *p != '\0'; p++) {
90                         if (*p < ' ' ) {
91                                 *p = '\0';
92                                 return buffer;
93                         }
94                 }
95                 return buffer;
96
97         case T_DOUBLE_QUOTED_STRING:
98                 if (!strchr(value, '%')) return value;
99
100                 radius_xlat(buffer, sizeof_buffer, value, request, NULL);
101                 return buffer;
102         }
103
104         return NULL;
105 }
106
107 #ifdef HAVE_REGEX_H
108 static FR_TOKEN getregex(const char **ptr, char *buffer, size_t buflen,
109                          int *pcflags)
110 {
111         const char *p = *ptr;
112         char *q = buffer;
113
114         if (*p != '/') return T_OP_INVALID;
115
116         *pcflags = REG_EXTENDED;
117
118         p++;
119         while (*p) {
120                 if (buflen <= 1) break;
121
122                 if (*p == '/') {
123                         p++;
124
125                         /*
126                          *      Check for case insensitivity
127                          */
128                         if (*p == 'i') {
129                                 p++;
130                                 *pcflags |= REG_ICASE;
131                         }
132
133                         break;
134                 }
135
136                 if (*p == '\\') {
137                         int x;
138                         
139                         switch (p[1]) {
140                         case 'r':
141                                 *q++ = '\r';
142                                 break;
143                         case 'n':
144                                 *q++ = '\n';
145                                 break;
146                         case 't':
147                                 *q++ = '\t';
148                                 break;
149                         case '"':
150                                 *q++ = '"';
151                                 break;
152                         case '\'':
153                                 *q++ = '\'';
154                                 break;
155                         case '`':
156                                 *q++ = '`';
157                                 break;
158                                 
159                                 /*
160                                  *      FIXME: add 'x' and 'u'
161                                  */
162
163                         default:
164                                 if ((p[1] >= '0') && (p[1] <= '9') &&
165                                     (sscanf(p + 1, "%3o", &x) == 1)) {
166                                         *q++ = x;
167                                         p += 2;
168                                 } else {
169                                         *q++ = p[1];
170                                 }
171                                 break;
172                         }
173                         p += 2;
174                         buflen--;
175                         continue;
176                 }
177
178                 *(q++) = *(p++);
179                 buflen--;
180         }
181         *q = '\0';
182         *ptr = p;
183
184         return T_DOUBLE_QUOTED_STRING;
185 }
186 #endif
187
188 static const FR_NAME_NUMBER modreturn_table[] = {
189         { "reject",     RLM_MODULE_REJECT       },
190         { "fail",       RLM_MODULE_FAIL         },
191         { "ok",         RLM_MODULE_OK           },
192         { "handled",    RLM_MODULE_HANDLED      },
193         { "invalid",    RLM_MODULE_INVALID      },
194         { "userlock",   RLM_MODULE_USERLOCK     },
195         { "notfound",   RLM_MODULE_NOTFOUND     },
196         { "noop",       RLM_MODULE_NOOP         },
197         { "updated",    RLM_MODULE_UPDATED      },
198         { NULL, 0 }
199 };
200
201
202 int radius_get_vp(REQUEST *request, const char *name, VALUE_PAIR **vp_p)
203 {
204         const char *vp_name = name;
205         REQUEST *myrequest = request;
206         DICT_ATTR *da;
207         VALUE_PAIR *vps = NULL;
208
209         *vp_p = NULL;
210
211         /*
212          *      Allow for tunneled sessions.
213          */
214         if (strncmp(vp_name, "outer.", 6) == 0) {
215                 if (!myrequest->parent) return TRUE;
216                 vp_name += 6;
217                 myrequest = myrequest->parent;
218         }
219
220         if (strncmp(vp_name, "request:", 8) == 0) {
221                 vp_name += 8;
222                 vps = myrequest->packet->vps;
223
224         } else if (strncmp(vp_name, "reply:", 6) == 0) {
225                 vp_name += 6;
226                 vps = myrequest->reply->vps;
227
228 #ifdef WITH_PROXY
229         } else if (strncmp(vp_name, "proxy-request:", 14) == 0) {
230                 vp_name += 14;
231                 if (request->proxy) vps = myrequest->proxy->vps;
232
233         } else if (strncmp(vp_name, "proxy-reply:", 12) == 0) {
234                 vp_name += 12;
235                 if (request->proxy_reply) vps = myrequest->proxy_reply->vps;
236 #endif
237
238         } else if (strncmp(vp_name, "config:", 7) == 0) {
239                 vp_name += 7;
240                 vps = myrequest->config_items;
241
242         } else if (strncmp(vp_name, "control:", 8) == 0) {
243                 vp_name += 8;
244                 vps = myrequest->config_items;
245
246 #ifdef WITH_COA
247         } else if (strncmp(vp_name, "coa:", 4) == 0) {
248                 vp_name += 4;
249
250                 if (myrequest->coa &&
251                     (myrequest->coa->proxy->code == PW_COA_REQUEST)) {
252                         vps = myrequest->coa->proxy->vps;
253                 }
254
255         } else if (strncmp(vp_name, "coa-reply:", 10) == 0) {
256                 vp_name += 10;
257
258                 if (myrequest->coa && /* match reply with request */
259                     (myrequest->coa->proxy->code == PW_COA_REQUEST) &&
260                     (myrequest->coa->proxy_reply)) {
261                         vps = myrequest->coa->proxy_reply->vps;
262                 }
263
264         } else if (strncmp(vp_name, "disconnect:", 11) == 0) {
265                 vp_name += 11;
266
267                 if (myrequest->coa &&
268                     (myrequest->coa->proxy->code == PW_DISCONNECT_REQUEST)) {
269                         vps = myrequest->coa->proxy->vps;
270                 }
271
272         } else if (strncmp(vp_name, "disconnect-reply:", 17) == 0) {
273                 vp_name += 17;
274
275                 if (myrequest->coa && /* match reply with request */
276                     (myrequest->coa->proxy->code == PW_DISCONNECT_REQUEST) &&
277                     (myrequest->coa->proxy_reply)) {
278                         vps = myrequest->coa->proxy_reply->vps;
279                 }
280 #endif
281
282         } else {
283                 vps = myrequest->packet->vps;
284         }
285
286         da = dict_attrbyname(vp_name);
287         if (!da) return FALSE;  /* not a dictionary name */
288
289         /*
290          *      May not may not be found, but it *is* a known name.
291          */
292         *vp_p = pairfind(vps, da->attr, da->vendor);
293         return TRUE;
294 }
295
296
297 /*
298  *      *presult is "did comparison match or not"
299  */
300 static int radius_do_cmp(REQUEST *request, int *presult,
301                          FR_TOKEN lt, const char *pleft, FR_TOKEN token,
302                          FR_TOKEN rt, const char *pright,
303                          int cflags, int modreturn)
304 {
305         int result;
306         uint32_t lint, rint;
307         VALUE_PAIR *vp = NULL;
308 #ifdef HAVE_REGEX_H
309         char buffer[1024];
310 #else
311         cflags = cflags;        /* -Wunused */
312 #endif
313
314         rt = rt;                /* -Wunused */
315
316         if (lt == T_BARE_WORD) {
317                 /*
318                  *      Maybe check the last return code.
319                  */
320                 if (token == T_OP_CMP_TRUE) {
321                         int isreturn;
322
323                         /*
324                          *      Looks like a return code, treat is as such.
325                          */
326                         isreturn = fr_str2int(modreturn_table, pleft, -1);
327                         if (isreturn != -1) {
328                                 *presult = (modreturn == isreturn);
329                                 return TRUE;
330                         }
331                 }
332
333                 /*
334                  *      Bare words on the left can be attribute names.
335                  */
336                 if (radius_get_vp(request, pleft, &vp)) {
337                         VALUE_PAIR myvp;
338
339                         /*
340                          *      VP exists, and that's all we're looking for.
341                          */
342                         if (token == T_OP_CMP_TRUE) {
343                                 *presult = (vp != NULL);
344                                 return TRUE;
345                         }
346
347                         if (!vp) {
348                                 DICT_ATTR *da;
349                                 
350                                 /*
351                                  *      The attribute on the LHS may
352                                  *      have been a dynamically
353                                  *      registered callback.  i.e. it
354                                  *      doesn't exist as a VALUE_PAIR.
355                                  *      If so, try looking for it.
356                                  */
357                                 da = dict_attrbyname(pleft);
358                                 if (da && (da->vendor == 0) && radius_find_compare(da->attr)) {
359                                         VALUE_PAIR *check = pairmake(pleft, pright, token);
360                                         *presult = (radius_callback_compare(request, NULL, check, NULL, NULL) == 0);
361                                         RDEBUG3("  Callback returns %d",
362                                                 *presult);
363                                         pairfree(&check);
364                                         return TRUE;
365                                 }
366                                 
367                                 RDEBUG2("    (Attribute %s was not found)",
368                                        pleft);
369                                 return FALSE;
370                         }
371
372 #ifdef HAVE_REGEX_H
373                         /*
374                          *      Regex comparisons treat everything as
375                          *      strings.
376                          */
377                         if ((token == T_OP_REG_EQ) ||
378                             (token == T_OP_REG_NE)) {
379                                 vp_prints_value(buffer, sizeof(buffer), vp, 0);
380                                 pleft = buffer;
381                                 goto do_checks;
382                         }
383 #endif
384
385                         memcpy(&myvp, vp, sizeof(myvp));
386                         if (!pairparsevalue(&myvp, pright)) {
387                                 RDEBUG2("Failed parsing \"%s\": %s",
388                                        pright, fr_strerror());
389                                 return FALSE;
390                         }
391
392                         myvp.operator = token;
393                         *presult = paircmp(&myvp, vp);
394                         RDEBUG3("  paircmp -> %d", *presult);
395                         return TRUE;
396                 } /* else it's not a VP in a list */
397         }
398
399 #ifdef HAVE_REGEX_H
400         do_checks:
401 #endif
402         switch (token) {
403         case T_OP_GE:
404         case T_OP_GT:
405         case T_OP_LE:
406         case T_OP_LT:
407                 if (!all_digits(pright)) {
408                         RDEBUG2("    (Right field is not a number at: %s)", pright);
409                         return FALSE;
410                 }
411                 rint = strtoul(pright, NULL, 0);
412                 if (!all_digits(pleft)) {
413                         RDEBUG2("    (Left field is not a number at: %s)", pleft);
414                         return FALSE;
415                 }
416                 lint = strtoul(pleft, NULL, 0);
417                 break;
418                 
419         default:
420                 lint = rint = 0;  /* quiet the compiler */
421                 break;
422         }
423         
424         switch (token) {
425         case T_OP_CMP_TRUE:
426                 /*
427                  *      Check for truth or falsehood.
428                  */
429                 if (all_digits(pleft)) {
430                         lint = strtoul(pleft, NULL, 0);
431                         result = (lint != 0);
432                         
433                 } else {
434                         result = (*pleft != '\0');
435                 }
436                 break;
437                 
438
439         case T_OP_CMP_EQ:
440                 result = (strcmp(pleft, pright) == 0);
441                 break;
442                 
443         case T_OP_NE:
444                 result = (strcmp(pleft, pright) != 0);
445                 break;
446                 
447         case T_OP_GE:
448                 result = (lint >= rint);
449                 break;
450                 
451         case T_OP_GT:
452                 result = (lint > rint);
453                 break;
454                 
455         case T_OP_LE:
456                 result = (lint <= rint);
457                 break;
458                 
459         case T_OP_LT:
460                 result = (lint < rint);
461                 break;
462
463 #ifdef HAVE_REGEX_H
464         case T_OP_REG_EQ: {
465                 int i, compare;
466                 regex_t reg;
467                 regmatch_t rxmatch[REQUEST_MAX_REGEX + 1];
468                 
469                 /*
470                  *      Include substring matches.
471                  */
472                 compare = regcomp(&reg, pright, cflags);
473                 if (compare != 0) {
474                         if (debug_flag) {
475                                 char errbuf[128];
476
477                                 regerror(compare, &reg, errbuf, sizeof(errbuf));
478                                 DEBUG("ERROR: Failed compiling regular expression: %s", errbuf);
479                         }
480                         return FALSE;
481                 }
482
483                 compare = regexec(&reg, pleft,
484                                   REQUEST_MAX_REGEX + 1,
485                                   rxmatch, 0);
486                 regfree(&reg);
487                 
488                 /*
489                  *      Add new %{0}, %{1}, etc.
490                  */
491                 if (compare == 0) for (i = 0; i <= REQUEST_MAX_REGEX; i++) {
492                         char *r;
493
494                         free(request_data_get(request, request,
495                                               REQUEST_DATA_REGEX | i));
496
497                         /*
498                          *      No %{i}, skip it.
499                          *      We MAY have %{2} without %{1}.
500                          */
501                         if (rxmatch[i].rm_so == -1) continue;
502                         
503                         /*
504                          *      Copy substring into allocated buffer
505                          */
506                         r = rad_malloc(rxmatch[i].rm_eo -rxmatch[i].rm_so + 1);
507                         memcpy(r, pleft + rxmatch[i].rm_so,
508                                rxmatch[i].rm_eo - rxmatch[i].rm_so);
509                         r[rxmatch[i].rm_eo - rxmatch[i].rm_so] = '\0';
510
511                         request_data_add(request, request,
512                                          REQUEST_DATA_REGEX | i,
513                                          r, free);
514                 }
515                 result = (compare == 0);
516         }
517                 break;
518                 
519         case T_OP_REG_NE: {
520                 int compare;
521                 regex_t reg;
522                 regmatch_t rxmatch[REQUEST_MAX_REGEX + 1];
523                 
524                 /*
525                  *      Include substring matches.
526                  */
527                 compare = regcomp(&reg, pright, cflags);
528                 if (compare != 0) {
529                         if (debug_flag) {
530                                 char errbuf[128];
531
532                                 regerror(compare, &reg, errbuf, sizeof(errbuf));
533                                 DEBUG("ERROR: Failed compiling regular expression: %s", errbuf);
534                         }
535                         return FALSE;
536                 }
537
538                 compare = regexec(&reg, pleft,
539                                   REQUEST_MAX_REGEX + 1,
540                                   rxmatch, 0);
541                 regfree(&reg);
542                 
543                 result = (compare != 0);
544         }
545                 break;
546 #endif
547                 
548         default:
549                 RDEBUG4(">>> NOT IMPLEMENTED %d", token);
550                 result = FALSE;
551                 break;
552         }
553         
554         *presult = result;
555         return TRUE;
556 }
557
558
559 int radius_evaluate_condition(REQUEST *request, int modreturn, int depth,
560                               const char **ptr, int evaluate_it, int *presult)
561 {
562         int found_condition = FALSE;
563         int result = TRUE;
564         int invert = FALSE;
565         int evaluate_next_condition = evaluate_it;
566         const char *p;
567         const char *q, *start;
568         FR_TOKEN token, lt, rt;
569         char left[1024], right[1024], comp[4];
570         const char *pleft, *pright;
571         char  xleft[1024], xright[1024];
572         int cflags = 0;
573         
574         if (!ptr || !*ptr || (depth >= 64)) {
575                 radlog(L_ERR, "Internal sanity check failed in evaluate condition");
576                 return FALSE;
577         }
578
579         /*
580          *      Horrible parser.
581          */
582         p =  *ptr;
583         while (*p) {
584                 while ((*p == ' ') || (*p == '\t')) p++;
585
586                 /*
587                  *      ! EXPR
588                  */
589                 if (!found_condition && (*p == '!')) {
590                         /*
591                          *      Don't change the results if we're not
592                          *      evaluating the condition.
593                          */
594                         if (evaluate_next_condition) {
595                                 RDEBUG4(">>> INVERT");
596                                 invert = TRUE;
597                         }
598                         p++;
599
600                         while ((*p == ' ') || (*p == '\t')) p++;
601                 }
602
603                 /*
604                  *      ( EXPR ) 
605                  */
606                 if (!found_condition && (*p == '(')) {
607                         const char *end = p + 1;
608
609                         /*
610                          *      Evaluate the condition, bailing out on
611                          *      parse error.
612                          */
613                         RDEBUG4(">>> RECURSING WITH ... %s", end);
614                         if (!radius_evaluate_condition(request, modreturn,
615                                                        depth + 1, &end,
616                                                        evaluate_next_condition,
617                                                        &result)) {
618                                 return FALSE;
619                         }
620
621                         if (invert) {
622                                 if (evaluate_next_condition) {
623                                         RDEBUG2("%.*s Converting !%s -> %s",
624                                                 depth, filler,
625                                                 (result != FALSE) ? "TRUE" : "FALSE",
626                                                 (result == FALSE) ? "TRUE" : "FALSE");
627                                         result = (result == FALSE);
628                                 }
629                                 invert = FALSE;
630                         }
631
632                         /*
633                          *      Start from the end of the previous
634                          *      condition
635                          */
636                         p = end;
637                         RDEBUG4(">>> AFTER RECURSION ... %s", end);
638
639                         while ((*p == ' ') || (*p == '\t')) p++;
640
641                         if (!*p) {
642                                 radlog(L_ERR, "No closing brace");
643                                 return FALSE;
644                         }
645
646                         if (*p == ')') p++; /* eat closing brace */
647                         found_condition = TRUE;
648
649                         while ((*p == ' ') || (*p == '\t')) p++;
650                 }
651
652                 /*
653                  *      At EOL or closing brace, update && return.
654                  */
655                 if (found_condition && (!*p || (*p == ')'))) break;
656
657                 /*
658                  *      Now it's either:
659                  *
660                  *      WORD
661                  *      WORD1 op WORD2
662                  *      && EXPR
663                  *      || EXPR
664                  */
665                 if (found_condition) {
666                         /*
667                          *      (A && B) means "evaluate B
668                          *      only if A was true"
669                          */
670                         if ((p[0] == '&') && (p[1] == '&')) {
671                                 if (!result) evaluate_next_condition = FALSE;
672                                 p += 2;
673                                 found_condition = FALSE;
674                                 continue; /* go back to the start */
675                         }
676
677                         /*
678                          *      (A || B) means "evaluate B
679                          *      only if A was false"
680                          */
681                         if ((p[0] == '|') && (p[1] == '|')) {
682                                 if (result) evaluate_next_condition = FALSE;
683                                 p += 2;
684                                 found_condition = FALSE;
685                                 continue;
686                         }
687
688                         /*
689                          *      It must be:
690                          *
691                          *      WORD
692                          *      WORD1 op WORD2
693                          */
694                 }
695
696                 if (found_condition) {
697                         radlog(L_ERR, "Consecutive conditions at %s", p);
698                         return FALSE;
699                 }
700
701                 RDEBUG4(">>> LOOKING AT %s", p);
702                 start = p;
703
704                 /*
705                  *      Look for common errors.
706                  */
707                 if ((p[0] == '%') && (p[1] == '{')) {
708                         radlog(L_ERR, "Bare %%{...} is invalid in condition at: %s", p);
709                         return FALSE;
710                 }
711
712                 /*
713                  *      Look for WORD1 op WORD2
714                  */
715                 lt = gettoken(&p, left, sizeof(left));
716                 if ((lt != T_BARE_WORD) &&
717                     (lt != T_DOUBLE_QUOTED_STRING) &&
718                     (lt != T_SINGLE_QUOTED_STRING) &&
719                     (lt != T_BACK_QUOTED_STRING)) {
720                         radlog(L_ERR, "Expected string or numbers at: %s", p);
721                         return FALSE;
722                 }
723
724                 pleft = left;
725                 if (evaluate_next_condition) {
726                         pleft = expand_string(xleft, sizeof(xleft), request,
727                                               lt, left);
728                         if (!pleft) {
729                                 radlog(L_ERR, "Failed expanding string at: %s",
730                                        left);
731                                 return FALSE;
732                         }
733                 }
734
735                 /*
736                  *      Peek ahead, to see if it's:
737                  *
738                  *      WORD
739                  *
740                  *      or something else, such as
741                  *
742                  *      WORD1 op WORD2
743                  *      WORD )
744                  *      WORD && EXPR
745                  *      WORD || EXPR
746                  */
747                 q = p;
748                 while ((*q == ' ') || (*q == '\t')) q++;
749
750                 /*
751                  *      If the next thing is:
752                  *
753                  *      EOL
754                  *      end of condition
755                  *      &&
756                  *      ||
757                  *
758                  *      Then WORD is just a test for existence.
759                  *      Remember that and skip ahead.
760                  */
761                 if (!*q || (*q == ')') ||
762                     ((q[0] == '&') && (q[1] == '&')) ||
763                     ((q[0] == '|') && (q[1] == '|'))) {
764                         token = T_OP_CMP_TRUE;
765                         rt = T_OP_INVALID;
766                         pright = NULL;
767                         goto do_cmp;
768                 }
769
770                 /*
771                  *      Otherwise, it's:
772                  *
773                  *      WORD1 op WORD2
774                  */
775                 token = gettoken(&p, comp, sizeof(comp));
776                 if ((token < T_OP_NE) || (token > T_OP_CMP_EQ) ||
777                     (token == T_OP_CMP_TRUE)) {
778                         radlog(L_ERR, "Expected comparison at: %s", comp);
779                         return FALSE;
780                 }
781                 
782                 /*
783                  *      Look for common errors.
784                  */
785                 if ((p[0] == '%') && (p[1] == '{')) {
786                         radlog(L_ERR, "Bare %%{...} is invalid in condition at: %s", p);
787                         return FALSE;
788                 }
789                 
790                 /*
791                  *      Validate strings.
792                  */
793 #ifdef HAVE_REGEX_H
794                 if ((token == T_OP_REG_EQ) ||
795                     (token == T_OP_REG_NE)) {
796                         rt = getregex(&p, right, sizeof(right), &cflags);
797                         if (rt != T_DOUBLE_QUOTED_STRING) {
798                                 radlog(L_ERR, "Expected regular expression at: %s", p);
799                                 return FALSE;
800                         }
801                 } else
802 #endif
803                         rt = gettoken(&p, right, sizeof(right));
804
805                 if ((rt != T_BARE_WORD) &&
806                     (rt != T_DOUBLE_QUOTED_STRING) &&
807                     (rt != T_SINGLE_QUOTED_STRING) &&
808                     (rt != T_BACK_QUOTED_STRING)) {
809                         radlog(L_ERR, "Expected string or numbers at: %s", p);
810                         return FALSE;
811                 }
812                 
813                 pright = right;
814                 if (evaluate_next_condition) {
815                         pright = expand_string(xright, sizeof(xright), request,
816                                                rt, right);
817                         if (!pright) {
818                                 radlog(L_ERR, "Failed expanding string at: %s",
819                                        right);
820                                 return FALSE;
821                         }
822                 }
823                 
824                 RDEBUG4(">>> %d:%s %d %d:%s",
825                        lt, pleft, token, rt, pright);
826                 
827         do_cmp:
828                 if (evaluate_next_condition) {
829                         /*
830                          *      More parse errors.
831                          */
832                         if (!radius_do_cmp(request, &result, lt, pleft, token,
833                                            rt, pright, cflags, modreturn)) {
834                                 return FALSE;
835                         }
836                         RDEBUG4(">>> Comparison returned %d", result);
837
838                         if (invert) {
839                                 RDEBUG4(">>> INVERTING result");
840                                 result = (result == FALSE);
841                         }
842
843                         RDEBUG2("%.*s Evaluating %s(%.*s) -> %s",
844                                depth, filler,
845                                invert ? "!" : "", p - start, start,
846                                (result != FALSE) ? "TRUE" : "FALSE");
847
848                         invert = FALSE;
849                         RDEBUG4(">>> GOT result %d", result);
850
851                         /*
852                          *      Not evaluating it.  We may be just
853                          *      parsing it.
854                          */
855                 } else if (request) {
856                         RDEBUG2("%.*s Skipping %s(%.*s)",
857                                depth, filler,
858                                invert ? "!" : "", p - start, start);
859                 }
860
861                 found_condition = TRUE;
862         } /* loop over the input condition */
863
864         if (!found_condition) {
865                 radlog(L_ERR, "Syntax error.  Expected condition at %s", p);
866                 return FALSE;
867         }
868
869         RDEBUG4(">>> AT EOL -> %d", result);
870         *ptr = p;
871         if (evaluate_it) *presult = result;
872         return TRUE;
873 }
874 #endif
875
876 static void fix_up(REQUEST *request)
877 {
878         VALUE_PAIR *vp;
879
880         request->username = NULL;
881         request->password = NULL;
882         
883         for (vp = request->packet->vps; vp != NULL; vp = vp->next) {
884                 if ((vp->attribute == PW_USER_NAME) &&
885                     !request->username) {
886                         request->username = vp;
887                         
888                 } else if (vp->attribute == PW_STRIPPED_USER_NAME) {
889                         request->username = vp;
890                         
891                 } else if (vp->attribute == PW_USER_PASSWORD) {
892                         request->password = vp;
893                 }
894         }
895 }
896
897
898 /*
899  *      The pairmove() function in src/lib/valuepair.c does all sorts of
900  *      extra magic that we don't want here.
901  *
902  *      FIXME: integrate this with the code calling it, so that we
903  *      only paircopy() those attributes that we're really going to
904  *      use.
905  */
906 void radius_pairmove(REQUEST *request, VALUE_PAIR **to, VALUE_PAIR *from)
907 {
908         int i, j, count, from_count, to_count, tailto;
909         VALUE_PAIR *vp, *next, **last;
910         VALUE_PAIR **from_list, **to_list;
911         int *edited = NULL;
912
913         /*
914          *      Set up arrays for editing, to remove some of the
915          *      O(N^2) dependencies.  This also makes it easier to
916          *      insert and remove attributes.
917          *
918          *      It also means that the operators apply ONLY to the
919          *      attributes in the original list.  With the previous
920          *      implementation of pairmove(), adding two attributes
921          *      via "+=" and then "=" would mean that the second one
922          *      wasn't added, because of the existence of the first
923          *      one in the "to" list.  This implementation doesn't
924          *      have that bug.
925          *
926          *      Also, the previous implementation did NOT implement
927          *      "-=" correctly.  If two of the same attributes existed
928          *      in the "to" list, and you tried to subtract something
929          *      matching the *second* value, then the pairdelete()
930          *      function was called, and the *all* attributes of that
931          *      number were deleted.  With this implementation, only
932          *      the matching attributes are deleted.
933          */
934         count = 0;
935         for (vp = from; vp != NULL; vp = vp->next) count++;
936         from_list = rad_malloc(sizeof(*from_list) * count);
937
938         for (vp = *to; vp != NULL; vp = vp->next) count++;
939         to_list = rad_malloc(sizeof(*to_list) * count);
940
941         /*
942          *      Move the lists to the arrays, and break the list
943          *      chains.
944          */
945         from_count = 0;
946         for (vp = from; vp != NULL; vp = next) {
947                 next = vp->next;
948                 from_list[from_count++] = vp;
949                 vp->next = NULL;
950         }
951
952         to_count = 0;
953         for (vp = *to; vp != NULL; vp = next) {
954                 next = vp->next;
955                 to_list[to_count++] = vp;
956                 vp->next = NULL;
957         }
958         tailto = to_count;
959         edited = rad_malloc(sizeof(*edited) * to_count);
960         memset(edited, 0, sizeof(*edited) * to_count);
961
962         RDEBUG4("::: FROM %d TO %d MAX %d", from_count, to_count, count);
963
964         /*
965          *      Now that we have the lists initialized, start working
966          *      over them.
967          */
968         for (i = 0; i < from_count; i++) {
969                 int found;
970
971                 RDEBUG4("::: Examining %s", from_list[i]->name);
972
973                 /*
974                  *      Attribute should be appended, OR the "to" list
975                  *      is empty, and we're supposed to replace or
976                  *      "add if not existing".
977                  */
978                 if (from_list[i]->operator == T_OP_ADD) goto append;
979
980                 found = FALSE;
981                 for (j = 0; j < to_count; j++) {
982                         if (edited[j] || !to_list[j] || !from_list[i]) continue;
983
984                         /*
985                          *      Attributes aren't the same, skip them.
986                          */
987                         if (from_list[i]->attribute != to_list[j]->attribute) {
988                                 continue;
989                         }
990
991                         /*
992                          *      We don't use a "switch" statement here
993                          *      because we want to break out of the
994                          *      "for" loop over 'j' in most cases.
995                          */
996
997                         /*
998                          *      Over-write the FIRST instance of the
999                          *      matching attribute name.  We free the
1000                          *      one in the "to" list, and move over
1001                          *      the one in the "from" list.
1002                          */
1003                         if (from_list[i]->operator == T_OP_SET) {
1004                                 RDEBUG4("::: OVERWRITING %s FROM %d TO %d",
1005                                        to_list[j]->name, i, j);
1006                                 pairfree(&to_list[j]);
1007                                 to_list[j] = from_list[i];
1008                                 from_list[i] = NULL;
1009                                 edited[j] = TRUE;
1010                                 break;
1011                         }
1012
1013                         /*
1014                          *      Add the attribute only if it does not
1015                          *      exist... but it exists, so we stop
1016                          *      looking.
1017                          */
1018                         if (from_list[i]->operator == T_OP_EQ) {
1019                                 found = TRUE;
1020                                 break;
1021                         }
1022
1023                         /*
1024                          *      Delete every attribute, independent
1025                          *      of its value.
1026                          */
1027                         if (from_list[i]->operator == T_OP_CMP_FALSE) {
1028                                 goto delete;
1029                         }
1030
1031                         /*
1032                          *      Delete all matching attributes from
1033                          *      "to"
1034                          */
1035                         if ((from_list[i]->operator == T_OP_SUB) ||
1036                             (from_list[i]->operator == T_OP_CMP_EQ) ||
1037                             (from_list[i]->operator == T_OP_LE) ||
1038                             (from_list[i]->operator == T_OP_GE)) {
1039                                 int rcode;
1040                                 int old_op = from_list[i]->operator;
1041
1042                                 /*
1043                                  *      Check for equality.
1044                                  */
1045                                 from_list[i]->operator = T_OP_CMP_EQ;
1046
1047                                 /*
1048                                  *      If equal, delete the one in
1049                                  *      the "to" list.
1050                                  */
1051                                 rcode = radius_compare_vps(NULL, from_list[i],
1052                                                            to_list[j]);
1053                                 /*
1054                                  *      We may want to do more
1055                                  *      subtractions, so we re-set the
1056                                  *      operator back to it's original
1057                                  *      value.
1058                                  */
1059                                 from_list[i]->operator = old_op;
1060
1061                                 switch (old_op) {
1062                                 case T_OP_CMP_EQ:
1063                                         if (rcode != 0) goto delete;
1064                                         break;
1065
1066                                 case T_OP_SUB:
1067                                         if (rcode == 0) {
1068                                         delete:
1069                                                 RDEBUG4("::: DELETING %s FROM %d TO %d",
1070                                                        from_list[i]->name, i, j);
1071                                                 pairfree(&to_list[j]);
1072                                                 to_list[j] = NULL;
1073                                         }
1074                                         break;
1075
1076                                         /*
1077                                          *      Enforce <=.  If it's
1078                                          *      >, replace it.
1079                                          */
1080                                 case T_OP_LE:
1081                                         if (rcode > 0) {
1082                                                 RDEBUG4("::: REPLACING %s FROM %d TO %d",
1083                                                        from_list[i]->name, i, j);
1084                                                 pairfree(&to_list[j]);
1085                                                 to_list[j] = from_list[i];
1086                                                 from_list[i] = NULL;
1087                                                 edited[j] = TRUE;
1088                                         }
1089                                         break;
1090
1091                                 case T_OP_GE:
1092                                         if (rcode < 0) {
1093                                                 RDEBUG4("::: REPLACING %s FROM %d TO %d",
1094                                                        from_list[i]->name, i, j);
1095                                                 pairfree(&to_list[j]);
1096                                                 to_list[j] = from_list[i];
1097                                                 from_list[i] = NULL;
1098                                                 edited[j] = TRUE;
1099                                         }
1100                                         break;
1101                                 }
1102
1103                                 continue;
1104                         }
1105
1106                         rad_assert(0 == 1); /* panic! */
1107                 }
1108
1109                 /*
1110                  *      We were asked to add it if it didn't exist,
1111                  *      and it doesn't exist.  Move it over to the
1112                  *      tail of the "to" list, UNLESS it was already
1113                  *      moved by another operator.
1114                  */
1115                 if (!found && from_list[i]) {
1116                         if ((from_list[i]->operator == T_OP_EQ) ||
1117                             (from_list[i]->operator == T_OP_LE) ||
1118                             (from_list[i]->operator == T_OP_GE) ||
1119                             (from_list[i]->operator == T_OP_SET)) {
1120                         append:
1121                                 RDEBUG4("::: APPENDING %s FROM %d TO %d",
1122                                        from_list[i]->name, i, tailto);
1123                                 to_list[tailto++] = from_list[i];
1124                                 from_list[i] = NULL;
1125                         }
1126                 }
1127         }
1128
1129         /*
1130          *      Delete attributes in the "from" list.
1131          */
1132         for (i = 0; i < from_count; i++) {
1133                 if (!from_list[i]) continue;
1134
1135                 pairfree(&from_list[i]);
1136         }
1137         free(from_list);
1138
1139         RDEBUG4("::: TO in %d out %d", to_count, tailto);
1140
1141         /*
1142          *      Re-chain the "to" list.
1143          */
1144         *to = NULL;
1145         last = to;
1146
1147         for (i = 0; i < tailto; i++) {
1148                 if (!to_list[i]) continue;
1149                 
1150                 RDEBUG4("::: to[%d] = %s", i, to_list[i]->name);
1151
1152                 /*
1153                  *      Mash the operator to a simple '='.  The
1154                  *      operators in the "to" list aren't used for
1155                  *      anything.  BUT they're used in the "detail"
1156                  *      file and debug output, where we don't want to
1157                  *      see the operators.
1158                  */
1159                 to_list[i]->operator = T_OP_EQ;
1160
1161                 *last = to_list[i];
1162                 last = &(*last)->next;
1163         }
1164
1165         rad_assert(request != NULL);
1166         rad_assert(request->packet != NULL);
1167
1168         /*
1169          *      Fix dumb cache issues
1170          */
1171         if (to == &request->packet->vps) {
1172                 fix_up(request);
1173         } else if (request->parent && (to == &request->parent->packet->vps)) {
1174                 fix_up(request->parent);
1175         }
1176
1177         free(to_list);
1178         free(edited);
1179 }
1180
1181
1182 #ifdef WITH_UNLANG
1183 /*
1184  *     Copied shamelessly from conffile.c, to simplify the API for
1185  *     now...
1186  */
1187 typedef enum conf_type {
1188         CONF_ITEM_INVALID = 0,
1189         CONF_ITEM_PAIR,
1190         CONF_ITEM_SECTION,
1191         CONF_ITEM_DATA
1192 } CONF_ITEM_TYPE;
1193
1194 struct conf_item {
1195         struct conf_item *next;
1196         struct conf_part *parent;
1197         int lineno;
1198         const char *filename;
1199         CONF_ITEM_TYPE type;
1200 };
1201 struct conf_pair {
1202         CONF_ITEM item;
1203         char *attr;
1204         char *value;
1205         FR_TOKEN operator;
1206         FR_TOKEN value_type;
1207 };
1208
1209 /*
1210  *      Add attributes to a list.
1211  */
1212 int radius_update_attrlist(REQUEST *request, CONF_SECTION *cs,
1213                            VALUE_PAIR *input_vps, const char *name)
1214 {
1215         CONF_ITEM *ci;
1216         VALUE_PAIR *newlist, *vp;
1217         VALUE_PAIR **output_vps = NULL;
1218         REQUEST *myrequest = request;
1219
1220         if (!request || !cs) return RLM_MODULE_INVALID;
1221
1222         /*
1223          *      If we are an inner tunnel session, permit the
1224          *      policy to update the outer lists directly.
1225          */
1226         if (strncmp(name, "outer.", 6) == 0) {
1227                 if (!request->parent) return RLM_MODULE_NOOP;
1228
1229                 myrequest = request->parent;
1230                 name += 6;
1231         }
1232
1233         if (strcmp(name, "request") == 0) {
1234                 output_vps = &myrequest->packet->vps;
1235
1236         } else if (strcmp(name, "reply") == 0) {
1237                 output_vps = &myrequest->reply->vps;
1238
1239 #ifdef WITH_PROXY
1240         } else if (strcmp(name, "proxy-request") == 0) {
1241                 if (request->proxy) output_vps = &myrequest->proxy->vps;
1242
1243         } else if (strcmp(name, "proxy-reply") == 0) {
1244                 if (request->proxy_reply) output_vps = &request->proxy_reply->vps;
1245 #endif
1246
1247         } else if (strcmp(name, "config") == 0) {
1248                 output_vps = &myrequest->config_items;
1249
1250         } else if (strcmp(name, "control") == 0) {
1251                 output_vps = &myrequest->config_items;
1252
1253 #ifdef WITH_COA
1254         } else if (strcmp(name, "coa") == 0) {
1255                 if (!myrequest->coa) {
1256                         request_alloc_coa(myrequest);
1257                         myrequest->coa->proxy->code = PW_COA_REQUEST;
1258                 }
1259                   
1260                 if (myrequest->coa &&
1261                     (myrequest->coa->proxy->code == PW_COA_REQUEST)) {
1262                         output_vps = &myrequest->coa->proxy->vps;
1263                 }
1264
1265         } else if (strcmp(name, "coa-reply") == 0) {
1266                 if (myrequest->coa && /* match reply with request */
1267                     (myrequest->coa->proxy->code == PW_COA_REQUEST) &&
1268                      (myrequest->coa->proxy_reply)) {
1269                       output_vps = &myrequest->coa->proxy_reply->vps;
1270                 }
1271
1272         } else if (strcmp(name, "disconnect") == 0) {
1273                 if (!myrequest->coa) {
1274                         request_alloc_coa(myrequest);
1275                         if (myrequest->coa) myrequest->coa->proxy->code = PW_DISCONNECT_REQUEST;
1276                 }
1277
1278                 if (myrequest->coa &&
1279                     (myrequest->coa->proxy->code == PW_DISCONNECT_REQUEST)) {
1280                         output_vps = &myrequest->coa->proxy->vps;
1281                 }
1282
1283         } else if (strcmp(name, "disconnect-reply") == 0) {
1284                 if (myrequest->coa && /* match reply with request */
1285                     (myrequest->coa->proxy->code == PW_DISCONNECT_REQUEST) &&
1286                     (myrequest->coa->proxy_reply)) {
1287                         output_vps = &myrequest->coa->proxy_reply->vps;
1288                 }
1289 #endif
1290
1291         } else {
1292                 return RLM_MODULE_INVALID;
1293         }
1294
1295         if (!output_vps) return RLM_MODULE_NOOP; /* didn't update the list */
1296
1297         newlist = paircopy(input_vps);
1298         if (!newlist) {
1299                 RDEBUG2("Out of memory");
1300                 return RLM_MODULE_FAIL;
1301         }
1302
1303         vp = newlist;
1304         for (ci=cf_item_find_next(cs, NULL);
1305              ci != NULL;
1306              ci=cf_item_find_next(cs, ci)) {
1307                 CONF_PAIR *cp;
1308
1309                 /*
1310                  *      This should never happen.
1311                  */
1312                 if (cf_item_is_section(ci)) {
1313                         pairfree(&newlist);
1314                         return RLM_MODULE_INVALID;
1315                 }
1316
1317                 cp = cf_itemtopair(ci);
1318
1319 #ifndef NDEBUG
1320                 if (debug_flag && radius_find_compare(vp->attribute)) {
1321                         DEBUG("WARNING: You are modifying the value of virtual attribute %s.  This is not supported.", vp->name);
1322                 }
1323 #endif
1324
1325                 /*
1326                  *      The VP && CF lists should be in sync.  If they're
1327                  *      not, panic.
1328                  */
1329                 if (vp->flags.do_xlat) {
1330                         const char *value;
1331                         char buffer[2048];
1332
1333                         value = expand_string(buffer, sizeof(buffer), request,
1334                                               cp->value_type, cp->value);
1335                         if (!value) {
1336                                 pairfree(&newlist);
1337                                 return RLM_MODULE_INVALID;
1338                         }
1339
1340                         if (!pairparsevalue(vp, value)) {
1341                                 RDEBUG2("ERROR: Failed parsing value \"%s\" for attribute %s: %s",
1342                                        value, vp->name, fr_strerror());
1343                                 pairfree(&newlist);
1344                                 return RLM_MODULE_FAIL;
1345                         }
1346                         vp->flags.do_xlat = 0;
1347                 }
1348                 vp = vp->next;
1349         }
1350
1351         radius_pairmove(request, output_vps, newlist);
1352
1353         return RLM_MODULE_UPDATED;
1354 }
1355 #endif