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