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