Added new function: 'pairxlatmove', which works like pairmove(),
[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                                 continue;
239                                 break;
240
241                         /*
242                          *      IF the password attribute exists, THEN
243                          *      we can do comparisons against it.  If not,
244                          *      then the request did NOT contain a
245                          *      User-Password attribute, so we CANNOT do
246                          *      comparisons against it.
247                          *
248                          *      This hack makes CHAP-Password work..
249                          */
250                         case PW_PASSWORD:
251                                 if (pairfind(request, PW_PASSWORD) == NULL) {
252                                         continue;
253                                 }
254                                 break;
255                 }
256
257                 /*
258                  *      See if this item is present in the request.
259                  */
260                 other = otherattr(check_item->attribute);
261                 auth_item = request;
262         try_again:
263                 for (; auth_item != NULL; auth_item = auth_item->next) {
264                         if (auth_item->attribute == other || other == 0)
265                                 break;
266                 }
267
268                 /*
269                  *      Not found, it's not a match.
270                  */
271                 if (auth_item == NULL) {
272                         return -1;
273                 }
274
275                 /*
276                  *      We've got to xlat the string before doing
277                  *      the comparison.
278                  */
279                 if (check_item->flags.do_xlat) {
280                         int rcode;
281                         char buffer[sizeof(check_item->strvalue)];
282
283                         check_item->flags.do_xlat = 0;
284                         rcode = radius_xlat(buffer, sizeof(buffer),
285                                             check_item->strvalue,
286                                             req, NULL);
287                         
288                         /*
289                          *      Parse the string into a new value.
290                          */
291                         pairparsevalue(check_item, buffer);
292                 }
293
294                 /*
295                  *      OK it is present now compare them.
296                  */
297                 compare = paircompare(req, auth_item, check_item, check, reply);
298
299                 switch (check_item->operator) {
300                         case T_OP_EQ:
301                         default:
302                                 radlog(L_ERR,  "Invalid operator for item %s: "
303                                                 "reverting to '=='", check_item->name);
304                                 /*FALLTHRU*/
305                         case T_OP_CMP_TRUE:    /* compare always == 0 */
306                         case T_OP_CMP_FALSE:   /* compare always == 1 */
307                         case T_OP_CMP_EQ:
308                                 if (compare != 0) result = -1;
309                                 break;
310
311                         case T_OP_NE:
312                                 if (compare == 0) result = -1;
313                                 break;
314
315                         case T_OP_LT:
316                                 if (compare >= 0) result = -1;
317                                 break;
318
319                         case T_OP_GT:
320                                 if (compare <= 0) result = -1;
321                                 break;
322                     
323                         case T_OP_LE:
324                                 if (compare > 0) result = -1;
325                                 break;
326
327                         case T_OP_GE:
328                                 if (compare < 0) result = -1;
329                                 break;
330
331 #ifdef HAVE_REGEX_H
332                         case T_OP_REG_EQ:
333                                 regcomp(&reg, (char *)check_item->strvalue, REG_EXTENDED);
334                                 compare = regexec(&reg, (char *)auth_item->strvalue,
335                                                 0, NULL, 0);
336                                 regfree(&reg);
337                                 if (compare != 0) result = -1;
338                                 break;
339
340                         case T_OP_REG_NE:
341                                 regcomp(&reg, (char *)check_item->strvalue, REG_EXTENDED);
342                                 compare = regexec(&reg, (char *)auth_item->strvalue,
343                                                 0, NULL, 0);
344                                 regfree(&reg);
345                                 if (compare == 0) result = -1;
346                                 break;
347 #endif
348
349                 } /* switch over the operator of the check item */
350
351                 /*
352                  *      This attribute didn't match, but maybe there's
353                  *      another of the same attribute, which DOES match.
354                  */
355                 if (result != 0) {
356                         auth_item = auth_item->next;
357                         result = 0;
358                         goto try_again;
359                 }
360
361         } /* for every entry in the check item list */
362
363         return 0;               /* it matched */
364 }
365
366 /*
367  *      Compare two attributes simply.  Calls paircompare.
368  */
369
370 int simplepaircmp(REQUEST *req, VALUE_PAIR *first, VALUE_PAIR *second)
371 {
372         return paircompare( req, first, second, NULL, NULL );
373 }
374
375
376 /*
377  *      Compare a Connect-Info and a Connect-Rate
378  */
379 static int connectcmp(void *instance, REQUEST *req, VALUE_PAIR *request, VALUE_PAIR *check,
380                 VALUE_PAIR *check_pairs, VALUE_PAIR **reply_pairs)
381 {
382         int rate;
383
384         instance = instance;
385         check_pairs = check_pairs; /* shut the compiler up */
386         reply_pairs = reply_pairs;
387
388         rate = atoi((char *)request->strvalue);
389         return rate - check->lvalue;
390 }
391
392
393 /*
394  *      Compare a portno with a range.
395  */
396 static int portcmp(void *instance, REQUEST *req, VALUE_PAIR *request, VALUE_PAIR *check,
397         VALUE_PAIR *check_pairs, VALUE_PAIR **reply_pairs)
398 {
399         char buf[MAX_STRING_LEN];
400         char *s, *p;
401         uint32_t lo, hi;
402         uint32_t port = request->lvalue;
403
404         instance = instance;
405         check_pairs = check_pairs; /* shut the compiler up */
406         reply_pairs = reply_pairs;
407
408         if ((strchr((char *)check->strvalue, ',') == NULL) &&
409                         (strchr((char *)check->strvalue, '-') == NULL)) {
410                 return (request->lvalue - check->lvalue);
411         }
412
413         /* Same size */
414         strcpy(buf, (char *)check->strvalue);
415         s = strtok(buf, ",");
416
417         while (s != NULL) {
418                 if ((p = strchr(s, '-')) != NULL)
419                         p++;
420                 else
421                         p = s;
422                 lo = strtoul(s, NULL, 10);
423                 hi = strtoul(p, NULL, 10);
424                 if (lo <= port && port <= hi) {
425                         return 0;
426                 }
427                 s = strtok(NULL, ",");
428         } 
429
430         return -1;
431 }
432
433 /*
434  *      Compare prefix/suffix.
435  *
436  *      If they compare: 
437  *      - if PW_STRIP_USER_NAME is present in check_pairs,
438  *        strip the username of prefix/suffix.
439  *      - if PW_STRIP_USER_NAME is not present in check_pairs,
440  *        add a PW_STRIPPED_USER_NAME to the request.
441  */
442 static int presufcmp(void *instance, REQUEST *req, VALUE_PAIR *request, VALUE_PAIR *check,
443         VALUE_PAIR *check_pairs, VALUE_PAIR **reply_pairs)
444 {
445         VALUE_PAIR *vp;
446         char *name = (char *)request->strvalue;
447         char rest[MAX_STRING_LEN];
448         int len, namelen;
449         int ret = -1;
450         
451         instance = instance;
452         reply_pairs = reply_pairs; /* shut the compiler up */
453
454 #if 0 /* DEBUG */
455         printf("Comparing %s and %s, check->attr is %d\n",
456                 name, check->strvalue, check->attribute);
457 #endif
458
459         len = strlen((char *)check->strvalue);
460         switch (check->attribute) {
461                 case PW_PREFIX:
462                         ret = strncmp(name, (char *)check->strvalue, len);
463                         if (ret == 0 && rest)
464                                 strcpy(rest, name + len);
465                         break;
466                 case PW_SUFFIX:
467                         namelen = strlen(name);
468                         if (namelen < len)
469                                 break;
470                         ret = strcmp(name + namelen - len,
471                                         (char *)check->strvalue);
472                         if (ret == 0 && rest) {
473                                 strncpy(rest, name, namelen - len);
474                                 rest[namelen - len] = 0;
475                         }
476                         break;
477         }
478         if (ret != 0)
479                 return ret;
480
481         if (pairfind(check_pairs, PW_STRIP_USER_NAME)) {
482                 /*
483                  *      I don't think we want to update the User-Name
484                  *      attribute in place... - atd
485                  */
486                 strcpy((char *)request->strvalue, rest);
487                 request->length = strlen(rest);
488         } else {
489                 if ((vp = pairfind(check_pairs, PW_STRIPPED_USER_NAME)) != NULL){
490                         strcpy((char *)vp->strvalue, rest);
491                         vp->length = strlen(rest);
492                 } else if ((vp = paircreate(PW_STRIPPED_USER_NAME,
493                                 PW_TYPE_STRING)) != NULL) {
494                         strcpy((char *)vp->strvalue, rest);
495                         vp->length = strlen(rest);
496                         pairadd(&request, vp);
497                 } /* else no memory! Die, die!: FIXME!! */
498         }
499
500         return ret;
501 }
502
503
504 /*
505  *      Compare the current time to a range.
506  *      Hmm... it would save work, and probably be better,
507  *      if we were passed the REQUEST data structure, so we
508  *      could use it's 'timestamp' element.  That way, we could
509  *      do the comparison against when the packet came in, not now,
510  *      and have one less system call to do.
511  */
512 static int timecmp(void *instance, REQUEST *req, VALUE_PAIR *request, VALUE_PAIR *check,
513         VALUE_PAIR *check_pairs, VALUE_PAIR **reply_pairs)
514 {
515         instance = instance;
516         request = request;      /* shut the compiler up */
517         check_pairs = check_pairs;
518         reply_pairs = reply_pairs;
519
520         if (timestr_match((char *)check->strvalue, time(NULL)) >= 0) {
521                 return 0;
522         }
523         return -1;
524 }
525
526 /*
527  *      Matches if there is NO SUCH ATTRIBUTE as the one named
528  *      in check->strvalue.  If there IS such an attribute, it
529  *      doesn't match.
530  *
531  *      This is ugly, and definitely non-optimal.  We should be
532  *      doing the lookup only ONCE, and storing the result
533  *      in check->lvalue...
534  */
535 static int attrcmp(void *instance, REQUEST *req, VALUE_PAIR *request, VALUE_PAIR *check,
536         VALUE_PAIR *check_pairs, VALUE_PAIR **reply_pairs)
537 {
538         VALUE_PAIR *pair;
539         DICT_ATTR  *dict;
540         int attr;
541
542         instance = instance;
543         check_pairs = check_pairs; /* shut the compiler up */
544         reply_pairs = reply_pairs;
545
546         if (check->lvalue == 0) {
547                 dict = dict_attrbyname((char *)check->strvalue);
548                 if (dict == NULL) {
549                         return -1;
550                 }
551                 attr = dict->attr;
552         } else {
553                 attr = check->lvalue;
554         }
555
556         /*
557          *      If there's no such attribute, then return MATCH,
558          *      else FAILURE.
559          */
560         pair = pairfind(request, attr);
561         if (pair == NULL) {
562                 return 0;
563         }
564
565         return -1;
566 }
567
568 /*
569  *      Compare the expiration date.
570  */
571 static int expirecmp(void *instance, REQUEST *req, VALUE_PAIR *request, VALUE_PAIR *check,
572                      VALUE_PAIR *check_pairs, VALUE_PAIR **reply_pairs)
573 {
574         time_t now;
575
576         instance = instance;
577         request = request;      /* shut the compiler up */
578         check_pairs = check_pairs;
579         reply_pairs = reply_pairs;
580
581         /*
582          *  FIXME!  This should be request->timestamp!
583          */
584         now = time(NULL);
585
586         if (now <= check->lvalue) {
587                 return 0;
588         }
589
590         return +1;
591 }
592
593 /*
594  *      Register server-builtin special attributes.
595  */
596 void pair_builtincompare_init(void)
597 {
598         paircompare_register(PW_NAS_PORT_ID, -1, portcmp, NULL);
599         paircompare_register(PW_PREFIX, PW_USER_NAME, presufcmp, NULL);
600         paircompare_register(PW_SUFFIX, PW_USER_NAME, presufcmp, NULL);
601         paircompare_register(PW_CONNECT_RATE, PW_CONNECT_INFO, connectcmp, NULL);
602         paircompare_register(PW_CURRENT_TIME, 0, timecmp, NULL);
603         paircompare_register(PW_NO_SUCH_ATTRIBUTE, 0, attrcmp, NULL);
604         paircompare_register(PW_EXPIRATION, 0, expirecmp, NULL);
605 }
606
607 /*
608  *      Move pairs, replacing/over-writing them, and doing xlat.
609  */
610 /*
611  *      Move attributes from one list to the other
612  *      if not already present.
613  */
614 void pairxlatmove(REQUEST *req, VALUE_PAIR **to, VALUE_PAIR **from)
615 {
616         VALUE_PAIR **tailto, *i, *j, *next;
617         VALUE_PAIR *tailfrom = NULL;
618         VALUE_PAIR *found, *prev;
619
620         /*
621          *      Point "tailto" to the end of the "to" list.
622          */
623         tailto = to;
624         for(i = *to; i; i = i->next) {
625                 tailto = &i->next;
626         }
627
628         /*
629          *      Loop over the "from" list.
630          */
631         for(i = *from; i; i = next) {
632                 next = i->next;
633
634                 /*
635                  *      Don't move 'fallthrough' over.
636                  */
637                 if (i->attribute == PW_FALL_THROUGH) {
638                         continue;
639                 }
640
641                 /*
642                  *      We've got to xlat the string before moving
643                  *      it over.
644                  */
645                 if (i->flags.do_xlat) {
646                         int rcode;
647                         char buffer[sizeof(i->strvalue)];
648
649                         i->flags.do_xlat = 0;
650                         rcode = radius_xlat(buffer, sizeof(buffer),
651                                             i->strvalue,
652                                             req, NULL);
653                         
654                         /*
655                          *      Parse the string into a new value.
656                          */
657                         pairparsevalue(i, buffer);
658                 }
659
660                 found = pairfind(*to, i->attribute);
661                 switch (i->operator) {
662                         
663                         /*
664                          *  If a similar attribute is found,
665                          *  delete it.
666                          */
667                 case T_OP_SUB:          /* -= */
668                         if (found) {
669                                 if (!i->strvalue[0] ||
670                                     (strcmp((char *)found->strvalue,
671                                             (char *)i->strvalue) == 0)){
672                                         pairdelete(to, found->attribute);
673                                         
674                                         /*
675                                          *      'tailto' may have been
676                                          *      deleted...
677                                          */
678                                         tailto = to;
679                                         for(j = *to; j; j = j->next) {
680                                                 tailto = &j->next;
681                                         }
682                                 }
683                         }
684                         tailfrom = i;
685                         continue;
686                         break;
687                         
688                         /*
689                          *  Add it, if it's not already there.
690                          */
691                 case T_OP_EQ:           /* = */
692                         if (found) {
693                                 tailfrom = i;
694                                 continue; /* with the loop */
695                         }
696                         break;
697                         
698                         /*
699                          *  If a similar attribute is found,
700                          *  replace it with the new one.  Otherwise,
701                          *  add the new one to the list.
702                          */
703                 case T_OP_SET:          /* := */
704                         if (found) {
705
706
707                                 /*
708                                  *  FIXME: REPLACE the attribute,
709                                  *  instead of moving it somewhere
710                                  *  else!
711                                  */
712                                 pairdelete(to, found->attribute);
713                                 /*
714                                  *      'tailto' may have been
715                                  *      deleted...
716                                  */
717                                 tailto = to;
718                                 for(j = *to; j; j = j->next) {
719                                         tailto = &j->next;
720                                 }
721                         }
722                         break;
723                         
724                         /*
725                          *  FIXME: Add support for <=, >=, <, >
726                          *
727                          *  which will mean (for integers)
728                          *  'make the attribute the smaller, etc'
729                          */
730                         
731                         /*
732                          *  Add the new element to the list, even
733                          *  if similar ones already exist.
734                          */
735                 default:
736                 case T_OP_ADD:          /* += */
737                         break;
738                 }
739                 
740                 if (tailfrom)
741                         tailfrom->next = next;
742                 else
743                         *from = next;
744                 
745                 /*
746                  *      If ALL of the 'to' attributes have been deleted,
747                  *      then ensure that the 'tail' is updated to point
748                  *      to the head.
749                  */
750                 if (!*to) {
751                         tailto = to;
752                 }
753                 *tailto = i;
754                 if (i) {
755                         i->next = NULL;
756                         tailto = &i->next;
757                 }
758         } /* loop over the 'from' list */
759 }