Removed unused variable.
[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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
20  *
21  * Copyright 2000  The FreeRADIUS server project
22  * Copyright 2000  Alan DeKok <aland@ox.org>
23  */
24
25 static const char rcsid[] = "$Id$";
26
27 #include "autoconf.h"
28 #include "libradius.h"
29
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <string.h>
33
34 #if 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 #endif
50
51 #include "radiusd.h"
52
53 struct cmp {
54         int attribute;
55         int otherattr;
56         void *instance; /* module instance */
57         RAD_COMPARE_FUNC compare;
58         struct cmp *next;
59 };
60 static struct cmp *cmp;
61
62
63 /*
64  *      Compare 2 attributes. May call the attribute compare function.
65  */
66 static int paircompare(REQUEST *req, VALUE_PAIR *request, VALUE_PAIR *check,
67         VALUE_PAIR *check_pairs, VALUE_PAIR **reply_pairs)
68 {
69         int ret = -2;
70         struct cmp *c;
71
72         /*
73          *      Sanity check.
74          */
75 #if 0
76         if (request->attribute != check->attribute)
77                 return -2;
78 #endif
79
80         /*
81          *      Check for =* and !* and return appropriately
82          */
83         if( check->operator == T_OP_CMP_TRUE )
84                  return 0;  /* always return 0/EQUAL */
85         if( check->operator == T_OP_CMP_FALSE )
86                  return 1;  /* always return 1/NOT EQUAL */
87
88         /*
89          *      See if there is a special compare function.
90          */
91         for (c = cmp; c; c = c->next)
92                 if (c->attribute == check->attribute)
93                         return (c->compare)(c->instance, req, request, check,
94                                 check_pairs, reply_pairs);
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->strvalue, check->strvalue,
110                                         request->length);
111                         break;
112                 case PW_TYPE_STRING:
113                         ret = strcmp((char *)request->strvalue,
114                                         (char *)check->strvalue);
115                         break;
116                 case PW_TYPE_INTEGER:
117                 case PW_TYPE_DATE:
118                         ret = request->lvalue - check->lvalue;
119                         break;
120                 case PW_TYPE_IPADDR:
121                         ret = ntohl(request->lvalue) - ntohl(check->lvalue);
122                         break;
123                 default:
124                         break;
125         }
126
127         return ret;
128 }
129
130
131 /*
132  *      See what attribute we want to compare with.
133  */
134 static int otherattr(int attr)
135 {
136         struct cmp      *c;
137
138         for (c = cmp; c; c = c->next) {
139                 if (c->attribute == attr)
140                         return c->otherattr;
141         }
142
143         return attr;
144 }
145
146 /*
147  *      Register a function as compare function.
148  *      compare_attr is the attribute in the request we want to
149  *      compare with. Normally this is the same as "attr".
150  *      You can set this to:
151  *
152  *      -1   the same as "attr"
153  *      0    always call compare function, not tied to request attribute
154  *      >0   Attribute to compare with.
155  *
156  *      For example, PW_GROUP in a check item needs to be compared
157  *      with PW_USER_NAME in the incoming request.
158  */
159 int paircompare_register(int attr, int compare_attr, RAD_COMPARE_FUNC fun, void *instance)
160 {
161         struct cmp      *c;
162
163         paircompare_unregister(attr, fun);
164
165         c = rad_malloc(sizeof(struct cmp));
166
167         if (compare_attr < 0) 
168                 compare_attr = attr;
169         c->compare = fun;
170         c->attribute = attr;
171         c->otherattr = compare_attr;
172         c->instance = instance;
173         c->next = cmp;
174         cmp = c;
175
176         return 0;
177 }
178
179 /*
180  *      Unregister a function.
181  */
182 void paircompare_unregister(int attr, RAD_COMPARE_FUNC fun)
183 {
184         struct cmp      *c, *last;
185
186         last = NULL;
187         for (c = cmp; c; c = c->next) {
188                 if (c->attribute == attr && c->compare == fun)
189                         break;
190                 last = c;
191         }
192
193         if (c == NULL) return;
194
195         if (last != NULL)
196                 last->next = c->next;
197         else
198                 cmp = c->next;
199
200         free(c);
201 }
202
203 /*
204  *      Compare two pair lists except for the password information.
205  *      For every element in "check" at least one matching copy must
206  *      be present in "reply".
207  *
208  *      Return 0 on match.
209  */
210 int paircmp(REQUEST *req, VALUE_PAIR *request, VALUE_PAIR *check, VALUE_PAIR **reply)
211 {
212         VALUE_PAIR *check_item;
213         VALUE_PAIR *auth_item;
214         int result = 0;
215         int compare;
216         int other;
217 #ifdef HAVE_REGEX_H
218         regex_t reg;
219 #endif
220
221         for (check_item = check; check_item != NULL; check_item = check_item->next) {
222                 /*
223                  *      If the user is setting a configuration value,
224                  *      then don't bother comparing it to any attributes
225                  *      sent to us by the user.  It ALWAYS matches.
226                  */
227                 if ((check_item->operator == T_OP_SET) ||
228                     (check_item->operator == T_OP_ADD)) {
229                         continue;
230                 }
231
232                 switch (check_item->attribute) {
233                         /*
234                          *      Attributes we skip during comparison.
235                          *      These are "server" check items.
236                          */
237                         case PW_CRYPT_PASSWORD:
238                         case PW_AUTHTYPE:
239                         case PW_AUTZTYPE:
240                                 continue;
241                                 break;
242
243                         /*
244                          *      IF the password attribute exists, THEN
245                          *      we can do comparisons against it.  If not,
246                          *      then the request did NOT contain a
247                          *      User-Password attribute, so we CANNOT do
248                          *      comparisons against it.
249                          *
250                          *      This hack makes CHAP-Password work..
251                          */
252                         case PW_PASSWORD:
253                                 if (pairfind(request, PW_PASSWORD) == NULL) {
254                                         continue;
255                                 }
256                                 break;
257                 }
258
259                 /*
260                  *      See if this item is present in the request.
261                  */
262                 other = otherattr(check_item->attribute);
263
264                 auth_item = request;
265         try_again:
266                 for (; auth_item != NULL; auth_item = auth_item->next) {
267                         if (auth_item->attribute == other || other == 0)
268                                 break;
269                 }
270
271                 /*
272                  *      Not found, it's not a match.
273                  */
274                 if (auth_item == NULL) {
275                         return -1;
276                 }
277
278                 /*
279                  *      We've got to xlat the string before doing
280                  *      the comparison.
281                  */
282                 if (check_item->flags.do_xlat) {
283                         int rcode;
284                         char buffer[sizeof(check_item->strvalue)];
285
286                         check_item->flags.do_xlat = 0;
287                         rcode = radius_xlat(buffer, sizeof(buffer),
288                                             check_item->strvalue,
289                                             req, NULL);
290                         
291                         /*
292                          *      Parse the string into a new value.
293                          */
294                         pairparsevalue(check_item, buffer);
295                 }
296
297                 /*
298                  *      OK it is present now compare them.
299                  */
300                 compare = paircompare(req, auth_item, check_item, check, reply);
301
302                 switch (check_item->operator) {
303                         case T_OP_EQ:
304                         default:
305                                 radlog(L_ERR,  "Invalid operator for item %s: "
306                                                 "reverting to '=='", check_item->name);
307                                 /*FALLTHRU*/
308                         case T_OP_CMP_TRUE:    /* compare always == 0 */
309                         case T_OP_CMP_FALSE:   /* compare always == 1 */
310                         case T_OP_CMP_EQ:
311                                 if (compare != 0) result = -1;
312                                 break;
313
314                         case T_OP_NE:
315                                 if (compare == 0) result = -1;
316                                 break;
317
318                         case T_OP_LT:
319                                 if (compare >= 0) result = -1;
320                                 break;
321
322                         case T_OP_GT:
323                                 if (compare <= 0) result = -1;
324                                 break;
325                     
326                         case T_OP_LE:
327                                 if (compare > 0) result = -1;
328                                 break;
329
330                         case T_OP_GE:
331                                 if (compare < 0) result = -1;
332                                 break;
333
334 #ifdef HAVE_REGEX_H
335                         case T_OP_REG_EQ:
336                                 regcomp(&reg, (char *)check_item->strvalue, REG_EXTENDED);
337                                 compare = regexec(&reg, (char *)auth_item->strvalue,
338                                                 0, NULL, 0);
339                                 regfree(&reg);
340                                 if (compare != 0) result = -1;
341                                 break;
342
343                         case T_OP_REG_NE:
344                                 regcomp(&reg, (char *)check_item->strvalue, REG_EXTENDED);
345                                 compare = regexec(&reg, (char *)auth_item->strvalue,
346                                                 0, NULL, 0);
347                                 regfree(&reg);
348                                 if (compare == 0) result = -1;
349                                 break;
350 #endif
351
352                 } /* switch over the operator of the check item */
353
354                 /*
355                  *      This attribute didn't match, but maybe there's
356                  *      another of the same attribute, which DOES match.
357                  */
358                 if (result != 0) {
359                         auth_item = auth_item->next;
360                         result = 0;
361                         goto try_again;
362                 }
363
364         } /* for every entry in the check item list */
365
366         return 0;               /* it matched */
367 }
368
369 /*
370  *      Compare two attributes simply.  Calls paircompare.
371  */
372
373 int simplepaircmp(REQUEST *req, VALUE_PAIR *first, VALUE_PAIR *second)
374 {
375         return paircompare( req, first, second, NULL, NULL );
376 }
377
378
379 /*
380  *      Compare a Connect-Info and a Connect-Rate
381  */
382 static int connectcmp(void *instance, REQUEST *req, VALUE_PAIR *request, VALUE_PAIR *check,
383                 VALUE_PAIR *check_pairs, VALUE_PAIR **reply_pairs)
384 {
385         int rate;
386
387         instance = instance;
388         check_pairs = check_pairs; /* shut the compiler up */
389         reply_pairs = reply_pairs;
390
391         rate = atoi((char *)request->strvalue);
392         return rate - check->lvalue;
393 }
394
395
396 /*
397  *      Compare a portno with a range.
398  */
399 static int portcmp(void *instance, REQUEST *req, VALUE_PAIR *request, VALUE_PAIR *check,
400         VALUE_PAIR *check_pairs, VALUE_PAIR **reply_pairs)
401 {
402         char buf[MAX_STRING_LEN];
403         char *s, *p;
404         uint32_t lo, hi;
405         uint32_t port = request->lvalue;
406
407         instance = instance;
408         check_pairs = check_pairs; /* shut the compiler up */
409         reply_pairs = reply_pairs;
410
411         if ((strchr((char *)check->strvalue, ',') == NULL) &&
412                         (strchr((char *)check->strvalue, '-') == NULL)) {
413                 return (request->lvalue - check->lvalue);
414         }
415
416         /* Same size */
417         strcpy(buf, (char *)check->strvalue);
418         s = strtok(buf, ",");
419
420         while (s != NULL) {
421                 if ((p = strchr(s, '-')) != NULL)
422                         p++;
423                 else
424                         p = s;
425                 lo = strtoul(s, NULL, 10);
426                 hi = strtoul(p, NULL, 10);
427                 if (lo <= port && port <= hi) {
428                         return 0;
429                 }
430                 s = strtok(NULL, ",");
431         } 
432
433         return -1;
434 }
435
436 /*
437  *      Compare prefix/suffix.
438  *
439  *      If they compare: 
440  *      - if PW_STRIP_USER_NAME is present in check_pairs,
441  *        strip the username of prefix/suffix.
442  *      - if PW_STRIP_USER_NAME is not present in check_pairs,
443  *        add a PW_STRIPPED_USER_NAME to the request.
444  */
445 static int presufcmp(void *instance, REQUEST *req, VALUE_PAIR *request, VALUE_PAIR *check,
446         VALUE_PAIR *check_pairs, VALUE_PAIR **reply_pairs)
447 {
448         VALUE_PAIR *vp;
449         char *name = (char *)request->strvalue;
450         char rest[MAX_STRING_LEN];
451         int len, namelen;
452         int ret = -1;
453         
454         instance = instance;
455         reply_pairs = reply_pairs; /* shut the compiler up */
456
457 #if 0 /* DEBUG */
458         printf("Comparing %s and %s, check->attr is %d\n",
459                 name, check->strvalue, check->attribute);
460 #endif
461
462         len = strlen((char *)check->strvalue);
463         switch (check->attribute) {
464                 case PW_PREFIX:
465                         ret = strncmp(name, (char *)check->strvalue, len);
466                         if (ret == 0 && rest)
467                                 strcpy(rest, name + len);
468                         break;
469                 case PW_SUFFIX:
470                         namelen = strlen(name);
471                         if (namelen < len)
472                                 break;
473                         ret = strcmp(name + namelen - len,
474                                         (char *)check->strvalue);
475                         if (ret == 0 && rest) {
476                                 strncpy(rest, name, namelen - len);
477                                 rest[namelen - len] = 0;
478                         }
479                         break;
480         }
481         if (ret != 0)
482                 return ret;
483
484         if (pairfind(check_pairs, PW_STRIP_USER_NAME)) {
485                 /*
486                  *      I don't think we want to update the User-Name
487                  *      attribute in place... - atd
488                  */
489                 strcpy((char *)request->strvalue, rest);
490                 request->length = strlen(rest);
491         } else {
492                 if ((vp = pairfind(check_pairs, PW_STRIPPED_USER_NAME)) != NULL){
493                         strcpy((char *)vp->strvalue, rest);
494                         vp->length = strlen(rest);
495                 } else if ((vp = paircreate(PW_STRIPPED_USER_NAME,
496                                 PW_TYPE_STRING)) != NULL) {
497                         strcpy((char *)vp->strvalue, rest);
498                         vp->length = strlen(rest);
499                         pairadd(&request, vp);
500                 } /* else no memory! Die, die!: FIXME!! */
501         }
502
503         return ret;
504 }
505
506
507 /*
508  *      Compare the current time to a range.
509  *      Hmm... it would save work, and probably be better,
510  *      if we were passed the REQUEST data structure, so we
511  *      could use it's 'timestamp' element.  That way, we could
512  *      do the comparison against when the packet came in, not now,
513  *      and have one less system call to do.
514  */
515 static int timecmp(void *instance, REQUEST *req, VALUE_PAIR *request, VALUE_PAIR *check,
516         VALUE_PAIR *check_pairs, VALUE_PAIR **reply_pairs)
517 {
518         instance = instance;
519         request = request;      /* shut the compiler up */
520         check_pairs = check_pairs;
521         reply_pairs = reply_pairs;
522
523         if (timestr_match((char *)check->strvalue, time(NULL)) >= 0) {
524                 return 0;
525         }
526         return -1;
527 }
528
529 /*
530  *      Matches if there is NO SUCH ATTRIBUTE as the one named
531  *      in check->strvalue.  If there IS such an attribute, it
532  *      doesn't match.
533  *
534  *      This is ugly, and definitely non-optimal.  We should be
535  *      doing the lookup only ONCE, and storing the result
536  *      in check->lvalue...
537  */
538 static int attrcmp(void *instance, REQUEST *req, VALUE_PAIR *request, VALUE_PAIR *check,
539         VALUE_PAIR *check_pairs, VALUE_PAIR **reply_pairs)
540 {
541         VALUE_PAIR *pair;
542         DICT_ATTR  *dict;
543         int attr;
544
545         instance = instance;
546         check_pairs = check_pairs; /* shut the compiler up */
547         reply_pairs = reply_pairs;
548
549         if (check->lvalue == 0) {
550                 dict = dict_attrbyname((char *)check->strvalue);
551                 if (dict == NULL) {
552                         return -1;
553                 }
554                 attr = dict->attr;
555         } else {
556                 attr = check->lvalue;
557         }
558
559         /*
560          *      If there's no such attribute, then return MATCH,
561          *      else FAILURE.
562          */
563         pair = pairfind(request, attr);
564         if (pair == NULL) {
565                 return 0;
566         }
567
568         return -1;
569 }
570
571 /*
572  *      Compare the expiration date.
573  */
574 static int expirecmp(void *instance, REQUEST *req, VALUE_PAIR *request, VALUE_PAIR *check,
575                      VALUE_PAIR *check_pairs, VALUE_PAIR **reply_pairs)
576 {
577         time_t now;
578
579         instance = instance;
580         request = request;      /* shut the compiler up */
581         check_pairs = check_pairs;
582         reply_pairs = reply_pairs;
583
584         /*
585          *  FIXME!  This should be request->timestamp!
586          */
587         now = time(NULL);
588
589         if (now <= check->lvalue) {
590                 return 0;
591         }
592
593         return +1;
594 }
595
596 /*
597  *      Register server-builtin special attributes.
598  */
599 void pair_builtincompare_init(void)
600 {
601         paircompare_register(PW_NAS_PORT_ID, -1, portcmp, NULL);
602         paircompare_register(PW_PREFIX, PW_USER_NAME, presufcmp, NULL);
603         paircompare_register(PW_SUFFIX, PW_USER_NAME, presufcmp, NULL);
604         paircompare_register(PW_CONNECT_RATE, PW_CONNECT_INFO, connectcmp, NULL);
605         paircompare_register(PW_CURRENT_TIME, 0, timecmp, NULL);
606         paircompare_register(PW_NO_SUCH_ATTRIBUTE, 0, attrcmp, NULL);
607         paircompare_register(PW_EXPIRATION, 0, expirecmp, NULL);
608 }
609
610 /*
611  *      Move pairs, replacing/over-writing them, and doing xlat.
612  */
613 /*
614  *      Move attributes from one list to the other
615  *      if not already present.
616  */
617 void pairxlatmove(REQUEST *req, VALUE_PAIR **to, VALUE_PAIR **from)
618 {
619         VALUE_PAIR **tailto, *i, *j, *next;
620         VALUE_PAIR *tailfrom = NULL;
621         VALUE_PAIR *found;
622
623         /*
624          *      Point "tailto" to the end of the "to" list.
625          */
626         tailto = to;
627         for(i = *to; i; i = i->next) {
628                 tailto = &i->next;
629         }
630
631         /*
632          *      Loop over the "from" list.
633          */
634         for(i = *from; i; i = next) {
635                 next = i->next;
636
637                 /*
638                  *      Don't move 'fallthrough' over.
639                  */
640                 if (i->attribute == PW_FALL_THROUGH) {
641                         continue;
642                 }
643
644                 /*
645                  *      We've got to xlat the string before moving
646                  *      it over.
647                  */
648                 if (i->flags.do_xlat) {
649                         int rcode;
650                         char buffer[sizeof(i->strvalue)];
651
652                         i->flags.do_xlat = 0;
653                         rcode = radius_xlat(buffer, sizeof(buffer),
654                                             i->strvalue,
655                                             req, NULL);
656                         
657                         /*
658                          *      Parse the string into a new value.
659                          */
660                         pairparsevalue(i, buffer);
661                 }
662
663                 found = pairfind(*to, i->attribute);
664                 switch (i->operator) {
665                         
666                         /*
667                          *  If a similar attribute is found,
668                          *  delete it.
669                          */
670                 case T_OP_SUB:          /* -= */
671                         if (found) {
672                                 if (!i->strvalue[0] ||
673                                     (strcmp((char *)found->strvalue,
674                                             (char *)i->strvalue) == 0)){
675                                         pairdelete(to, found->attribute);
676                                         
677                                         /*
678                                          *      'tailto' may have been
679                                          *      deleted...
680                                          */
681                                         tailto = to;
682                                         for(j = *to; j; j = j->next) {
683                                                 tailto = &j->next;
684                                         }
685                                 }
686                         }
687                         tailfrom = i;
688                         continue;
689                         break;
690                         
691                         /*
692                          *  Add it, if it's not already there.
693                          */
694                 case T_OP_EQ:           /* = */
695                         if (found) {
696                                 tailfrom = i;
697                                 continue; /* with the loop */
698                         }
699                         break;
700                         
701                         /*
702                          *  If a similar attribute is found,
703                          *  replace it with the new one.  Otherwise,
704                          *  add the new one to the list.
705                          */
706                 case T_OP_SET:          /* := */
707                         if (found) {
708                                 VALUE_PAIR *vp;
709
710                                 vp = found->next;
711                                 memcpy(found, i, sizeof(*found));
712                                 found->next = vp;
713                         }
714                         break;
715                         
716                         /*
717                          *  FIXME: Add support for <=, >=, <, >
718                          *
719                          *  which will mean (for integers)
720                          *  'make the attribute the smaller, etc'
721                          */
722                         
723                         /*
724                          *  Add the new element to the list, even
725                          *  if similar ones already exist.
726                          */
727                 default:
728                 case T_OP_ADD:          /* += */
729                         break;
730                 }
731                 
732                 if (tailfrom)
733                         tailfrom->next = next;
734                 else
735                         *from = next;
736                 
737                 /*
738                  *      If ALL of the 'to' attributes have been deleted,
739                  *      then ensure that the 'tail' is updated to point
740                  *      to the head.
741                  */
742                 if (!*to) {
743                         tailto = to;
744                 }
745                 *tailto = i;
746                 if (i) {
747                         i->next = NULL;
748                         tailto = &i->next;
749                 }
750         } /* loop over the 'from' list */
751 }