Don't call inet_ntop() for IP addresses by default. It's not
[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
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32
33 #ifdef HAVE_NETINET_IN_H
34 #       include <netinet/in.h>
35 #endif
36
37 #ifdef HAVE_REGEX_H
38 #       include <regex.h>
39
40 /*
41  *  For POSIX Regular expressions.
42  *  (0) Means no extended regular expressions.
43  *  REG_EXTENDED means use extended regular expressions.
44  */
45 #ifndef REG_EXTENDED
46 #define REG_EXTENDED (0)
47 #endif
48
49 #ifndef REG_NOSUB
50 #define REG_NOSUB (0)
51 #endif
52 #endif
53
54 #include "radiusd.h"
55
56 struct cmp {
57         int attribute;
58         int otherattr;
59         void *instance; /* module instance */
60         RAD_COMPARE_FUNC compare;
61         struct cmp *next;
62 };
63 static struct cmp *cmp;
64
65
66 /*
67  *      Compare 2 attributes. May call the attribute compare function.
68  */
69 static int paircompare(REQUEST *req, VALUE_PAIR *request, VALUE_PAIR *check,
70                        VALUE_PAIR *check_pairs, VALUE_PAIR **reply_pairs)
71 {
72         int ret = -2;
73         struct cmp *c;
74
75         /*
76          *      Sanity check.
77          */
78 #if 0
79         if (request->attribute != check->attribute)
80                 return -2;
81 #endif
82
83         /*
84          *      Check for =* and !* and return appropriately
85          */
86         if( check->operator == T_OP_CMP_TRUE )
87                  return 0;  /* always return 0/EQUAL */
88         if( check->operator == T_OP_CMP_FALSE )
89                  return 1;  /* always return 1/NOT EQUAL */
90
91         /*
92          *      See if there is a special compare function.
93          *
94          *      FIXME: use new RB-Tree code.
95          */
96         for (c = cmp; c; c = c->next)
97                 if (c->attribute == check->attribute)
98                         return (c->compare)(c->instance, req, request, check,
99                                 check_pairs, reply_pairs);
100
101         if (!request) return -1; /* doesn't exist, don't compare it */
102
103         switch(check->type) {
104 #ifdef ASCEND_BINARY
105                 /*
106                  *      Ascend binary attributes can be treated
107                  *      as opaque objects, I guess...
108                  */
109                 case PW_TYPE_ABINARY:
110 #endif
111                 case PW_TYPE_OCTETS:
112                         if (request->length != check->length) {
113                                 ret = 1; /* NOT equal */
114                                 break;
115                         }
116                         ret = memcmp(request->vp_strvalue, check->vp_strvalue,
117                                         request->length);
118                         break;
119                 case PW_TYPE_STRING:
120                         if (check->flags.caseless) {
121                                 ret = strcasecmp((char *)request->vp_strvalue,
122                                                  (char *)check->vp_strvalue);
123                         } else {
124                                 ret = strcmp((char *)request->vp_strvalue,
125                                              (char *)check->vp_strvalue);
126                         }
127                         break;
128                 case PW_TYPE_INTEGER:
129                 case PW_TYPE_DATE:
130                         ret = request->lvalue - check->lvalue;
131                         break;
132                 case PW_TYPE_IPADDR:
133                         ret = ntohl(request->lvalue) - ntohl(check->lvalue);
134                         break;
135                 default:
136                         break;
137         }
138
139         return ret;
140 }
141
142
143 /*
144  *      See what attribute we want to compare with.
145  */
146 static int otherattr(int attr)
147 {
148         struct cmp      *c;
149
150         for (c = cmp; c; c = c->next) {
151                 if (c->attribute == attr)
152                         return c->otherattr;
153         }
154
155         return attr;
156 }
157
158 /*
159  *      Register a function as compare function.
160  *      compare_attr is the attribute in the request we want to
161  *      compare with. Normally this is the same as "attr".
162  *      You can set this to:
163  *
164  *      -1   the same as "attr"
165  *      0    always call compare function, not tied to request attribute
166  *      >0   Attribute to compare with.
167  *
168  *      For example, PW_GROUP in a check item needs to be compared
169  *      with PW_USER_NAME in the incoming request.
170  */
171 int paircompare_register(int attr, int compare_attr, RAD_COMPARE_FUNC fun, void *instance)
172 {
173         struct cmp      *c;
174
175         paircompare_unregister(attr, fun);
176
177         c = rad_malloc(sizeof(struct cmp));
178
179         if (compare_attr < 0)
180                 compare_attr = attr;
181         c->compare = fun;
182         c->attribute = attr;
183         c->otherattr = compare_attr;
184         c->instance = instance;
185         c->next = cmp;
186         cmp = c;
187
188         return 0;
189 }
190
191 /*
192  *      Unregister a function.
193  */
194 void paircompare_unregister(int attr, RAD_COMPARE_FUNC fun)
195 {
196         struct cmp      *c, *last;
197
198         last = NULL;
199         for (c = cmp; c; c = c->next) {
200                 if (c->attribute == attr && c->compare == fun)
201                         break;
202                 last = c;
203         }
204
205         if (c == NULL) return;
206
207         if (last != NULL)
208                 last->next = c->next;
209         else
210                 cmp = c->next;
211
212         free(c);
213 }
214
215 /*
216  *      Compare two pair lists except for the password information.
217  *      For every element in "check" at least one matching copy must
218  *      be present in "reply".
219  *
220  *      Return 0 on match.
221  */
222 int paircmp(REQUEST *req, VALUE_PAIR *request, VALUE_PAIR *check, VALUE_PAIR **reply)
223 {
224         VALUE_PAIR *check_item;
225         VALUE_PAIR *auth_item;
226         int result = 0;
227         int compare;
228         int other;
229 #ifdef HAVE_REGEX_H
230         regex_t reg;
231 #endif
232
233         for (check_item = check; check_item != NULL; check_item = check_item->next) {
234                 /*
235                  *      If the user is setting a configuration value,
236                  *      then don't bother comparing it to any attributes
237                  *      sent to us by the user.  It ALWAYS matches.
238                  */
239                 if ((check_item->operator == T_OP_SET) ||
240                     (check_item->operator == T_OP_ADD)) {
241                         continue;
242                 }
243
244                 switch (check_item->attribute) {
245                         /*
246                          *      Attributes we skip during comparison.
247                          *      These are "server" check items.
248                          */
249                         case PW_CRYPT_PASSWORD:
250                         case PW_AUTH_TYPE:
251                         case PW_AUTZ_TYPE:
252                         case PW_ACCT_TYPE:
253                         case PW_SESSION_TYPE:
254                         case PW_STRIP_USER_NAME:
255                                 continue;
256                                 break;
257
258                         /*
259                          *      IF the password attribute exists, THEN
260                          *      we can do comparisons against it.  If not,
261                          *      then the request did NOT contain a
262                          *      User-Password attribute, so we CANNOT do
263                          *      comparisons against it.
264                          *
265                          *      This hack makes CHAP-Password work..
266                          */
267                         case PW_PASSWORD:
268                                 if (pairfind(request, PW_PASSWORD) == NULL) {
269                                         continue;
270                                 }
271                                 break;
272                 }
273
274                 /*
275                  *      See if this item is present in the request.
276                  */
277                 other = otherattr(check_item->attribute);
278
279                 auth_item = request;
280         try_again:
281                 for (; auth_item != NULL; auth_item = auth_item->next) {
282                         if (auth_item->attribute == other || other == 0)
283                                 break;
284                 }
285
286                 /*
287                  *      Not found, it's not a match.
288                  */
289                 if (auth_item == NULL) {
290                         /*
291                          *      Didn't find it.  If we were *trying*
292                          *      to not find it, then we succeeded.
293                          */
294                         if (check_item->operator == T_OP_CMP_FALSE)
295                                 return 0;
296                         else
297                                 return -1;
298                 }
299
300                 /*
301                  *      Else we found it, but we were trying to not
302                  *      find it, so we failed.
303                  */
304                 if (check_item->operator == T_OP_CMP_FALSE)
305                         return -1;
306
307
308                 /*
309                  *      We've got to xlat the string before doing
310                  *      the comparison.
311                  */
312                 if (check_item->flags.do_xlat) {
313                         int rcode;
314                         char buffer[sizeof(check_item->vp_strvalue)];
315
316                         check_item->flags.do_xlat = 0;
317                         rcode = radius_xlat(buffer, sizeof(buffer),
318                                             check_item->vp_strvalue,
319                                             req, NULL);
320
321                         /*
322                          *      Parse the string into a new value.
323                          */
324                         pairparsevalue(check_item, buffer);
325                 }
326
327                 /*
328                  *      OK it is present now compare them.
329                  */
330                 compare = paircompare(req, auth_item, check_item, check, reply);
331
332                 switch (check_item->operator) {
333                         case T_OP_EQ:
334                         default:
335                                 radlog(L_INFO,  "Invalid operator for item %s: "
336                                                 "reverting to '=='", check_item->name);
337                                 /*FALLTHRU*/
338                         case T_OP_CMP_TRUE:    /* compare always == 0 */
339                         case T_OP_CMP_FALSE:   /* compare always == 1 */
340                         case T_OP_CMP_EQ:
341                                 if (compare != 0) result = -1;
342                                 break;
343
344                         case T_OP_NE:
345                                 if (compare == 0) result = -1;
346                                 break;
347
348                         case T_OP_LT:
349                                 if (compare >= 0) result = -1;
350                                 break;
351
352                         case T_OP_GT:
353                                 if (compare <= 0) result = -1;
354                                 break;
355
356                         case T_OP_LE:
357                                 if (compare > 0) result = -1;
358                                 break;
359
360                         case T_OP_GE:
361                                 if (compare < 0) result = -1;
362                                 break;
363
364 #ifdef HAVE_REGEX_H
365                         case T_OP_REG_EQ:
366                         {
367                                 int i;
368                                 regmatch_t rxmatch[REQUEST_MAX_REGEX + 1];
369
370                                 if ((auth_item->type == PW_TYPE_IPADDR) &&
371                                     (auth_item->vp_strvalue[0] == '\0')) {
372                                   inet_ntop(AF_INET, &(auth_item->lvalue),
373                                             auth_item->vp_strvalue,
374                                             sizeof(auth_item->vp_strvalue));
375                                 }
376
377                                 /*
378                                  *      Include substring matches.
379                                  */
380                                 regcomp(&reg, (char *)check_item->vp_strvalue,
381                                         REG_EXTENDED);
382                                 compare = regexec(&reg,
383                                                   (char *)auth_item->vp_strvalue,
384                                                   REQUEST_MAX_REGEX + 1,
385                                                   rxmatch, 0);
386                                 regfree(&reg);
387
388                                 /*
389                                  *      Add %{0}, %{1}, etc.
390                                  */
391                                 for (i = 0; i <= REQUEST_MAX_REGEX; i++) {
392                                         char *p;
393                                         char buffer[sizeof(check_item->vp_strvalue)];
394
395                                         /*
396                                          *      Didn't match: delete old
397                                          *      match, if it existed.
398                                          */
399                                         if ((compare != 0) ||
400                                             (rxmatch[i].rm_so == -1)) {
401                                                 p = request_data_get(req, req,
402                                                                      REQUEST_DATA_REGEX | i);
403                                                 if (p) {
404                                                         free(p);
405                                                         continue;
406                                                 }
407
408                                                 /*
409                                                  *      No previous match
410                                                  *      to delete, stop.
411                                                  */
412                                                 break;
413                                         }
414                                         
415                                         /*
416                                          *      Copy substring into buffer.
417                                          */
418                                         memcpy(buffer,
419                                                auth_item->vp_strvalue + rxmatch[i].rm_so,
420                                                rxmatch[i].rm_eo - rxmatch[i].rm_so);
421                                         buffer[rxmatch[i].rm_eo - rxmatch[i].rm_so] = '\0';
422
423                                         /*
424                                          *      Copy substring, and add it to
425                                          *      the request.
426                                          *
427                                          *      Note that we don't check
428                                          *      for out of memory, which is
429                                          *      the only error we can get...
430                                          */
431                                         p = strdup(buffer);
432                                         request_data_add(req,
433                                                          req,
434                                                          REQUEST_DATA_REGEX | i,
435                                                          p, free);
436                                 }
437                         }                               
438                                 if (compare != 0) result = -1;
439                                 break;
440
441                         case T_OP_REG_NE:
442                                 if ((auth_item->type == PW_TYPE_IPADDR) &&
443                                     (auth_item->vp_strvalue[0] == '\0')) {
444                                   inet_ntop(AF_INET, &(auth_item->lvalue),
445                                             auth_item->vp_strvalue,
446                                             sizeof(auth_item->vp_strvalue));
447                                 }
448
449                                 regcomp(&reg, (char *)check_item->vp_strvalue, REG_EXTENDED|REG_NOSUB);
450                                 compare = regexec(&reg, (char *)auth_item->vp_strvalue,
451                                                 0, NULL, 0);
452                                 regfree(&reg);
453                                 if (compare == 0) result = -1;
454                                 break;
455 #endif
456
457                 } /* switch over the operator of the check item */
458
459                 /*
460                  *      This attribute didn't match, but maybe there's
461                  *      another of the same attribute, which DOES match.
462                  */
463                 if (result != 0) {
464                         auth_item = auth_item->next;
465                         result = 0;
466                         goto try_again;
467                 }
468
469         } /* for every entry in the check item list */
470
471         return 0;               /* it matched */
472 }
473
474 /*
475  *      Compare two attributes simply.  Calls paircompare.
476  */
477
478 int simplepaircmp(REQUEST *req, VALUE_PAIR *first, VALUE_PAIR *second)
479 {
480         return paircompare( req, first, second, NULL, NULL );
481 }
482
483
484 /*
485  *      Move pairs, replacing/over-writing them, and doing xlat.
486  */
487 /*
488  *      Move attributes from one list to the other
489  *      if not already present.
490  */
491 void pairxlatmove(REQUEST *req, VALUE_PAIR **to, VALUE_PAIR **from)
492 {
493         VALUE_PAIR **tailto, *i, *j, *next;
494         VALUE_PAIR *tailfrom = NULL;
495         VALUE_PAIR *found;
496
497         /*
498          *      Point "tailto" to the end of the "to" list.
499          */
500         tailto = to;
501         for(i = *to; i; i = i->next) {
502                 tailto = &i->next;
503         }
504
505         /*
506          *      Loop over the "from" list.
507          */
508         for(i = *from; i; i = next) {
509                 next = i->next;
510
511                 /*
512                  *      Don't move 'fallthrough' over.
513                  */
514                 if (i->attribute == PW_FALL_THROUGH) {
515                         continue;
516                 }
517
518                 /*
519                  *      We've got to xlat the string before moving
520                  *      it over.
521                  */
522                 if (i->flags.do_xlat) {
523                         int rcode;
524                         char buffer[sizeof(i->vp_strvalue)];
525
526                         i->flags.do_xlat = 0;
527                         rcode = radius_xlat(buffer, sizeof(buffer),
528                                             i->vp_strvalue,
529                                             req, NULL);
530
531                         /*
532                          *      Parse the string into a new value.
533                          */
534                         pairparsevalue(i, buffer);
535                 }
536
537                 found = pairfind(*to, i->attribute);
538                 switch (i->operator) {
539
540                         /*
541                          *  If a similar attribute is found,
542                          *  delete it.
543                          */
544                 case T_OP_SUB:          /* -= */
545                         if (found) {
546                                 if (!i->vp_strvalue[0] ||
547                                     (strcmp((char *)found->vp_strvalue,
548                                             (char *)i->vp_strvalue) == 0)){
549                                         pairdelete(to, found->attribute);
550
551                                         /*
552                                          *      'tailto' may have been
553                                          *      deleted...
554                                          */
555                                         tailto = to;
556                                         for(j = *to; j; j = j->next) {
557                                                 tailto = &j->next;
558                                         }
559                                 }
560                         }
561                         tailfrom = i;
562                         continue;
563                         break;
564
565                         /*
566                          *  Add it, if it's not already there.
567                          */
568                 case T_OP_EQ:           /* = */
569                         if (found) {
570                                 tailfrom = i;
571                                 continue; /* with the loop */
572                         }
573                         break;
574
575                         /*
576                          *  If a similar attribute is found,
577                          *  replace it with the new one.  Otherwise,
578                          *  add the new one to the list.
579                          */
580                 case T_OP_SET:          /* := */
581                         if (found) {
582                                 VALUE_PAIR *vp;
583
584                                 vp = found->next;
585                                 memcpy(found, i, sizeof(*found));
586                                 found->next = vp;
587                                 continue;
588                         }
589                         break;
590
591                         /*
592                          *  FIXME: Add support for <=, >=, <, >
593                          *
594                          *  which will mean (for integers)
595                          *  'make the attribute the smaller, etc'
596                          */
597
598                         /*
599                          *  Add the new element to the list, even
600                          *  if similar ones already exist.
601                          */
602                 default:
603                 case T_OP_ADD:          /* += */
604                         break;
605                 }
606
607                 if (tailfrom)
608                         tailfrom->next = next;
609                 else
610                         *from = next;
611
612                 /*
613                  *      If ALL of the 'to' attributes have been deleted,
614                  *      then ensure that the 'tail' is updated to point
615                  *      to the head.
616                  */
617                 if (!*to) {
618                         tailto = to;
619                 }
620                 *tailto = i;
621                 if (i) {
622                         i->next = NULL;
623                         tailto = &i->next;
624                 }
625         } /* loop over the 'from' list */
626 }