Massively cleaned up #include's, so they're in a consistent
[freeradius.git] / src / main / valuepair.c
1 /*
2  * valuepair.c  Valuepair functions that are radiusd-specific
3  *              and as such do not belong in the library.
4  *
5  * Version:     $Id$
6  *
7  *   This program is free software; you can redistribute it and/or modify
8  *   it under the terms of the GNU General Public License as published by
9  *   the Free Software Foundation; either version 2 of the License, or
10  *   (at your option) any later version.
11  *
12  *   This program is distributed in the hope that it will be useful,
13  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
14  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  *   GNU General Public License for more details.
16  *
17  *   You should have received a copy of the GNU General Public License
18  *   along with this program; if not, write to the Free Software
19  *   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20  *
21  * Copyright 2000,2006  The FreeRADIUS server project
22  * Copyright 2000  Alan DeKok <aland@ox.org>
23  */
24
25 #include <freeradius-devel/ident.h>
26 RCSID("$Id$")
27
28 #include <freeradius-devel/radiusd.h>
29
30 #ifdef HAVE_REGEX_H
31 #       include <regex.h>
32
33 /*
34  *  For POSIX Regular expressions.
35  *  (0) Means no extended regular expressions.
36  *  REG_EXTENDED means use extended regular expressions.
37  */
38 #ifndef REG_EXTENDED
39 #define REG_EXTENDED (0)
40 #endif
41
42 #ifndef REG_NOSUB
43 #define REG_NOSUB (0)
44 #endif
45 #endif
46
47 struct cmp {
48         int attribute;
49         int otherattr;
50         void *instance; /* module instance */
51         RAD_COMPARE_FUNC compare;
52         struct cmp *next;
53 };
54 static struct cmp *cmp;
55
56
57 /*
58  *      Compare 2 attributes. May call the attribute compare function.
59  */
60 static int compare_pair(REQUEST *req, VALUE_PAIR *request, VALUE_PAIR *check,
61                        VALUE_PAIR *check_pairs, VALUE_PAIR **reply_pairs)
62 {
63         int ret = -2;
64         struct cmp *c;
65
66         /*
67          *      Check for =* and !* and return appropriately
68          */
69         if( check->operator == T_OP_CMP_TRUE )
70                  return 0;  /* always return 0/EQUAL */
71         if( check->operator == T_OP_CMP_FALSE )
72                  return 1;  /* always return 1/NOT EQUAL */
73
74         /*
75          *      See if there is a special compare function.
76          *
77          *      FIXME: use new RB-Tree code.
78          */
79         for (c = cmp; c; c = c->next)
80                 if (c->attribute == check->attribute)
81                         return (c->compare)(c->instance, req, request, check,
82                                 check_pairs, reply_pairs);
83
84         if (!request) return -1; /* doesn't exist, don't compare it */
85
86         switch(check->type) {
87 #ifdef ASCEND_BINARY
88                 /*
89                  *      Ascend binary attributes can be treated
90                  *      as opaque objects, I guess...
91                  */
92                 case PW_TYPE_ABINARY:
93 #endif
94                 case PW_TYPE_OCTETS:
95                         if (request->length != check->length) {
96                                 ret = 1; /* NOT equal */
97                                 break;
98                         }
99                         ret = memcmp(request->vp_strvalue, check->vp_strvalue,
100                                         request->length);
101                         break;
102                 case PW_TYPE_STRING:
103                         if (check->flags.caseless) {
104                                 ret = strcasecmp((char *)request->vp_strvalue,
105                                                  (char *)check->vp_strvalue);
106                         } else {
107                                 ret = strcmp((char *)request->vp_strvalue,
108                                              (char *)check->vp_strvalue);
109                         }
110                         break;
111                 case PW_TYPE_INTEGER:
112                 case PW_TYPE_DATE:
113                         ret = request->lvalue - check->lvalue;
114                         break;
115                 case PW_TYPE_IPADDR:
116                         ret = ntohl(request->vp_ipaddr) - ntohl(check->vp_ipaddr);
117                         break;
118                 case PW_TYPE_IPV6ADDR:
119                         ret = memcmp(&request->vp_ipv6addr, &check->vp_ipv6addr,
120                                      sizeof(request->vp_ipv6addr));
121                         break;
122                         
123                 case PW_TYPE_IPV6PREFIX:
124                         ret = memcmp(&request->vp_ipv6prefix, &check->vp_ipv6prefix,
125                                      sizeof(request->vp_ipv6prefix));
126                         break;
127                 
128                 case PW_TYPE_IFID:
129                         ret = memcmp(&request->vp_ifid, &check->vp_ifid,
130                                      sizeof(request->vp_ifid));
131                         break;
132
133                 default:
134                         break;
135         }
136
137         return ret;
138 }
139
140
141 /*
142  *      See what attribute we want to compare with.
143  */
144 static int otherattr(int attr)
145 {
146         struct cmp      *c;
147
148         for (c = cmp; c; c = c->next) {
149                 if (c->attribute == attr)
150                         return c->otherattr;
151         }
152
153         return attr;
154 }
155
156 /*
157  *      Register a function as compare function.
158  *      compare_attr is the attribute in the request we want to
159  *      compare with. Normally this is the same as "attr".
160  *      You can set this to:
161  *
162  *      -1   the same as "attr"
163  *      0    always call compare function, not tied to request attribute
164  *      >0   Attribute to compare with.
165  *
166  *      For example, PW_GROUP in a check item needs to be compared
167  *      with PW_USER_NAME in the incoming request.
168  */
169 int paircompare_register(int attr, int compare_attr, RAD_COMPARE_FUNC fun, void *instance)
170 {
171         struct cmp      *c;
172
173         paircompare_unregister(attr, fun);
174
175         c = rad_malloc(sizeof(struct cmp));
176
177         if (compare_attr < 0)
178                 compare_attr = attr;
179         c->compare = fun;
180         c->attribute = attr;
181         c->otherattr = compare_attr;
182         c->instance = instance;
183         c->next = cmp;
184         cmp = c;
185
186         return 0;
187 }
188
189 /*
190  *      Unregister a function.
191  */
192 void paircompare_unregister(int attr, RAD_COMPARE_FUNC fun)
193 {
194         struct cmp      *c, *last;
195
196         last = NULL;
197         for (c = cmp; c; c = c->next) {
198                 if (c->attribute == attr && c->compare == fun)
199                         break;
200                 last = c;
201         }
202
203         if (c == NULL) return;
204
205         if (last != NULL)
206                 last->next = c->next;
207         else
208                 cmp = c->next;
209
210         free(c);
211 }
212
213 /*
214  *      Compare two pair lists except for the password information.
215  *      For every element in "check" at least one matching copy must
216  *      be present in "reply".
217  *
218  *      Return 0 on match.
219  */
220 int paircompare(REQUEST *req, VALUE_PAIR *request, VALUE_PAIR *check, VALUE_PAIR **reply)
221 {
222         VALUE_PAIR *check_item;
223         VALUE_PAIR *auth_item;
224         int result = 0;
225         int compare;
226         int other;
227 #ifdef HAVE_REGEX_H
228         regex_t reg;
229 #endif
230
231         for (check_item = check; check_item != NULL; check_item = check_item->next) {
232                 /*
233                  *      If the user is setting a configuration value,
234                  *      then don't bother comparing it to any attributes
235                  *      sent to us by the user.  It ALWAYS matches.
236                  */
237                 if ((check_item->operator == T_OP_SET) ||
238                     (check_item->operator == T_OP_ADD)) {
239                         continue;
240                 }
241
242                 switch (check_item->attribute) {
243                         /*
244                          *      Attributes we skip during comparison.
245                          *      These are "server" check items.
246                          */
247                         case PW_CRYPT_PASSWORD:
248                         case PW_AUTH_TYPE:
249                         case PW_AUTZ_TYPE:
250                         case PW_ACCT_TYPE:
251                         case PW_SESSION_TYPE:
252                         case PW_STRIP_USER_NAME:
253                                 continue;
254                                 break;
255
256                         /*
257                          *      IF the password attribute exists, THEN
258                          *      we can do comparisons against it.  If not,
259                          *      then the request did NOT contain a
260                          *      User-Password attribute, so we CANNOT do
261                          *      comparisons against it.
262                          *
263                          *      This hack makes CHAP-Password work..
264                          */
265                         case PW_USER_PASSWORD:
266                                 if (pairfind(request, PW_USER_PASSWORD) == NULL) {
267                                         continue;
268                                 }
269                                 break;
270                 }
271
272                 /*
273                  *      See if this item is present in the request.
274                  */
275                 other = otherattr(check_item->attribute);
276
277                 auth_item = request;
278         try_again:
279                 for (; auth_item != NULL; auth_item = auth_item->next) {
280                         if (auth_item->attribute == other || other == 0)
281                                 break;
282                 }
283
284                 /*
285                  *      Not found, it's not a match.
286                  */
287                 if (auth_item == NULL) {
288                         /*
289                          *      Didn't find it.  If we were *trying*
290                          *      to not find it, then we succeeded.
291                          */
292                         if (check_item->operator == T_OP_CMP_FALSE)
293                                 return 0;
294                         else
295                                 return -1;
296                 }
297
298                 /*
299                  *      Else we found it, but we were trying to not
300                  *      find it, so we failed.
301                  */
302                 if (check_item->operator == T_OP_CMP_FALSE)
303                         return -1;
304
305
306                 /*
307                  *      We've got to xlat the string before doing
308                  *      the comparison.
309                  */
310                 if (check_item->flags.do_xlat) {
311                         int rcode;
312                         char buffer[sizeof(check_item->vp_strvalue)];
313
314                         check_item->flags.do_xlat = 0;
315                         rcode = radius_xlat(buffer, sizeof(buffer),
316                                             check_item->vp_strvalue,
317                                             req, NULL);
318
319                         /*
320                          *      Parse the string into a new value.
321                          */
322                         pairparsevalue(check_item, buffer);
323                 }
324
325                 /*
326                  *      OK it is present now compare them.
327                  */
328                 compare = compare_pair(req, auth_item, check_item, check, reply);
329
330                 switch (check_item->operator) {
331                         case T_OP_EQ:
332                         default:
333                                 radlog(L_INFO,  "Invalid operator for item %s: "
334                                                 "reverting to '=='", check_item->name);
335                                 /*FALLTHRU*/
336                         case T_OP_CMP_TRUE:    /* compare always == 0 */
337                         case T_OP_CMP_FALSE:   /* compare always == 1 */
338                         case T_OP_CMP_EQ:
339                                 if (compare != 0) result = -1;
340                                 break;
341
342                         case T_OP_NE:
343                                 if (compare == 0) result = -1;
344                                 break;
345
346                         case T_OP_LT:
347                                 if (compare >= 0) result = -1;
348                                 break;
349
350                         case T_OP_GT:
351                                 if (compare <= 0) result = -1;
352                                 break;
353
354                         case T_OP_LE:
355                                 if (compare > 0) result = -1;
356                                 break;
357
358                         case T_OP_GE:
359                                 if (compare < 0) result = -1;
360                                 break;
361
362 #ifdef HAVE_REGEX_H
363                         case T_OP_REG_EQ:
364                         {
365                                 int i;
366                                 regmatch_t rxmatch[REQUEST_MAX_REGEX + 1];
367
368                                 if ((auth_item->type == PW_TYPE_IPADDR) &&
369                                     (auth_item->vp_strvalue[0] == '\0')) {
370                                   inet_ntop(AF_INET, &(auth_item->lvalue),
371                                             auth_item->vp_strvalue,
372                                             sizeof(auth_item->vp_strvalue));
373                                 }
374
375                                 /*
376                                  *      Include substring matches.
377                                  */
378                                 regcomp(&reg, (char *)check_item->vp_strvalue,
379                                         REG_EXTENDED);
380                                 compare = regexec(&reg,
381                                                   (char *)auth_item->vp_strvalue,
382                                                   REQUEST_MAX_REGEX + 1,
383                                                   rxmatch, 0);
384                                 regfree(&reg);
385
386                                 /*
387                                  *      Add %{0}, %{1}, etc.
388                                  */
389                                 for (i = 0; i <= REQUEST_MAX_REGEX; i++) {
390                                         char *p;
391                                         char buffer[sizeof(check_item->vp_strvalue)];
392
393                                         /*
394                                          *      Didn't match: delete old
395                                          *      match, if it existed.
396                                          */
397                                         if ((compare != 0) ||
398                                             (rxmatch[i].rm_so == -1)) {
399                                                 p = request_data_get(req, req,
400                                                                      REQUEST_DATA_REGEX | i);
401                                                 if (p) {
402                                                         free(p);
403                                                         continue;
404                                                 }
405
406                                                 /*
407                                                  *      No previous match
408                                                  *      to delete, stop.
409                                                  */
410                                                 break;
411                                         }
412                                         
413                                         /*
414                                          *      Copy substring into buffer.
415                                          */
416                                         memcpy(buffer,
417                                                auth_item->vp_strvalue + rxmatch[i].rm_so,
418                                                rxmatch[i].rm_eo - rxmatch[i].rm_so);
419                                         buffer[rxmatch[i].rm_eo - rxmatch[i].rm_so] = '\0';
420
421                                         /*
422                                          *      Copy substring, and add it to
423                                          *      the request.
424                                          *
425                                          *      Note that we don't check
426                                          *      for out of memory, which is
427                                          *      the only error we can get...
428                                          */
429                                         p = strdup(buffer);
430                                         request_data_add(req,
431                                                          req,
432                                                          REQUEST_DATA_REGEX | i,
433                                                          p, free);
434                                 }
435                         }                               
436                                 if (compare != 0) result = -1;
437                                 break;
438
439                         case T_OP_REG_NE:
440                                 if ((auth_item->type == PW_TYPE_IPADDR) &&
441                                     (auth_item->vp_strvalue[0] == '\0')) {
442                                   inet_ntop(AF_INET, &(auth_item->lvalue),
443                                             auth_item->vp_strvalue,
444                                             sizeof(auth_item->vp_strvalue));
445                                 }
446
447                                 regcomp(&reg, (char *)check_item->vp_strvalue, REG_EXTENDED|REG_NOSUB);
448                                 compare = regexec(&reg, (char *)auth_item->vp_strvalue,
449                                                 0, NULL, 0);
450                                 regfree(&reg);
451                                 if (compare == 0) result = -1;
452                                 break;
453 #endif
454
455                 } /* switch over the operator of the check item */
456
457                 /*
458                  *      This attribute didn't match, but maybe there's
459                  *      another of the same attribute, which DOES match.
460                  */
461                 if (result != 0) {
462                         auth_item = auth_item->next;
463                         result = 0;
464                         goto try_again;
465                 }
466
467         } /* for every entry in the check item list */
468
469         return 0;               /* it matched */
470 }
471
472 /*
473  *      Compare two attributes simply.  Calls compare_pair.
474  */
475
476 int simplepaircmp(REQUEST *req, VALUE_PAIR *first, VALUE_PAIR *second)
477 {
478         return compare_pair( req, first, second, NULL, NULL );
479 }
480
481
482 /*
483  *      Move pairs, replacing/over-writing them, and doing xlat.
484  */
485 /*
486  *      Move attributes from one list to the other
487  *      if not already present.
488  */
489 void pairxlatmove(REQUEST *req, VALUE_PAIR **to, VALUE_PAIR **from)
490 {
491         VALUE_PAIR **tailto, *i, *j, *next;
492         VALUE_PAIR *tailfrom = NULL;
493         VALUE_PAIR *found;
494
495         /*
496          *      Point "tailto" to the end of the "to" list.
497          */
498         tailto = to;
499         for(i = *to; i; i = i->next) {
500                 tailto = &i->next;
501         }
502
503         /*
504          *      Loop over the "from" list.
505          */
506         for(i = *from; i; i = next) {
507                 next = i->next;
508
509                 /*
510                  *      Don't move 'fallthrough' over.
511                  */
512                 if (i->attribute == PW_FALL_THROUGH) {
513                         continue;
514                 }
515
516                 /*
517                  *      We've got to xlat the string before moving
518                  *      it over.
519                  */
520                 if (i->flags.do_xlat) {
521                         int rcode;
522                         char buffer[sizeof(i->vp_strvalue)];
523
524                         i->flags.do_xlat = 0;
525                         rcode = radius_xlat(buffer, sizeof(buffer),
526                                             i->vp_strvalue,
527                                             req, NULL);
528
529                         /*
530                          *      Parse the string into a new value.
531                          */
532                         pairparsevalue(i, buffer);
533                 }
534
535                 found = pairfind(*to, i->attribute);
536                 switch (i->operator) {
537
538                         /*
539                          *  If a similar attribute is found,
540                          *  delete it.
541                          */
542                 case T_OP_SUB:          /* -= */
543                         if (found) {
544                                 if (!i->vp_strvalue[0] ||
545                                     (strcmp((char *)found->vp_strvalue,
546                                             (char *)i->vp_strvalue) == 0)){
547                                         pairdelete(to, found->attribute);
548
549                                         /*
550                                          *      'tailto' may have been
551                                          *      deleted...
552                                          */
553                                         tailto = to;
554                                         for(j = *to; j; j = j->next) {
555                                                 tailto = &j->next;
556                                         }
557                                 }
558                         }
559                         tailfrom = i;
560                         continue;
561                         break;
562
563                         /*
564                          *  Add it, if it's not already there.
565                          */
566                 case T_OP_EQ:           /* = */
567                         if (found) {
568                                 tailfrom = i;
569                                 continue; /* with the loop */
570                         }
571                         break;
572
573                         /*
574                          *  If a similar attribute is found,
575                          *  replace it with the new one.  Otherwise,
576                          *  add the new one to the list.
577                          */
578                 case T_OP_SET:          /* := */
579                         if (found) {
580                                 VALUE_PAIR *vp;
581
582                                 vp = found->next;
583                                 memcpy(found, i, sizeof(*found));
584                                 found->next = vp;
585                                 continue;
586                         }
587                         break;
588
589                         /*
590                          *  FIXME: Add support for <=, >=, <, >
591                          *
592                          *  which will mean (for integers)
593                          *  'make the attribute the smaller, etc'
594                          */
595
596                         /*
597                          *  Add the new element to the list, even
598                          *  if similar ones already exist.
599                          */
600                 default:
601                 case T_OP_ADD:          /* += */
602                         break;
603                 }
604
605                 if (tailfrom)
606                         tailfrom->next = next;
607                 else
608                         *from = next;
609
610                 /*
611                  *      If ALL of the 'to' attributes have been deleted,
612                  *      then ensure that the 'tail' is updated to point
613                  *      to the head.
614                  */
615                 if (!*to) {
616                         tailto = to;
617                 }
618                 *tailto = i;
619                 if (i) {
620                         i->next = NULL;
621                         tailto = &i->next;
622                 }
623         } /* loop over the 'from' list */
624 }