Added new valuepair function 'simplepaircmp()' to compare only two
[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) -
105                                ntohl(check->lvalue);
106                         break;
107                 default:
108                         break;
109         }
110
111         return ret;
112 }
113
114
115 /*
116  *      See what attribute we want to compare with.
117  */
118 static int otherattr(int attr)
119 {
120         struct cmp      *c;
121
122         for (c = cmp; c; c = c->next) {
123                 if (c->attribute == attr)
124                         return c->otherattr;
125         }
126
127         return attr;
128 }
129
130 /*
131  *      Register a function as compare function.
132  *      compare_attr is the attribute in the request we want to
133  *      compare with. Normally this is the same as "attr".
134  *      You can set this to:
135  *
136  *      -1   the same as "attr"
137  *      0    always call compare function, not tied to request attribute
138  *      >0   Attribute to compare with.
139  *
140  *      For example, PW_GROUP in a check item needs to be compared
141  *      with PW_USER_NAME in the incoming request.
142  */
143 int paircompare_register(int attr, int compare_attr, RAD_COMPARE_FUNC fun, void *instance)
144 {
145         struct cmp      *c;
146
147         paircompare_unregister(attr, fun);
148
149         c = rad_malloc(sizeof(struct cmp));
150
151         if (compare_attr < 0) 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)
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_PASSWORD:
222                         case PW_CRYPT_PASSWORD:
223                                 check_item = check_item->next;
224                                 continue;
225                 }
226                 /*
227                  *      See if this item is present in the request.
228                  */
229                 other = otherattr(check_item->attribute);
230                 auth_item = request;
231                 for (; auth_item != NULL; auth_item = auth_item->next) {
232                         if (auth_item->attribute == other || other == 0)
233                                 break;
234                 }
235
236                 if (auth_item == NULL) {
237                         result = -1;
238                         continue;
239                 }
240
241                 /*
242                  *      OK it is present now compare them.
243                  */
244                 compare = paircompare(auth_item, check_item, check, reply);
245
246                 switch (check_item->operator)
247                   {
248                   case T_OP_EQ:
249                   default:
250                     radlog(L_ERR,  "Invalid operator for item %s: "
251                                 "reverting to '=='", check_item->name);
252                     /*FALLTHRU*/
253                   case T_OP_CMP_EQ:
254                     if (compare != 0) return -1;
255                     break;
256
257                   case T_OP_NE:
258                     if (compare == 0) return -1;
259                     break;
260
261                   case T_OP_LT:
262                     if (compare >= 0) return -1;
263                     break;
264
265                   case T_OP_GT:
266                     if (compare <= 0) return -1;
267                     break;
268                     
269                   case T_OP_LE:
270                     if (compare > 0) return -1;
271                     break;
272
273                   case T_OP_GE:
274                     if (compare < 0) return -1;
275                     break;
276
277 #ifdef HAVE_REGEX_H
278                   case T_OP_REG_EQ:
279                     regcomp(&reg, (char *)check_item->strvalue, 0);
280                     compare = regexec(&reg, (char *)auth_item->strvalue,
281                                       0, NULL, 0);
282                     regfree(&reg);
283                     if (compare != 0) return -1;
284                     break;
285
286                   case T_OP_REG_NE:
287                     regcomp(&reg, (char *)check_item->strvalue, 0);
288                     compare = regexec(&reg, (char *)auth_item->strvalue,
289                                       0, NULL, 0);
290                     regfree(&reg);
291                     if (compare == 0) return -1;
292                     break;
293 #endif
294
295                   }
296
297                 if (result == 0)
298                         check_item = check_item->next;
299         }
300
301         return result;
302 }
303
304 /*
305  *      Compare two attributes simply.  Calls paircompare.
306  */
307
308 int simplepaircmp(VALUE_PAIR *first, VALUE_PAIR *second)
309 {
310
311     return paircompare( first, second, NULL, NULL );
312
313 }
314
315
316 /*
317  *      Compare a Connect-Info and a Connect-Rate
318  */
319 static int connectcmp(void *instance, VALUE_PAIR *request, VALUE_PAIR *check,
320         VALUE_PAIR *check_pairs, VALUE_PAIR **reply_pairs)
321 {
322         int     rate;
323
324         instance = instance;
325         check_pairs = check_pairs; /* shut the compiler up */
326         reply_pairs = reply_pairs;
327
328         rate = atoi((char *)request->strvalue);
329         return rate - check->lvalue;
330 }
331
332
333 /*
334  *      Compare a portno with a range.
335  */
336 static int portcmp(void *instance, VALUE_PAIR *request, VALUE_PAIR *check,
337         VALUE_PAIR *check_pairs, VALUE_PAIR **reply_pairs)
338 {
339         char            buf[MAX_STRING_LEN];
340         char            *s, *p;
341         int             lo, hi;
342         int             port = request->lvalue;
343
344         instance = instance;
345         check_pairs = check_pairs; /* shut the compiler up */
346         reply_pairs = reply_pairs;
347
348         if ((strchr((char *)check->strvalue, ',') == NULL) &&
349             (strchr((char *)check->strvalue, '-') == NULL)) {
350                 return (request->lvalue - check->lvalue);
351         }
352
353         /* Same size */
354         strcpy(buf, (char *)check->strvalue);
355         s = strtok(buf, ",");
356
357         while (s) {
358                 if ((p = strchr(s, '-')) != NULL)
359                         p++;
360                 else
361                         p = s;
362                 lo = atoi(s);
363                 hi = atoi(p);
364                 if (lo <= port && port <= hi) {
365                         return 0;
366                 }
367                 s = strtok(NULL, ",");
368         } 
369
370         return -1;
371 }
372
373 /*
374  *      Compare prefix/suffix.
375  *
376  *      If they compare: 
377  *      - if PW_STRIP_USER_NAME is present in check_pairs,
378  *        strip the username of prefix/suffix.
379  *      - if PW_STRIP_USER_NAME is not present in check_pairs,
380  *        add a PW_STRIPPED_USER_NAME to the request.
381  */
382 static int presufcmp(void *instance, VALUE_PAIR *request, VALUE_PAIR *check,
383         VALUE_PAIR *check_pairs, VALUE_PAIR **reply_pairs)
384 {
385         VALUE_PAIR      *vp;
386         char            *name = (char *)request->strvalue;
387         char            rest[MAX_STRING_LEN];
388         int             len, namelen;
389         int             ret = -1;
390         
391         instance = instance;
392         reply_pairs = reply_pairs; /* shut the compiler up */
393
394 #if 0 /* DEBUG */
395         printf("Comparing %s and %s, check->attr is %d\n",
396                 name, check->strvalue, check->attribute);
397 #endif
398
399         len = strlen((char *)check->strvalue);
400         switch (check->attribute) {
401                 case PW_PREFIX:
402                         ret = strncmp(name, (char *)check->strvalue, len);
403                         if (ret == 0 && rest)
404                                 strcpy(rest, name + len);
405                         break;
406                 case PW_SUFFIX:
407                         namelen = strlen(name);
408                         if (namelen < len)
409                                 break;
410                         ret = strcmp(name + namelen - len,
411                                      (char *)check->strvalue);
412                         if (ret == 0 && rest) {
413                                 strncpy(rest, name, namelen - len);
414                                 rest[namelen - len] = 0;
415                         }
416                         break;
417         }
418         if (ret != 0)
419                 return ret;
420
421         if (pairfind(check_pairs, PW_STRIP_USER_NAME)) {
422                 /*
423                  *      I don't think we want to update the User-Name
424                  *      attribute in place... - atd
425                  */
426                 strcpy((char *)request->strvalue, rest);
427                 request->length = strlen(rest);
428         } else {
429                 if ((vp = pairfind(check_pairs, PW_STRIPPED_USER_NAME)) != NULL){
430                         strcpy((char *)vp->strvalue, rest);
431                         vp->length = strlen(rest);
432                 } else if ((vp = paircreate(PW_STRIPPED_USER_NAME,
433                             PW_TYPE_STRING)) != NULL) {
434                         strcpy((char *)vp->strvalue, rest);
435                         vp->length = strlen(rest);
436                         pairadd(&request, vp);
437                 } /* else no memory! Die, die!: FIXME!! */
438         }
439
440         return ret;
441 }
442
443
444 /*
445  *      Compare the current time to a range.
446  *      Hmm... it would save work, and probably be better,
447  *      if we were passed the REQUEST data structure, so we
448  *      could use it's 'timestamp' element.  That way, we could
449  *      do the comparison against when the packet came in, not now,
450  *      and have one less system call to do.
451  */
452 static int timecmp(void *instance, VALUE_PAIR *request, VALUE_PAIR *check,
453         VALUE_PAIR *check_pairs, VALUE_PAIR **reply_pairs)
454 {
455         instance = instance;
456         request = request;      /* shut the compiler up */
457         check_pairs = check_pairs;
458         reply_pairs = reply_pairs;
459
460         if (timestr_match((char *)check->strvalue, time(NULL)) >= 0) {
461                 return 0;
462         }
463         return -1;
464 }
465
466 /*
467  *      Matches if there is NO SUCH ATTRIBUTE as the one named
468  *      in check->strvalue.  If there IS such an attribute, it
469  *      doesn't match.
470  *
471  *      This is ugly, and definitely non-optimal.  We should be
472  *      doing the lookup only ONCE, and storing the result
473  *      in check->lvalue...
474  */
475 static int attrcmp(void *instance, VALUE_PAIR *request, VALUE_PAIR *check,
476         VALUE_PAIR *check_pairs, VALUE_PAIR **reply_pairs)
477 {
478         VALUE_PAIR *pair;
479         DICT_ATTR  *dict;
480         int attr;
481
482         instance = instance;
483         check_pairs = check_pairs; /* shut the compiler up */
484         reply_pairs = reply_pairs;
485
486         if (check->lvalue == 0) {
487                 dict = dict_attrbyname((char *)check->strvalue);
488                 if (!dict) {
489                         return -1;
490                 }
491                 attr = dict->attr;
492         } else {
493                 attr = check->lvalue;
494         }
495
496         /*
497          *      If there's no such attribute, then return MATCH,
498          *      else FAILURE.
499          */
500         pair = pairfind(request, attr);
501         if (!pair) {
502                 return 0;
503         }
504
505         return -1;
506 }
507
508 /*
509  *      Register server-builtin special attributes.
510  */
511 void pair_builtincompare_init(void)
512 {
513         paircompare_register(PW_NAS_PORT_ID, -1, portcmp, NULL);
514         paircompare_register(PW_PREFIX, PW_USER_NAME, presufcmp, NULL);
515         paircompare_register(PW_SUFFIX, PW_USER_NAME, presufcmp, NULL);
516         paircompare_register(PW_CONNECT_RATE, PW_CONNECT_INFO, connectcmp, NULL);
517         paircompare_register(PW_CURRENT_TIME, 0, timecmp, NULL);
518         paircompare_register(PW_NO_SUCH_ATTRIBUTE, 0, attrcmp, NULL);
519 }