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