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