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