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