Minor re-arrangements to Expiration. Attribute 21 is deprecated,
[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(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          *      See if there is a special compare function.
82          */
83         for (c = cmp; c; c = c->next)
84                 if (c->attribute == check->attribute)
85                         return (c->compare)(c->instance, request, check,
86                                 check_pairs, reply_pairs);
87
88         switch(check->type) {
89 #ifdef ASCEND_BINARY
90                 /*
91                  *      Ascend binary attributes can be treated
92                  *      as opaque objects, I guess...
93                  */
94                 case PW_TYPE_ABINARY:
95 #endif
96                 case PW_TYPE_OCTETS:
97                         if (request->length != check->length) {
98                                 ret = 1; /* NOT equal */
99                                 break;
100                         }
101                         ret = memcmp(request->strvalue, check->strvalue,
102                                         request->length);
103                         break;
104                 case PW_TYPE_STRING:
105                         ret = strcmp((char *)request->strvalue,
106                                         (char *)check->strvalue);
107                         break;
108                 case PW_TYPE_INTEGER:
109                 case PW_TYPE_DATE:
110                         ret = request->lvalue - check->lvalue;
111                         break;
112                 case PW_TYPE_IPADDR:
113                         ret = ntohl(request->lvalue) - ntohl(check->lvalue);
114                         break;
115                 default:
116                         break;
117         }
118
119         return ret;
120 }
121
122
123 /*
124  *      See what attribute we want to compare with.
125  */
126 static int otherattr(int attr)
127 {
128         struct cmp      *c;
129
130         for (c = cmp; c; c = c->next) {
131                 if (c->attribute == attr)
132                         return c->otherattr;
133         }
134
135         return attr;
136 }
137
138 /*
139  *      Register a function as compare function.
140  *      compare_attr is the attribute in the request we want to
141  *      compare with. Normally this is the same as "attr".
142  *      You can set this to:
143  *
144  *      -1   the same as "attr"
145  *      0    always call compare function, not tied to request attribute
146  *      >0   Attribute to compare with.
147  *
148  *      For example, PW_GROUP in a check item needs to be compared
149  *      with PW_USER_NAME in the incoming request.
150  */
151 int paircompare_register(int attr, int compare_attr, RAD_COMPARE_FUNC fun, void *instance)
152 {
153         struct cmp      *c;
154
155         paircompare_unregister(attr, fun);
156
157         c = rad_malloc(sizeof(struct cmp));
158
159         if (compare_attr < 0) 
160                 compare_attr = attr;
161         c->compare = fun;
162         c->attribute = attr;
163         c->otherattr = compare_attr;
164         c->instance = instance;
165         c->next = cmp;
166         cmp = c;
167
168         return 0;
169 }
170
171 /*
172  *      Unregister a function.
173  */
174 void paircompare_unregister(int attr, RAD_COMPARE_FUNC fun)
175 {
176         struct cmp      *c, *last;
177
178         last = NULL;
179         for (c = cmp; c; c = c->next) {
180                 if (c->attribute == attr && c->compare == fun)
181                         break;
182                 last = c;
183         }
184
185         if (c == NULL) return;
186
187         if (last != NULL)
188                 last->next = c->next;
189         else
190                 cmp = c->next;
191
192         free(c);
193 }
194
195 /*
196  *      Compare two pair lists except for the password information.
197  *      For every element in "check" at least one matching copy must
198  *      be present in "reply".
199  *
200  *      Return 0 on match.
201  */
202 int paircmp(VALUE_PAIR *request, VALUE_PAIR *check, VALUE_PAIR **reply)
203 {
204         VALUE_PAIR *check_item = check;
205         VALUE_PAIR *auth_item;
206         int result = 0;
207         int compare;
208         int other;
209 #ifdef HAVE_REGEX_H
210         regex_t reg;
211 #endif
212
213         while (result == 0 && check_item != NULL) {
214                 /*
215                  *      If the user is setting a configuration value,
216                  *      then don't bother comparing it to any attributes
217                  *      sent to us by the user.  It ALWAYS matches.
218                  */
219                 if ((check_item->operator == T_OP_SET) ||
220                     (check_item->operator == T_OP_ADD)) {
221                         check_item = check_item->next;
222                         continue;
223                 }
224
225                 switch (check_item->attribute) {
226                         /*
227                          *      Attributes we skip during comparison.
228                          *      These are "server" check items.
229                          */
230                         case PW_CRYPT_PASSWORD:
231                                 check_item = check_item->next;
232                                 continue;
233                                 break;
234
235                         /*
236                          *      IF the password attribute exists, THEN
237                          *      we can do comparisons against it.  If not,
238                          *      then the request did NOT contain a Password
239                          *      attribute, so we CANNOT do comparisons
240                          *      against it.
241                          *
242                          *      This hack makes CHAP-Password work..
243                          */
244                         case PW_PASSWORD:
245                                 if (pairfind(request, PW_PASSWORD) == NULL) {
246                                         check_item = check_item->next;
247                                         continue;
248                                 }
249                                 break;
250                 }
251
252                 /*
253                  *      See if this item is present in the request.
254                  */
255                 other = otherattr(check_item->attribute);
256                 auth_item = request;
257                 for (; auth_item != NULL; auth_item = auth_item->next) {
258                         if (auth_item->attribute == other || other == 0)
259                                 break;
260                 }
261
262                 if (auth_item == NULL) {
263                         result = -1;
264                         continue;
265                 }
266
267                 /*
268                  *      OK it is present now compare them.
269                  */
270                 compare = paircompare(auth_item, check_item, check, reply);
271
272                 switch (check_item->operator) {
273                         case T_OP_EQ:
274                         default:
275                                 radlog(L_ERR,  "Invalid operator for item %s: "
276                                                 "reverting to '=='", check_item->name);
277                                 /*FALLTHRU*/
278                         case T_OP_CMP_EQ:
279                                 if (compare != 0) return -1;
280                                 break;
281
282                         case T_OP_NE:
283                                 if (compare == 0) return -1;
284                                 break;
285
286                         case T_OP_LT:
287                                 if (compare >= 0) return -1;
288                                 break;
289
290                         case T_OP_GT:
291                                 if (compare <= 0) return -1;
292                                 break;
293                     
294                         case T_OP_LE:
295                                 if (compare > 0) return -1;
296                                 break;
297
298                         case T_OP_GE:
299                                 if (compare < 0) return -1;
300                                 break;
301
302 #ifdef HAVE_REGEX_H
303                         case T_OP_REG_EQ:
304                                 regcomp(&reg, (char *)check_item->strvalue, REG_EXTENDED);
305                                 compare = regexec(&reg, (char *)auth_item->strvalue,
306                                                 0, NULL, 0);
307                                 regfree(&reg);
308                                 if (compare != 0) return -1;
309                                 break;
310
311                         case T_OP_REG_NE:
312                                 regcomp(&reg, (char *)check_item->strvalue, REG_EXTENDED);
313                                 compare = regexec(&reg, (char *)auth_item->strvalue,
314                                                 0, NULL, 0);
315                                 regfree(&reg);
316                                 if (compare == 0) return -1;
317                                 break;
318 #endif
319
320                 }
321
322                 if (result == 0)
323                         check_item = check_item->next;
324         }
325
326         return result;
327 }
328
329 /*
330  *      Compare two attributes simply.  Calls paircompare.
331  */
332
333 int simplepaircmp(VALUE_PAIR *first, VALUE_PAIR *second)
334 {
335         return paircompare( first, second, NULL, NULL );
336 }
337
338
339 /*
340  *      Compare a Connect-Info and a Connect-Rate
341  */
342 static int connectcmp(void *instance, VALUE_PAIR *request, VALUE_PAIR *check,
343                 VALUE_PAIR *check_pairs, VALUE_PAIR **reply_pairs)
344 {
345         int rate;
346
347         instance = instance;
348         check_pairs = check_pairs; /* shut the compiler up */
349         reply_pairs = reply_pairs;
350
351         rate = atoi((char *)request->strvalue);
352         return rate - check->lvalue;
353 }
354
355
356 /*
357  *      Compare a portno with a range.
358  */
359 static int portcmp(void *instance, VALUE_PAIR *request, VALUE_PAIR *check,
360         VALUE_PAIR *check_pairs, VALUE_PAIR **reply_pairs)
361 {
362         char buf[MAX_STRING_LEN];
363         char *s, *p;
364         int lo, hi;
365         int port = request->lvalue;
366
367         instance = instance;
368         check_pairs = check_pairs; /* shut the compiler up */
369         reply_pairs = reply_pairs;
370
371         if ((strchr((char *)check->strvalue, ',') == NULL) &&
372                         (strchr((char *)check->strvalue, '-') == NULL)) {
373                 return (request->lvalue - check->lvalue);
374         }
375
376         /* Same size */
377         strcpy(buf, (char *)check->strvalue);
378         s = strtok(buf, ",");
379
380         while (s != NULL) {
381                 if ((p = strchr(s, '-')) != NULL)
382                         p++;
383                 else
384                         p = s;
385                 lo = atoi(s);
386                 hi = atoi(p);
387                 if (lo <= port && port <= hi) {
388                         return 0;
389                 }
390                 s = strtok(NULL, ",");
391         } 
392
393         return -1;
394 }
395
396 /*
397  *      Compare prefix/suffix.
398  *
399  *      If they compare: 
400  *      - if PW_STRIP_USER_NAME is present in check_pairs,
401  *        strip the username of prefix/suffix.
402  *      - if PW_STRIP_USER_NAME is not present in check_pairs,
403  *        add a PW_STRIPPED_USER_NAME to the request.
404  */
405 static int presufcmp(void *instance, VALUE_PAIR *request, VALUE_PAIR *check,
406         VALUE_PAIR *check_pairs, VALUE_PAIR **reply_pairs)
407 {
408         VALUE_PAIR *vp;
409         char *name = (char *)request->strvalue;
410         char rest[MAX_STRING_LEN];
411         int len, namelen;
412         int ret = -1;
413         
414         instance = instance;
415         reply_pairs = reply_pairs; /* shut the compiler up */
416
417 #if 0 /* DEBUG */
418         printf("Comparing %s and %s, check->attr is %d\n",
419                 name, check->strvalue, check->attribute);
420 #endif
421
422         len = strlen((char *)check->strvalue);
423         switch (check->attribute) {
424                 case PW_PREFIX:
425                         ret = strncmp(name, (char *)check->strvalue, len);
426                         if (ret == 0 && rest)
427                                 strcpy(rest, name + len);
428                         break;
429                 case PW_SUFFIX:
430                         namelen = strlen(name);
431                         if (namelen < len)
432                                 break;
433                         ret = strcmp(name + namelen - len,
434                                         (char *)check->strvalue);
435                         if (ret == 0 && rest) {
436                                 strncpy(rest, name, namelen - len);
437                                 rest[namelen - len] = 0;
438                         }
439                         break;
440         }
441         if (ret != 0)
442                 return ret;
443
444         if (pairfind(check_pairs, PW_STRIP_USER_NAME)) {
445                 /*
446                  *      I don't think we want to update the User-Name
447                  *      attribute in place... - atd
448                  */
449                 strcpy((char *)request->strvalue, rest);
450                 request->length = strlen(rest);
451         } else {
452                 if ((vp = pairfind(check_pairs, PW_STRIPPED_USER_NAME)) != NULL){
453                         strcpy((char *)vp->strvalue, rest);
454                         vp->length = strlen(rest);
455                 } else if ((vp = paircreate(PW_STRIPPED_USER_NAME,
456                                 PW_TYPE_STRING)) != NULL) {
457                         strcpy((char *)vp->strvalue, rest);
458                         vp->length = strlen(rest);
459                         pairadd(&request, vp);
460                 } /* else no memory! Die, die!: FIXME!! */
461         }
462
463         return ret;
464 }
465
466
467 /*
468  *      Compare the current time to a range.
469  *      Hmm... it would save work, and probably be better,
470  *      if we were passed the REQUEST data structure, so we
471  *      could use it's 'timestamp' element.  That way, we could
472  *      do the comparison against when the packet came in, not now,
473  *      and have one less system call to do.
474  */
475 static int timecmp(void *instance, VALUE_PAIR *request, VALUE_PAIR *check,
476         VALUE_PAIR *check_pairs, VALUE_PAIR **reply_pairs)
477 {
478         instance = instance;
479         request = request;      /* shut the compiler up */
480         check_pairs = check_pairs;
481         reply_pairs = reply_pairs;
482
483         if (timestr_match((char *)check->strvalue, time(NULL)) >= 0) {
484                 return 0;
485         }
486         return -1;
487 }
488
489 /*
490  *      Matches if there is NO SUCH ATTRIBUTE as the one named
491  *      in check->strvalue.  If there IS such an attribute, it
492  *      doesn't match.
493  *
494  *      This is ugly, and definitely non-optimal.  We should be
495  *      doing the lookup only ONCE, and storing the result
496  *      in check->lvalue...
497  */
498 static int attrcmp(void *instance, VALUE_PAIR *request, VALUE_PAIR *check,
499         VALUE_PAIR *check_pairs, VALUE_PAIR **reply_pairs)
500 {
501         VALUE_PAIR *pair;
502         DICT_ATTR  *dict;
503         int attr;
504
505         instance = instance;
506         check_pairs = check_pairs; /* shut the compiler up */
507         reply_pairs = reply_pairs;
508
509         if (check->lvalue == 0) {
510                 dict = dict_attrbyname((char *)check->strvalue);
511                 if (dict == NULL) {
512                         return -1;
513                 }
514                 attr = dict->attr;
515         } else {
516                 attr = check->lvalue;
517         }
518
519         /*
520          *      If there's no such attribute, then return MATCH,
521          *      else FAILURE.
522          */
523         pair = pairfind(request, attr);
524         if (pair == NULL) {
525                 return 0;
526         }
527
528         return -1;
529 }
530
531 /*
532  *      Compare the expiration date.
533  */
534 static int expirecmp(void *instance, VALUE_PAIR *request, VALUE_PAIR *check,
535                      VALUE_PAIR *check_pairs, VALUE_PAIR **reply_pairs)
536 {
537         time_t now;
538
539         instance = instance;
540         request = request;      /* shut the compiler up */
541         check_pairs = check_pairs;
542         reply_pairs = reply_pairs;
543
544         /*
545          *  FIXME!  This should be request->timestamp!
546          */
547         now = time(NULL);
548
549         if (now <= check->lvalue) {
550                 return 0;
551         }
552
553         return +1;
554 }
555
556 /*
557  *      Register server-builtin special attributes.
558  */
559 void pair_builtincompare_init(void)
560 {
561         paircompare_register(PW_NAS_PORT_ID, -1, portcmp, NULL);
562         paircompare_register(PW_PREFIX, PW_USER_NAME, presufcmp, NULL);
563         paircompare_register(PW_SUFFIX, PW_USER_NAME, presufcmp, NULL);
564         paircompare_register(PW_CONNECT_RATE, PW_CONNECT_INFO, connectcmp, NULL);
565         paircompare_register(PW_CURRENT_TIME, 0, timecmp, NULL);
566         paircompare_register(PW_NO_SUCH_ATTRIBUTE, 0, attrcmp, NULL);
567         paircompare_register(PW_EXPIRATION, 0, expirecmp, NULL);
568 }