Fix condition for attribute not found
[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->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