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