Signed / unsigned fixes and function prototypes
[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         unsigned int attribute;
50         unsigned 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 (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                     (check->vendor == 0)) {
271                         return (c->compare)(c->instance, req, request, check,
272                                 check_pairs, reply_pairs);
273                 }
274
275         if (!request) return -1; /* doesn't exist, don't compare it */
276
277         return radius_compare_vps(req, check, request);
278 }
279
280
281 /*
282  *      Find a comparison function for two attributes.
283  */
284 int radius_find_compare(unsigned int attribute)
285 {
286         struct cmp *c;
287
288         for (c = cmp; c; c = c->next) {
289                 if (c->attribute == attribute) {
290                         return TRUE;
291                 }
292         }
293
294         return FALSE;
295 }
296
297
298 /*
299  *      See what attribute we want to compare with.
300  */
301 static int otherattr(unsigned int attr)
302 {
303         struct cmp      *c;
304
305         for (c = cmp; c; c = c->next) {
306                 if (c->attribute == attr)
307                         return c->otherattr;
308         }
309
310         return attr;
311 }
312
313 /*
314  *      Register a function as compare function.
315  *      compare_attr is the attribute in the request we want to
316  *      compare with. Normally this is the same as "attr".
317  *      You can set this to:
318  *
319  *      -1   the same as "attr"
320  *      0    always call compare function, not tied to request attribute
321  *      >0   Attribute to compare with.
322  *
323  *      For example, PW_GROUP in a check item needs to be compared
324  *      with PW_USER_NAME in the incoming request.
325  */
326 int paircompare_register(unsigned int attr, int compare_attr, RAD_COMPARE_FUNC fun, void *instance)
327 {
328         struct cmp      *c;
329
330         paircompare_unregister(attr, fun);
331
332         c = rad_malloc(sizeof(struct cmp));
333
334         c->compare = fun;
335         c->attribute = attr;
336         c->otherattr = compare_attr;
337         c->instance = instance;
338         c->next = cmp;
339         cmp = c;
340
341         return 0;
342 }
343
344 /*
345  *      Unregister a function.
346  */
347 void paircompare_unregister(unsigned int attr, RAD_COMPARE_FUNC fun)
348 {
349         struct cmp      *c, *last;
350
351         last = NULL;
352         for (c = cmp; c; c = c->next) {
353                 if (c->attribute == attr && c->compare == fun)
354                         break;
355                 last = c;
356         }
357
358         if (c == NULL) return;
359
360         if (last != NULL)
361                 last->next = c->next;
362         else
363                 cmp = c->next;
364
365         free(c);
366 }
367
368 /*
369  *      Compare two pair lists except for the password information.
370  *      For every element in "check" at least one matching copy must
371  *      be present in "reply".
372  *
373  *      Return 0 on match.
374  */
375 int paircompare(REQUEST *req, VALUE_PAIR *request, VALUE_PAIR *check, VALUE_PAIR **reply)
376 {
377         VALUE_PAIR *check_item;
378         VALUE_PAIR *auth_item;
379         int result = 0;
380         int compare;
381         int other;
382
383         for (check_item = check; check_item != NULL; check_item = check_item->next) {
384                 /*
385                  *      If the user is setting a configuration value,
386                  *      then don't bother comparing it to any attributes
387                  *      sent to us by the user.  It ALWAYS matches.
388                  */
389                 if ((check_item->operator == T_OP_SET) ||
390                     (check_item->operator == T_OP_ADD)) {
391                         continue;
392                 }
393
394                 switch (check_item->attribute) {
395                         /*
396                          *      Attributes we skip during comparison.
397                          *      These are "server" check items.
398                          */
399                         case PW_CRYPT_PASSWORD:
400                         case PW_AUTH_TYPE:
401                         case PW_AUTZ_TYPE:
402                         case PW_ACCT_TYPE:
403                         case PW_SESSION_TYPE:
404                         case PW_STRIP_USER_NAME:
405                                 continue;
406                                 break;
407
408                         /*
409                          *      IF the password attribute exists, THEN
410                          *      we can do comparisons against it.  If not,
411                          *      then the request did NOT contain a
412                          *      User-Password attribute, so we CANNOT do
413                          *      comparisons against it.
414                          *
415                          *      This hack makes CHAP-Password work..
416                          */
417                         case PW_USER_PASSWORD:
418                                 if (check_item->operator == T_OP_CMP_EQ) {
419                                         DEBUG("WARNING: Found User-Password == \"...\".");
420                                         DEBUG("WARNING: Are you sure you don't mean Cleartext-Password?");
421                                         DEBUG("WARNING: See \"man rlm_pap\" for more information.");
422                                 }
423                                 if (pairfind(request, PW_USER_PASSWORD, 0) == NULL) {
424                                         continue;
425                                 }
426                                 break;
427                 }
428
429                 /*
430                  *      See if this item is present in the request.
431                  */
432                 other = otherattr(check_item->attribute);
433
434                 auth_item = request;
435         try_again:
436                 if (other >= 0) {
437                         for (; auth_item != NULL; auth_item = auth_item->next) {
438                           if (auth_item->attribute == (unsigned int) other || other == 0)
439                                         break;
440                         }
441                 }
442
443                 /*
444                  *      Not found, it's not a match.
445                  */
446                 if (auth_item == NULL) {
447                         /*
448                          *      Didn't find it.  If we were *trying*
449                          *      to not find it, then we succeeded.
450                          */
451                         if (check_item->operator == T_OP_CMP_FALSE)
452                                 continue;
453                         else
454                                 return -1;
455                 }
456
457                 /*
458                  *      Else we found it, but we were trying to not
459                  *      find it, so we failed.
460                  */
461                 if (check_item->operator == T_OP_CMP_FALSE)
462                         return -1;
463
464
465                 /*
466                  *      We've got to xlat the string before doing
467                  *      the comparison.
468                  */
469                 if (check_item->flags.do_xlat) {
470                         int rcode;
471                         char buffer[sizeof(check_item->vp_strvalue)];
472
473                         check_item->flags.do_xlat = 0;
474                         rcode = radius_xlat(buffer, sizeof(buffer),
475                                             check_item->vp_strvalue,
476                                             req, NULL);
477
478                         /*
479                          *      Parse the string into a new value.
480                          */
481                         pairparsevalue(check_item, buffer);
482                 }
483
484                 /*
485                  *      OK it is present now compare them.
486                  */
487                 compare = radius_callback_compare(req, auth_item, check_item,
488                                                   check, reply);
489
490                 switch (check_item->operator) {
491                         case T_OP_EQ:
492                         default:
493                                 radlog(L_INFO,  "Invalid operator for item %s: "
494                                                 "reverting to '=='", check_item->name);
495                                 /*FALLTHRU*/
496                         case T_OP_CMP_TRUE:    /* compare always == 0 */
497                         case T_OP_CMP_FALSE:   /* compare always == 1 */
498                         case T_OP_CMP_EQ:
499                                 if (compare != 0) result = -1;
500                                 break;
501
502                         case T_OP_NE:
503                                 if (compare == 0) result = -1;
504                                 break;
505
506                         case T_OP_LT:
507                                 if (compare >= 0) result = -1;
508                                 break;
509
510                         case T_OP_GT:
511                                 if (compare <= 0) result = -1;
512                                 break;
513
514                         case T_OP_LE:
515                                 if (compare > 0) result = -1;
516                                 break;
517
518                         case T_OP_GE:
519                                 if (compare < 0) result = -1;
520                                 break;
521
522 #ifdef HAVE_REGEX_H
523                         case T_OP_REG_EQ:
524                         case T_OP_REG_NE:
525                                 result = compare;
526                                 break;
527 #endif
528                 } /* switch over the operator of the check item */
529
530                 /*
531                  *      This attribute didn't match, but maybe there's
532                  *      another of the same attribute, which DOES match.
533                  */
534                 if ((result != 0) && (other >= 0)) {
535                         auth_item = auth_item->next;
536                         result = 0;
537                         goto try_again;
538                 }
539
540         } /* for every entry in the check item list */
541
542         return result;
543 }
544
545 /*
546  *      Move pairs, replacing/over-writing them, and doing xlat.
547  */
548 /*
549  *      Move attributes from one list to the other
550  *      if not already present.
551  */
552 void pairxlatmove(REQUEST *req, VALUE_PAIR **to, VALUE_PAIR **from)
553 {
554         VALUE_PAIR **tailto, *i, *j, *next;
555         VALUE_PAIR *tailfrom = NULL;
556         VALUE_PAIR *found;
557
558         /*
559          *      Point "tailto" to the end of the "to" list.
560          */
561         tailto = to;
562         for(i = *to; i; i = i->next) {
563                 tailto = &i->next;
564         }
565
566         /*
567          *      Loop over the "from" list.
568          */
569         for(i = *from; i; i = next) {
570                 next = i->next;
571
572                 /*
573                  *      Don't move 'fallthrough' over.
574                  */
575                 if (i->attribute == PW_FALL_THROUGH) {
576                         continue;
577                 }
578
579                 /*
580                  *      We've got to xlat the string before moving
581                  *      it over.
582                  */
583                 if (i->flags.do_xlat) {
584                         int rcode;
585                         char buffer[sizeof(i->vp_strvalue)];
586
587                         i->flags.do_xlat = 0;
588                         rcode = radius_xlat(buffer, sizeof(buffer),
589                                             i->vp_strvalue,
590                                             req, NULL);
591
592                         /*
593                          *      Parse the string into a new value.
594                          */
595                         pairparsevalue(i, buffer);
596                 }
597
598                 found = pairfind(*to, i->attribute, i->vendor);
599                 switch (i->operator) {
600
601                         /*
602                          *  If a similar attribute is found,
603                          *  delete it.
604                          */
605                 case T_OP_SUB:          /* -= */
606                         if (found) {
607                                 if (!i->vp_strvalue[0] ||
608                                     (strcmp((char *)found->vp_strvalue,
609                                             (char *)i->vp_strvalue) == 0)){
610                                   pairdelete(to, found->attribute, found->vendor);
611
612                                         /*
613                                          *      'tailto' may have been
614                                          *      deleted...
615                                          */
616                                         tailto = to;
617                                         for(j = *to; j; j = j->next) {
618                                                 tailto = &j->next;
619                                         }
620                                 }
621                         }
622                         tailfrom = i;
623                         continue;
624                         break;
625
626                         /*
627                          *  Add it, if it's not already there.
628                          */
629                 case T_OP_EQ:           /* = */
630                         if (found) {
631                                 tailfrom = i;
632                                 continue; /* with the loop */
633                         }
634                         break;
635
636                         /*
637                          *  If a similar attribute is found,
638                          *  replace it with the new one.  Otherwise,
639                          *  add the new one to the list.
640                          */
641                 case T_OP_SET:          /* := */
642                         if (found) {
643                                 VALUE_PAIR *vp;
644
645                                 vp = found->next;
646                                 memcpy(found, i, sizeof(*found));
647                                 found->next = vp;
648                                 tailfrom = i;
649                                 continue;
650                         }
651                         break;
652
653                         /*
654                          *  FIXME: Add support for <=, >=, <, >
655                          *
656                          *  which will mean (for integers)
657                          *  'make the attribute the smaller, etc'
658                          */
659
660                         /*
661                          *  Add the new element to the list, even
662                          *  if similar ones already exist.
663                          */
664                 default:
665                 case T_OP_ADD:          /* += */
666                         break;
667                 }
668
669                 if (tailfrom)
670                         tailfrom->next = next;
671                 else
672                         *from = next;
673
674                 /*
675                  *      If ALL of the 'to' attributes have been deleted,
676                  *      then ensure that the 'tail' is updated to point
677                  *      to the head.
678                  */
679                 if (!*to) {
680                         tailto = to;
681                 }
682                 *tailto = i;
683                 if (i) {
684                         i->next = NULL;
685                         tailto = &i->next;
686                 }
687         } /* loop over the 'from' list */
688 }
689
690 /*
691  *      Create a pair, and add it to a particular list of VPs
692  *
693  *      Note that this function ALWAYS returns.  If we're OOM, then
694  *      it causes the server to exit!
695  */
696 VALUE_PAIR *radius_paircreate(REQUEST *request, VALUE_PAIR **vps,
697                               unsigned int attribute, unsigned int vendor, int type)
698 {
699         VALUE_PAIR *vp;
700
701         request = request;      /* -Wunused */
702
703         vp = paircreate(attribute, vendor, type);
704         if (!vp) {
705                 radlog(L_ERR, "No memory!");
706                 rad_assert("No memory" == NULL);
707                 _exit(1);
708         }
709
710         if (vps) pairadd(vps, vp);
711
712         return vp;
713 }
714
715 /*
716  *      Create a pair, and add it to a particular list of VPs
717  *
718  *      Note that this function ALWAYS returns.  If we're OOM, then
719  *      it causes the server to exit!
720  */
721 VALUE_PAIR *radius_pairmake(REQUEST *request, VALUE_PAIR **vps,
722                             const char *attribute, const char *value,
723                             int operator)
724 {
725         VALUE_PAIR *vp;
726
727         request = request;      /* -Wunused */
728
729         vp = pairmake(attribute, value, operator);
730         if (!vp) return NULL;
731
732         if (vps) pairadd(vps, vp);
733
734         return vp;
735 }
736
737 void debug_pair(VALUE_PAIR *vp)
738 {
739         if (!vp || !debug_flag || !fr_log_fp) return;
740
741         fputc('\t', fr_log_fp);
742         vp_print(fr_log_fp, vp);
743         fputc('\n', fr_log_fp);
744 }
745
746 void debug_pair_list(VALUE_PAIR *vp)
747 {
748         if (!vp || !debug_flag || !fr_log_fp) return;
749
750         while (vp) {
751                 fputc('\t', fr_log_fp);
752                 vp_print(fr_log_fp, vp);
753                 fputc('\n', fr_log_fp);
754                 vp = vp->next;
755         }
756         fflush(fr_log_fp);
757 }