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