Fix typos in previous commit
[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                                 *presult = 0;
370                                 return TRUE;
371                         }
372
373 #ifdef HAVE_REGEX_H
374                         /*
375                          *      Regex comparisons treat everything as
376                          *      strings.
377                          */
378                         if ((token == T_OP_REG_EQ) ||
379                             (token == T_OP_REG_NE)) {
380                                 vp_prints_value(buffer, sizeof(buffer), vp, 0);
381                                 pleft = buffer;
382                                 goto do_checks;
383                         }
384 #endif
385
386                         memcpy(&myvp, vp, sizeof(myvp));
387                         if (!pairparsevalue(&myvp, pright)) {
388                                 RDEBUG2("Failed parsing \"%s\": %s",
389                                        pright, fr_strerror());
390                                 return FALSE;
391                         }
392
393                         myvp.operator = token;
394                         *presult = paircmp(&myvp, vp);
395                         RDEBUG3("  paircmp -> %d", *presult);
396                         return TRUE;
397                 } /* else it's not a VP in a list */
398         }
399
400 #ifdef HAVE_REGEX_H
401         do_checks:
402 #endif
403         switch (token) {
404         case T_OP_GE:
405         case T_OP_GT:
406         case T_OP_LE:
407         case T_OP_LT:
408                 if (!all_digits(pright)) {
409                         RDEBUG2("    (Right field is not a number at: %s)", pright);
410                         return FALSE;
411                 }
412                 rint = strtoul(pright, NULL, 0);
413                 if (!all_digits(pleft)) {
414                         RDEBUG2("    (Left field is not a number at: %s)", pleft);
415                         return FALSE;
416                 }
417                 lint = strtoul(pleft, NULL, 0);
418                 break;
419                 
420         default:
421                 lint = rint = 0;  /* quiet the compiler */
422                 break;
423         }
424         
425         switch (token) {
426         case T_OP_CMP_TRUE:
427                 /*
428                  *      Check for truth or falsehood.
429                  */
430                 if (all_digits(pleft)) {
431                         lint = strtoul(pleft, NULL, 0);
432                         result = (lint != 0);
433                         
434                 } else {
435                         result = (*pleft != '\0');
436                 }
437                 break;
438                 
439
440         case T_OP_CMP_EQ:
441                 result = (strcmp(pleft, pright) == 0);
442                 break;
443                 
444         case T_OP_NE:
445                 result = (strcmp(pleft, pright) != 0);
446                 break;
447                 
448         case T_OP_GE:
449                 result = (lint >= rint);
450                 break;
451                 
452         case T_OP_GT:
453                 result = (lint > rint);
454                 break;
455                 
456         case T_OP_LE:
457                 result = (lint <= rint);
458                 break;
459                 
460         case T_OP_LT:
461                 result = (lint < rint);
462                 break;
463
464 #ifdef HAVE_REGEX_H
465         case T_OP_REG_EQ: {
466                 int i, compare;
467                 regex_t reg;
468                 regmatch_t rxmatch[REQUEST_MAX_REGEX + 1];
469                 
470                 /*
471                  *      Include substring matches.
472                  */
473                 compare = regcomp(&reg, pright, cflags);
474                 if (compare != 0) {
475                         if (debug_flag) {
476                                 char errbuf[128];
477
478                                 regerror(compare, &reg, errbuf, sizeof(errbuf));
479                                 DEBUG("ERROR: Failed compiling regular expression: %s", errbuf);
480                         }
481                         return FALSE;
482                 }
483
484                 compare = regexec(&reg, pleft,
485                                   REQUEST_MAX_REGEX + 1,
486                                   rxmatch, 0);
487                 regfree(&reg);
488                 
489                 /*
490                  *      Add new %{0}, %{1}, etc.
491                  */
492                 if (compare == 0) for (i = 0; i <= REQUEST_MAX_REGEX; i++) {
493                         char *r;
494
495                         free(request_data_get(request, request,
496                                               REQUEST_DATA_REGEX | i));
497
498                         /*
499                          *      No %{i}, skip it.
500                          *      We MAY have %{2} without %{1}.
501                          */
502                         if (rxmatch[i].rm_so == -1) continue;
503                         
504                         /*
505                          *      Copy substring into allocated buffer
506                          */
507                         r = rad_malloc(rxmatch[i].rm_eo -rxmatch[i].rm_so + 1);
508                         memcpy(r, pleft + rxmatch[i].rm_so,
509                                rxmatch[i].rm_eo - rxmatch[i].rm_so);
510                         r[rxmatch[i].rm_eo - rxmatch[i].rm_so] = '\0';
511
512                         request_data_add(request, request,
513                                          REQUEST_DATA_REGEX | i,
514                                          r, free);
515                 }
516                 result = (compare == 0);
517         }
518                 break;
519                 
520         case T_OP_REG_NE: {
521                 int compare;
522                 regex_t reg;
523                 regmatch_t rxmatch[REQUEST_MAX_REGEX + 1];
524                 
525                 /*
526                  *      Include substring matches.
527                  */
528                 compare = regcomp(&reg, pright, cflags);
529                 if (compare != 0) {
530                         if (debug_flag) {
531                                 char errbuf[128];
532
533                                 regerror(compare, &reg, errbuf, sizeof(errbuf));
534                                 DEBUG("ERROR: Failed compiling regular expression: %s", errbuf);
535                         }
536                         return FALSE;
537                 }
538
539                 compare = regexec(&reg, pleft,
540                                   REQUEST_MAX_REGEX + 1,
541                                   rxmatch, 0);
542                 regfree(&reg);
543                 
544                 result = (compare != 0);
545         }
546                 break;
547 #endif
548                 
549         default:
550                 RDEBUG4(">>> NOT IMPLEMENTED %d", token);
551                 result = FALSE;
552                 break;
553         }
554         
555         *presult = result;
556         return TRUE;
557 }
558
559
560 int radius_evaluate_condition(REQUEST *request, int modreturn, int depth,
561                               const char **ptr, int evaluate_it, int *presult)
562 {
563         int found_condition = FALSE;
564         int result = TRUE;
565         int invert = FALSE;
566         int evaluate_next_condition = evaluate_it;
567         const char *p;
568         const char *q, *start;
569         FR_TOKEN token, lt, rt;
570         char left[1024], right[1024], comp[4];
571         const char *pleft, *pright;
572         char  xleft[1024], xright[1024];
573         int cflags = 0;
574         
575         if (!ptr || !*ptr || (depth >= 64)) {
576                 radlog(L_ERR, "Internal sanity check failed in evaluate condition");
577                 return FALSE;
578         }
579
580         /*
581          *      Horrible parser.
582          */
583         p =  *ptr;
584         while (*p) {
585                 while ((*p == ' ') || (*p == '\t')) p++;
586
587                 /*
588                  *      ! EXPR
589                  */
590                 if (!found_condition && (*p == '!')) {
591                         /*
592                          *      Don't change the results if we're not
593                          *      evaluating the condition.
594                          */
595                         if (evaluate_next_condition) {
596                                 RDEBUG4(">>> INVERT");
597                                 invert = TRUE;
598                         }
599                         p++;
600
601                         while ((*p == ' ') || (*p == '\t')) p++;
602                 }
603
604                 /*
605                  *      ( EXPR ) 
606                  */
607                 if (!found_condition && (*p == '(')) {
608                         const char *end = p + 1;
609
610                         /*
611                          *      Evaluate the condition, bailing out on
612                          *      parse error.
613                          */
614                         RDEBUG4(">>> RECURSING WITH ... %s", end);
615                         if (!radius_evaluate_condition(request, modreturn,
616                                                        depth + 1, &end,
617                                                        evaluate_next_condition,
618                                                        &result)) {
619                                 return FALSE;
620                         }
621
622                         if (invert) {
623                                 if (evaluate_next_condition) {
624                                         RDEBUG2("%.*s Converting !%s -> %s",
625                                                 depth, filler,
626                                                 (result != FALSE) ? "TRUE" : "FALSE",
627                                                 (result == FALSE) ? "TRUE" : "FALSE");
628                                         result = (result == FALSE);
629                                 }
630                                 invert = FALSE;
631                         }
632
633                         /*
634                          *      Start from the end of the previous
635                          *      condition
636                          */
637                         p = end;
638                         RDEBUG4(">>> AFTER RECURSION ... %s", end);
639
640                         while ((*p == ' ') || (*p == '\t')) p++;
641
642                         if (!*p) {
643                                 radlog(L_ERR, "No closing brace");
644                                 return FALSE;
645                         }
646
647                         if (*p == ')') p++; /* eat closing brace */
648                         found_condition = TRUE;
649
650                         while ((*p == ' ') || (*p == '\t')) p++;
651                 }
652
653                 /*
654                  *      At EOL or closing brace, update && return.
655                  */
656                 if (found_condition && (!*p || (*p == ')'))) break;
657
658                 /*
659                  *      Now it's either:
660                  *
661                  *      WORD
662                  *      WORD1 op WORD2
663                  *      && EXPR
664                  *      || EXPR
665                  */
666                 if (found_condition) {
667                         /*
668                          *      (A && B) means "evaluate B
669                          *      only if A was true"
670                          */
671                         if ((p[0] == '&') && (p[1] == '&')) {
672                                 if (!result) evaluate_next_condition = FALSE;
673                                 p += 2;
674                                 found_condition = FALSE;
675                                 continue; /* go back to the start */
676                         }
677
678                         /*
679                          *      (A || B) means "evaluate B
680                          *      only if A was false"
681                          */
682                         if ((p[0] == '|') && (p[1] == '|')) {
683                                 if (result) evaluate_next_condition = FALSE;
684                                 p += 2;
685                                 found_condition = FALSE;
686                                 continue;
687                         }
688
689                         /*
690                          *      It must be:
691                          *
692                          *      WORD
693                          *      WORD1 op WORD2
694                          */
695                 }
696
697                 if (found_condition) {
698                         radlog(L_ERR, "Consecutive conditions at %s", p);
699                         return FALSE;
700                 }
701
702                 RDEBUG4(">>> LOOKING AT %s", p);
703                 start = p;
704
705                 /*
706                  *      Look for common errors.
707                  */
708                 if ((p[0] == '%') && (p[1] == '{')) {
709                         radlog(L_ERR, "Bare %%{...} is invalid in condition at: %s", p);
710                         return FALSE;
711                 }
712
713                 /*
714                  *      Look for WORD1 op WORD2
715                  */
716                 lt = gettoken(&p, left, sizeof(left));
717                 if ((lt != T_BARE_WORD) &&
718                     (lt != T_DOUBLE_QUOTED_STRING) &&
719                     (lt != T_SINGLE_QUOTED_STRING) &&
720                     (lt != T_BACK_QUOTED_STRING)) {
721                         radlog(L_ERR, "Expected string or numbers at: %s", p);
722                         return FALSE;
723                 }
724
725                 pleft = left;
726                 if (evaluate_next_condition) {
727                         pleft = expand_string(xleft, sizeof(xleft), request,
728                                               lt, left);
729                         if (!pleft) {
730                                 radlog(L_ERR, "Failed expanding string at: %s",
731                                        left);
732                                 return FALSE;
733                         }
734                 }
735
736                 /*
737                  *      Peek ahead, to see if it's:
738                  *
739                  *      WORD
740                  *
741                  *      or something else, such as
742                  *
743                  *      WORD1 op WORD2
744                  *      WORD )
745                  *      WORD && EXPR
746                  *      WORD || EXPR
747                  */
748                 q = p;
749                 while ((*q == ' ') || (*q == '\t')) q++;
750
751                 /*
752                  *      If the next thing is:
753                  *
754                  *      EOL
755                  *      end of condition
756                  *      &&
757                  *      ||
758                  *
759                  *      Then WORD is just a test for existence.
760                  *      Remember that and skip ahead.
761                  */
762                 if (!*q || (*q == ')') ||
763                     ((q[0] == '&') && (q[1] == '&')) ||
764                     ((q[0] == '|') && (q[1] == '|'))) {
765                         token = T_OP_CMP_TRUE;
766                         rt = T_OP_INVALID;
767                         pright = NULL;
768                         goto do_cmp;
769                 }
770
771                 /*
772                  *      Otherwise, it's:
773                  *
774                  *      WORD1 op WORD2
775                  */
776                 token = gettoken(&p, comp, sizeof(comp));
777                 if ((token < T_OP_NE) || (token > T_OP_CMP_EQ) ||
778                     (token == T_OP_CMP_TRUE)) {
779                         radlog(L_ERR, "Expected comparison at: %s", comp);
780                         return FALSE;
781                 }
782                 
783                 /*
784                  *      Look for common errors.
785                  */
786                 if ((p[0] == '%') && (p[1] == '{')) {
787                         radlog(L_ERR, "Bare %%{...} is invalid in condition at: %s", p);
788                         return FALSE;
789                 }
790                 
791                 /*
792                  *      Validate strings.
793                  */
794 #ifdef HAVE_REGEX_H
795                 if ((token == T_OP_REG_EQ) ||
796                     (token == T_OP_REG_NE)) {
797                         rt = getregex(&p, right, sizeof(right), &cflags);
798                         if (rt != T_DOUBLE_QUOTED_STRING) {
799                                 radlog(L_ERR, "Expected regular expression at: %s", p);
800                                 return FALSE;
801                         }
802                 } else
803 #endif
804                         rt = gettoken(&p, right, sizeof(right));
805
806                 if ((rt != T_BARE_WORD) &&
807                     (rt != T_DOUBLE_QUOTED_STRING) &&
808                     (rt != T_SINGLE_QUOTED_STRING) &&
809                     (rt != T_BACK_QUOTED_STRING)) {
810                         radlog(L_ERR, "Expected string or numbers at: %s", p);
811                         return FALSE;
812                 }
813                 
814                 pright = right;
815                 if (evaluate_next_condition) {
816                         pright = expand_string(xright, sizeof(xright), request,
817                                                rt, right);
818                         if (!pright) {
819                                 radlog(L_ERR, "Failed expanding string at: %s",
820                                        right);
821                                 return FALSE;
822                         }
823                 }
824                 
825                 RDEBUG4(">>> %d:%s %d %d:%s",
826                        lt, pleft, token, rt, pright);
827                 
828         do_cmp:
829                 if (evaluate_next_condition) {
830                         /*
831                          *      More parse errors.
832                          */
833                         if (!radius_do_cmp(request, &result, lt, pleft, token,
834                                            rt, pright, cflags, modreturn)) {
835                                 return FALSE;
836                         }
837                         RDEBUG4(">>> Comparison returned %d", result);
838
839                         if (invert) {
840                                 RDEBUG4(">>> INVERTING result");
841                                 result = (result == FALSE);
842                         }
843
844                         RDEBUG2("%.*s Evaluating %s(%.*s) -> %s",
845                                depth, filler,
846                                invert ? "!" : "", p - start, start,
847                                (result != FALSE) ? "TRUE" : "FALSE");
848
849                         invert = FALSE;
850                         RDEBUG4(">>> GOT result %d", result);
851
852                         /*
853                          *      Not evaluating it.  We may be just
854                          *      parsing it.
855                          */
856                 } else if (request) {
857                         RDEBUG2("%.*s Skipping %s(%.*s)",
858                                depth, filler,
859                                invert ? "!" : "", p - start, start);
860                 }
861
862                 found_condition = TRUE;
863         } /* loop over the input condition */
864
865         if (!found_condition) {
866                 radlog(L_ERR, "Syntax error.  Expected condition at %s", p);
867                 return FALSE;
868         }
869
870         RDEBUG4(">>> AT EOL -> %d", result);
871         *ptr = p;
872         if (evaluate_it) *presult = result;
873         return TRUE;
874 }
875 #endif
876
877 static void fix_up(REQUEST *request)
878 {
879         VALUE_PAIR *vp;
880
881         request->username = NULL;
882         request->password = NULL;
883         
884         for (vp = request->packet->vps; vp != NULL; vp = vp->next) {
885                 if (vp->vendor != 0) continue;
886
887                 if ((vp->attribute == PW_USER_NAME) &&
888                     !request->username) {
889                         request->username = vp;
890                         
891                 } else if (vp->attribute == PW_STRIPPED_USER_NAME) {
892                         request->username = vp;
893                         
894                 } else if (vp->attribute == PW_USER_PASSWORD) {
895                         request->password = vp;
896                 }
897
898                 if (request->username && request->password) break;
899         }
900 }
901
902
903 /*
904  *      The pairmove() function in src/lib/valuepair.c does all sorts of
905  *      extra magic that we don't want here.
906  *
907  *      FIXME: integrate this with the code calling it, so that we
908  *      only paircopy() those attributes that we're really going to
909  *      use.
910  */
911 void radius_pairmove(REQUEST *request, VALUE_PAIR **to, VALUE_PAIR *from)
912 {
913         int i, j, count, from_count, to_count, tailto;
914         VALUE_PAIR *vp, *next, **last;
915         VALUE_PAIR **from_list, **to_list;
916         int *edited = NULL;
917
918         /*
919          *      Set up arrays for editing, to remove some of the
920          *      O(N^2) dependencies.  This also makes it easier to
921          *      insert and remove attributes.
922          *
923          *      It also means that the operators apply ONLY to the
924          *      attributes in the original list.  With the previous
925          *      implementation of pairmove(), adding two attributes
926          *      via "+=" and then "=" would mean that the second one
927          *      wasn't added, because of the existence of the first
928          *      one in the "to" list.  This implementation doesn't
929          *      have that bug.
930          *
931          *      Also, the previous implementation did NOT implement
932          *      "-=" correctly.  If two of the same attributes existed
933          *      in the "to" list, and you tried to subtract something
934          *      matching the *second* value, then the pairdelete()
935          *      function was called, and the *all* attributes of that
936          *      number were deleted.  With this implementation, only
937          *      the matching attributes are deleted.
938          */
939         count = 0;
940         for (vp = from; vp != NULL; vp = vp->next) count++;
941         from_list = rad_malloc(sizeof(*from_list) * count);
942
943         for (vp = *to; vp != NULL; vp = vp->next) count++;
944         to_list = rad_malloc(sizeof(*to_list) * count);
945
946         /*
947          *      Move the lists to the arrays, and break the list
948          *      chains.
949          */
950         from_count = 0;
951         for (vp = from; vp != NULL; vp = next) {
952                 next = vp->next;
953                 from_list[from_count++] = vp;
954                 vp->next = NULL;
955         }
956
957         to_count = 0;
958         for (vp = *to; vp != NULL; vp = next) {
959                 next = vp->next;
960                 to_list[to_count++] = vp;
961                 vp->next = NULL;
962         }
963         tailto = to_count;
964         edited = rad_malloc(sizeof(*edited) * to_count);
965         memset(edited, 0, sizeof(*edited) * to_count);
966
967         RDEBUG4("::: FROM %d TO %d MAX %d", from_count, to_count, count);
968
969         /*
970          *      Now that we have the lists initialized, start working
971          *      over them.
972          */
973         for (i = 0; i < from_count; i++) {
974                 int found;
975
976                 RDEBUG4("::: Examining %s", from_list[i]->name);
977
978                 /*
979                  *      Attribute should be appended, OR the "to" list
980                  *      is empty, and we're supposed to replace or
981                  *      "add if not existing".
982                  */
983                 if (from_list[i]->operator == T_OP_ADD) goto append;
984
985                 found = FALSE;
986                 for (j = 0; j < to_count; j++) {
987                         if (edited[j] || !to_list[j] || !from_list[i]) continue;
988
989                         /*
990                          *      Attributes aren't the same, skip them.
991                          */
992                         if ((from_list[i]->attribute != to_list[j]->attribute) ||
993                             (from_list[i]->vendor != to_list[j]->vendor)) {
994                                 continue;
995                         }
996
997                         /*
998                          *      We don't use a "switch" statement here
999                          *      because we want to break out of the
1000                          *      "for" loop over 'j' in most cases.
1001                          */
1002
1003                         /*
1004                          *      Over-write the FIRST instance of the
1005                          *      matching attribute name.  We free the
1006                          *      one in the "to" list, and move over
1007                          *      the one in the "from" list.
1008                          */
1009                         if (from_list[i]->operator == T_OP_SET) {
1010                                 RDEBUG4("::: OVERWRITING %s FROM %d TO %d",
1011                                        to_list[j]->name, i, j);
1012                                 pairfree(&to_list[j]);
1013                                 to_list[j] = from_list[i];
1014                                 from_list[i] = NULL;
1015                                 edited[j] = TRUE;
1016                                 break;
1017                         }
1018
1019                         /*
1020                          *      Add the attribute only if it does not
1021                          *      exist... but it exists, so we stop
1022                          *      looking.
1023                          */
1024                         if (from_list[i]->operator == T_OP_EQ) {
1025                                 found = TRUE;
1026                                 break;
1027                         }
1028
1029                         /*
1030                          *      Delete every attribute, independent
1031                          *      of its value.
1032                          */
1033                         if (from_list[i]->operator == T_OP_CMP_FALSE) {
1034                                 goto delete;
1035                         }
1036
1037                         /*
1038                          *      Delete all matching attributes from
1039                          *      "to"
1040                          */
1041                         if ((from_list[i]->operator == T_OP_SUB) ||
1042                             (from_list[i]->operator == T_OP_CMP_EQ) ||
1043                             (from_list[i]->operator == T_OP_LE) ||
1044                             (from_list[i]->operator == T_OP_GE)) {
1045                                 int rcode;
1046                                 int old_op = from_list[i]->operator;
1047
1048                                 /*
1049                                  *      Check for equality.
1050                                  */
1051                                 from_list[i]->operator = T_OP_CMP_EQ;
1052
1053                                 /*
1054                                  *      If equal, delete the one in
1055                                  *      the "to" list.
1056                                  */
1057                                 rcode = radius_compare_vps(NULL, from_list[i],
1058                                                            to_list[j]);
1059                                 /*
1060                                  *      We may want to do more
1061                                  *      subtractions, so we re-set the
1062                                  *      operator back to it's original
1063                                  *      value.
1064                                  */
1065                                 from_list[i]->operator = old_op;
1066
1067                                 switch (old_op) {
1068                                 case T_OP_CMP_EQ:
1069                                         if (rcode != 0) goto delete;
1070                                         break;
1071
1072                                 case T_OP_SUB:
1073                                         if (rcode == 0) {
1074                                         delete:
1075                                                 RDEBUG4("::: DELETING %s FROM %d TO %d",
1076                                                        from_list[i]->name, i, j);
1077                                                 pairfree(&to_list[j]);
1078                                                 to_list[j] = NULL;
1079                                         }
1080                                         break;
1081
1082                                         /*
1083                                          *      Enforce <=.  If it's
1084                                          *      >, replace it.
1085                                          */
1086                                 case T_OP_LE:
1087                                         if (rcode > 0) {
1088                                                 RDEBUG4("::: REPLACING %s FROM %d TO %d",
1089                                                        from_list[i]->name, i, j);
1090                                                 pairfree(&to_list[j]);
1091                                                 to_list[j] = from_list[i];
1092                                                 from_list[i] = NULL;
1093                                                 edited[j] = TRUE;
1094                                         }
1095                                         break;
1096
1097                                 case T_OP_GE:
1098                                         if (rcode < 0) {
1099                                                 RDEBUG4("::: REPLACING %s FROM %d TO %d",
1100                                                        from_list[i]->name, i, j);
1101                                                 pairfree(&to_list[j]);
1102                                                 to_list[j] = from_list[i];
1103                                                 from_list[i] = NULL;
1104                                                 edited[j] = TRUE;
1105                                         }
1106                                         break;
1107                                 }
1108
1109                                 continue;
1110                         }
1111
1112                         rad_assert(0 == 1); /* panic! */
1113                 }
1114
1115                 /*
1116                  *      We were asked to add it if it didn't exist,
1117                  *      and it doesn't exist.  Move it over to the
1118                  *      tail of the "to" list, UNLESS it was already
1119                  *      moved by another operator.
1120                  */
1121                 if (!found && from_list[i]) {
1122                         if ((from_list[i]->operator == T_OP_EQ) ||
1123                             (from_list[i]->operator == T_OP_LE) ||
1124                             (from_list[i]->operator == T_OP_GE) ||
1125                             (from_list[i]->operator == T_OP_SET)) {
1126                         append:
1127                                 RDEBUG4("::: APPENDING %s FROM %d TO %d",
1128                                        from_list[i]->name, i, tailto);
1129                                 to_list[tailto++] = from_list[i];
1130                                 from_list[i] = NULL;
1131                         }
1132                 }
1133         }
1134
1135         /*
1136          *      Delete attributes in the "from" list.
1137          */
1138         for (i = 0; i < from_count; i++) {
1139                 if (!from_list[i]) continue;
1140
1141                 pairfree(&from_list[i]);
1142         }
1143         free(from_list);
1144
1145         RDEBUG4("::: TO in %d out %d", to_count, tailto);
1146
1147         /*
1148          *      Re-chain the "to" list.
1149          */
1150         *to = NULL;
1151         last = to;
1152
1153         for (i = 0; i < tailto; i++) {
1154                 if (!to_list[i]) continue;
1155                 
1156                 RDEBUG4("::: to[%d] = %s", i, to_list[i]->name);
1157
1158                 /*
1159                  *      Mash the operator to a simple '='.  The
1160                  *      operators in the "to" list aren't used for
1161                  *      anything.  BUT they're used in the "detail"
1162                  *      file and debug output, where we don't want to
1163                  *      see the operators.
1164                  */
1165                 to_list[i]->operator = T_OP_EQ;
1166
1167                 *last = to_list[i];
1168                 last = &(*last)->next;
1169         }
1170
1171         rad_assert(request != NULL);
1172         rad_assert(request->packet != NULL);
1173
1174         /*
1175          *      Fix dumb cache issues
1176          */
1177         if (to == &request->packet->vps) {
1178                 fix_up(request);
1179         } else if (request->parent && (to == &request->parent->packet->vps)) {
1180                 fix_up(request->parent);
1181         }
1182
1183         free(to_list);
1184         free(edited);
1185 }
1186
1187
1188 #ifdef WITH_UNLANG
1189 /*
1190  *     Copied shamelessly from conffile.c, to simplify the API for
1191  *     now...
1192  */
1193 typedef enum conf_type {
1194         CONF_ITEM_INVALID = 0,
1195         CONF_ITEM_PAIR,
1196         CONF_ITEM_SECTION,
1197         CONF_ITEM_DATA
1198 } CONF_ITEM_TYPE;
1199
1200 struct conf_item {
1201         struct conf_item *next;
1202         struct conf_part *parent;
1203         int lineno;
1204         const char *filename;
1205         CONF_ITEM_TYPE type;
1206 };
1207 struct conf_pair {
1208         CONF_ITEM item;
1209         char *attr;
1210         char *value;
1211         FR_TOKEN operator;
1212         FR_TOKEN value_type;
1213 };
1214
1215 /*
1216  *      Add attributes to a list.
1217  */
1218 int radius_update_attrlist(REQUEST *request, CONF_SECTION *cs,
1219                            VALUE_PAIR *input_vps, const char *name)
1220 {
1221         CONF_ITEM *ci;
1222         VALUE_PAIR *newlist, *vp;
1223         VALUE_PAIR **output_vps = NULL;
1224         REQUEST *myrequest = request;
1225
1226         if (!request || !cs) return RLM_MODULE_INVALID;
1227
1228         /*
1229          *      If we are an inner tunnel session, permit the
1230          *      policy to update the outer lists directly.
1231          */
1232         if (strncmp(name, "outer.", 6) == 0) {
1233                 if (!request->parent) return RLM_MODULE_NOOP;
1234
1235                 myrequest = request->parent;
1236                 name += 6;
1237         }
1238
1239         if (strcmp(name, "request") == 0) {
1240                 output_vps = &myrequest->packet->vps;
1241
1242         } else if (strcmp(name, "reply") == 0) {
1243                 output_vps = &myrequest->reply->vps;
1244
1245 #ifdef WITH_PROXY
1246         } else if (strcmp(name, "proxy-request") == 0) {
1247                 if (request->proxy) output_vps = &myrequest->proxy->vps;
1248
1249         } else if (strcmp(name, "proxy-reply") == 0) {
1250                 if (request->proxy_reply) output_vps = &request->proxy_reply->vps;
1251 #endif
1252
1253         } else if (strcmp(name, "config") == 0) {
1254                 output_vps = &myrequest->config_items;
1255
1256         } else if (strcmp(name, "control") == 0) {
1257                 output_vps = &myrequest->config_items;
1258
1259 #ifdef WITH_COA
1260         } else if (strcmp(name, "coa") == 0) {
1261                 if (!myrequest->coa) {
1262                         request_alloc_coa(myrequest);
1263                         myrequest->coa->proxy->code = PW_COA_REQUEST;
1264                 }
1265                   
1266                 if (myrequest->coa &&
1267                     (myrequest->coa->proxy->code == PW_COA_REQUEST)) {
1268                         output_vps = &myrequest->coa->proxy->vps;
1269                 }
1270
1271         } else if (strcmp(name, "coa-reply") == 0) {
1272                 if (myrequest->coa && /* match reply with request */
1273                     (myrequest->coa->proxy->code == PW_COA_REQUEST) &&
1274                      (myrequest->coa->proxy_reply)) {
1275                       output_vps = &myrequest->coa->proxy_reply->vps;
1276                 }
1277
1278         } else if (strcmp(name, "disconnect") == 0) {
1279                 if (!myrequest->coa) {
1280                         request_alloc_coa(myrequest);
1281                         if (myrequest->coa) myrequest->coa->proxy->code = PW_DISCONNECT_REQUEST;
1282                 }
1283
1284                 if (myrequest->coa &&
1285                     (myrequest->coa->proxy->code == PW_DISCONNECT_REQUEST)) {
1286                         output_vps = &myrequest->coa->proxy->vps;
1287                 }
1288
1289         } else if (strcmp(name, "disconnect-reply") == 0) {
1290                 if (myrequest->coa && /* match reply with request */
1291                     (myrequest->coa->proxy->code == PW_DISCONNECT_REQUEST) &&
1292                     (myrequest->coa->proxy_reply)) {
1293                         output_vps = &myrequest->coa->proxy_reply->vps;
1294                 }
1295 #endif
1296
1297         } else {
1298                 return RLM_MODULE_INVALID;
1299         }
1300
1301         if (!output_vps) return RLM_MODULE_NOOP; /* didn't update the list */
1302
1303         newlist = paircopy(input_vps);
1304         if (!newlist) {
1305                 RDEBUG2("Out of memory");
1306                 return RLM_MODULE_FAIL;
1307         }
1308
1309         vp = newlist;
1310         for (ci=cf_item_find_next(cs, NULL);
1311              ci != NULL;
1312              ci=cf_item_find_next(cs, ci)) {
1313                 CONF_PAIR *cp;
1314
1315                 /*
1316                  *      This should never happen.
1317                  */
1318                 if (cf_item_is_section(ci)) {
1319                         pairfree(&newlist);
1320                         return RLM_MODULE_INVALID;
1321                 }
1322
1323                 cp = cf_itemtopair(ci);
1324
1325 #ifndef NDEBUG
1326                 if (debug_flag && (vp->vendor == 0) &&
1327                     radius_find_compare(vp->attribute)) {
1328                         DEBUG("WARNING: You are modifying the value of virtual attribute %s.  This is not supported.", vp->name);
1329                 }
1330 #endif
1331
1332                 /*
1333                  *      The VP && CF lists should be in sync.  If they're
1334                  *      not, panic.
1335                  */
1336                 if (vp->flags.do_xlat) {
1337                         const char *value;
1338                         char buffer[2048];
1339
1340                         value = expand_string(buffer, sizeof(buffer), request,
1341                                               cp->value_type, cp->value);
1342                         if (!value) {
1343                                 pairfree(&newlist);
1344                                 return RLM_MODULE_INVALID;
1345                         }
1346
1347                         if (!pairparsevalue(vp, value)) {
1348                                 RDEBUG2("ERROR: Failed parsing value \"%s\" for attribute %s: %s",
1349                                        value, vp->name, fr_strerror());
1350                                 pairfree(&newlist);
1351                                 return RLM_MODULE_FAIL;
1352                         }
1353                         vp->flags.do_xlat = 0;
1354                 }
1355                 vp = vp->next;
1356         }
1357
1358         radius_pairmove(request, output_vps, newlist);
1359
1360         return RLM_MODULE_UPDATED;
1361 }
1362 #endif