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