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