radius_pairmake(), to mirror radius_paircreate().
[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 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          *      Tagged attributes are equal if and only if both the
167          *      tag AND value match.
168          */
169         if ((ret == 0) && check->flags.has_tag) {
170                 ret = ((int) vp->flags.tag) - ((int) check->flags.tag);
171                 if (ret != 0) return ret;
172         }
173
174         /*
175          *      Not a regular expression, compare the types.
176          */
177         switch(check->type) {
178 #ifdef ASCEND_BINARY
179                 /*
180                  *      Ascend binary attributes can be treated
181                  *      as opaque objects, I guess...
182                  */
183                 case PW_TYPE_ABINARY:
184 #endif
185                 case PW_TYPE_OCTETS:
186                         if (vp->length != check->length) {
187                                 ret = 1; /* NOT equal */
188                                 break;
189                         }
190                         ret = memcmp(vp->vp_strvalue, check->vp_strvalue,
191                                      vp->length);
192                         break;
193                 case PW_TYPE_STRING:
194                         ret = strcmp((char *)vp->vp_strvalue,
195                                      (char *)check->vp_strvalue);
196                         break;
197                 case PW_TYPE_BYTE:
198                 case PW_TYPE_SHORT:
199                 case PW_TYPE_INTEGER:
200                         ret = vp->vp_integer - check->vp_integer;
201                         break;
202                 case PW_TYPE_DATE:
203                         ret = vp->vp_date - check->vp_date;
204                         break;
205                 case PW_TYPE_IPADDR:
206                         ret = ntohl(vp->vp_ipaddr) - ntohl(check->vp_ipaddr);
207                         break;
208                 case PW_TYPE_IPV6ADDR:
209                         ret = memcmp(&vp->vp_ipv6addr, &check->vp_ipv6addr,
210                                      sizeof(vp->vp_ipv6addr));
211                         break;
212
213                 case PW_TYPE_IPV6PREFIX:
214                         ret = memcmp(&vp->vp_ipv6prefix, &check->vp_ipv6prefix,
215                                      sizeof(vp->vp_ipv6prefix));
216                         break;
217
218                 case PW_TYPE_IFID:
219                         ret = memcmp(&vp->vp_ifid, &check->vp_ifid,
220                                      sizeof(vp->vp_ifid));
221                         break;
222
223                 default:
224                         break;
225         }
226
227         return ret;
228 }
229
230
231 /*
232  *      Compare 2 attributes. May call the attribute compare function.
233  */
234 int radius_callback_compare(REQUEST *req, VALUE_PAIR *request,
235                             VALUE_PAIR *check, VALUE_PAIR *check_pairs,
236                             VALUE_PAIR **reply_pairs)
237 {
238         struct cmp *c;
239
240         /*
241          *      Check for =* and !* and return appropriately
242          */
243         if( check->operator == T_OP_CMP_TRUE )
244                  return 0;  /* always return 0/EQUAL */
245         if( check->operator == T_OP_CMP_FALSE )
246                  return 1;  /* always return 1/NOT EQUAL */
247
248         /*
249          *      See if there is a special compare function.
250          *
251          *      FIXME: use new RB-Tree code.
252          */
253         for (c = cmp; c; c = c->next)
254                 if (c->attribute == check->attribute) {
255                         return (c->compare)(c->instance, req, request, check,
256                                 check_pairs, reply_pairs);
257                 }
258
259         if (!request) return -1; /* doesn't exist, don't compare it */
260
261         return radius_compare_vps(req, check, request);
262 }
263
264
265 /*
266  *      See what attribute we want to compare with.
267  */
268 static int otherattr(int attr)
269 {
270         struct cmp      *c;
271
272         for (c = cmp; c; c = c->next) {
273                 if (c->attribute == attr)
274                         return c->otherattr;
275         }
276
277         return attr;
278 }
279
280 /*
281  *      Register a function as compare function.
282  *      compare_attr is the attribute in the request we want to
283  *      compare with. Normally this is the same as "attr".
284  *      You can set this to:
285  *
286  *      -1   the same as "attr"
287  *      0    always call compare function, not tied to request attribute
288  *      >0   Attribute to compare with.
289  *
290  *      For example, PW_GROUP in a check item needs to be compared
291  *      with PW_USER_NAME in the incoming request.
292  */
293 int paircompare_register(int attr, int compare_attr, RAD_COMPARE_FUNC fun, void *instance)
294 {
295         struct cmp      *c;
296
297         paircompare_unregister(attr, fun);
298
299         c = rad_malloc(sizeof(struct cmp));
300
301         c->compare = fun;
302         c->attribute = attr;
303         c->otherattr = compare_attr;
304         c->instance = instance;
305         c->next = cmp;
306         cmp = c;
307
308         return 0;
309 }
310
311 /*
312  *      Unregister a function.
313  */
314 void paircompare_unregister(int attr, RAD_COMPARE_FUNC fun)
315 {
316         struct cmp      *c, *last;
317
318         last = NULL;
319         for (c = cmp; c; c = c->next) {
320                 if (c->attribute == attr && c->compare == fun)
321                         break;
322                 last = c;
323         }
324
325         if (c == NULL) return;
326
327         if (last != NULL)
328                 last->next = c->next;
329         else
330                 cmp = c->next;
331
332         free(c);
333 }
334
335 /*
336  *      Compare two pair lists except for the password information.
337  *      For every element in "check" at least one matching copy must
338  *      be present in "reply".
339  *
340  *      Return 0 on match.
341  */
342 int paircompare(REQUEST *req, VALUE_PAIR *request, VALUE_PAIR *check, VALUE_PAIR **reply)
343 {
344         VALUE_PAIR *check_item;
345         VALUE_PAIR *auth_item;
346         int result = 0;
347         int compare;
348         int other;
349
350         for (check_item = check; check_item != NULL; check_item = check_item->next) {
351                 /*
352                  *      If the user is setting a configuration value,
353                  *      then don't bother comparing it to any attributes
354                  *      sent to us by the user.  It ALWAYS matches.
355                  */
356                 if ((check_item->operator == T_OP_SET) ||
357                     (check_item->operator == T_OP_ADD)) {
358                         continue;
359                 }
360
361                 switch (check_item->attribute) {
362                         /*
363                          *      Attributes we skip during comparison.
364                          *      These are "server" check items.
365                          */
366                         case PW_CRYPT_PASSWORD:
367                         case PW_AUTH_TYPE:
368                         case PW_AUTZ_TYPE:
369                         case PW_ACCT_TYPE:
370                         case PW_SESSION_TYPE:
371                         case PW_STRIP_USER_NAME:
372                                 continue;
373                                 break;
374
375                         /*
376                          *      IF the password attribute exists, THEN
377                          *      we can do comparisons against it.  If not,
378                          *      then the request did NOT contain a
379                          *      User-Password attribute, so we CANNOT do
380                          *      comparisons against it.
381                          *
382                          *      This hack makes CHAP-Password work..
383                          */
384                         case PW_USER_PASSWORD:
385                                 if (check_item->operator == T_OP_CMP_EQ) {
386                                         DEBUG("WARNING: Found User-Password == \"...\".");
387                                         DEBUG("WARNING: Are you sure you don't mean Cleartext-Password?");
388                                         DEBUG("WARNING: See \"man rlm_pap\" for more information.");
389                                 }
390                                 if (pairfind(request, PW_USER_PASSWORD) == NULL) {
391                                         continue;
392                                 }
393                                 break;
394                 }
395
396                 /*
397                  *      See if this item is present in the request.
398                  */
399                 other = otherattr(check_item->attribute);
400
401                 auth_item = request;
402         try_again:
403                 if (other >= 0) {
404                         for (; auth_item != NULL; auth_item = auth_item->next) {
405                                 if (auth_item->attribute == other || other == 0)
406                                         break;
407                         }
408                 }
409
410                 /*
411                  *      Not found, it's not a match.
412                  */
413                 if (auth_item == NULL) {
414                         /*
415                          *      Didn't find it.  If we were *trying*
416                          *      to not find it, then we succeeded.
417                          */
418                         if (check_item->operator == T_OP_CMP_FALSE)
419                                 continue;
420                         else
421                                 return -1;
422                 }
423
424                 /*
425                  *      Else we found it, but we were trying to not
426                  *      find it, so we failed.
427                  */
428                 if (check_item->operator == T_OP_CMP_FALSE)
429                         return -1;
430
431
432                 /*
433                  *      We've got to xlat the string before doing
434                  *      the comparison.
435                  */
436                 if (check_item->flags.do_xlat) {
437                         int rcode;
438                         char buffer[sizeof(check_item->vp_strvalue)];
439
440                         check_item->flags.do_xlat = 0;
441                         rcode = radius_xlat(buffer, sizeof(buffer),
442                                             check_item->vp_strvalue,
443                                             req, NULL);
444
445                         /*
446                          *      Parse the string into a new value.
447                          */
448                         pairparsevalue(check_item, buffer);
449                 }
450
451                 /*
452                  *      OK it is present now compare them.
453                  */
454                 compare = radius_callback_compare(req, auth_item, check_item,
455                                                   check, reply);
456
457                 switch (check_item->operator) {
458                         case T_OP_EQ:
459                         default:
460                                 radlog(L_INFO,  "Invalid operator for item %s: "
461                                                 "reverting to '=='", check_item->name);
462                                 /*FALLTHRU*/
463                         case T_OP_CMP_TRUE:    /* compare always == 0 */
464                         case T_OP_CMP_FALSE:   /* compare always == 1 */
465                         case T_OP_CMP_EQ:
466                                 if (compare != 0) result = -1;
467                                 break;
468
469                         case T_OP_NE:
470                                 if (compare == 0) result = -1;
471                                 break;
472
473                         case T_OP_LT:
474                                 if (compare >= 0) result = -1;
475                                 break;
476
477                         case T_OP_GT:
478                                 if (compare <= 0) result = -1;
479                                 break;
480
481                         case T_OP_LE:
482                                 if (compare > 0) result = -1;
483                                 break;
484
485                         case T_OP_GE:
486                                 if (compare < 0) result = -1;
487                                 break;
488
489 #ifdef HAVE_REGEX_H
490                         case T_OP_REG_EQ:
491                         case T_OP_REG_NE:
492                                 result = compare;
493                                 break;
494 #endif
495                 } /* switch over the operator of the check item */
496
497                 /*
498                  *      This attribute didn't match, but maybe there's
499                  *      another of the same attribute, which DOES match.
500                  */
501                 if ((result != 0) && (other >= 0)) {
502                         auth_item = auth_item->next;
503                         result = 0;
504                         goto try_again;
505                 }
506
507         } /* for every entry in the check item list */
508
509         return result;
510 }
511
512 /*
513  *      Move pairs, replacing/over-writing them, and doing xlat.
514  */
515 /*
516  *      Move attributes from one list to the other
517  *      if not already present.
518  */
519 void pairxlatmove(REQUEST *req, VALUE_PAIR **to, VALUE_PAIR **from)
520 {
521         VALUE_PAIR **tailto, *i, *j, *next;
522         VALUE_PAIR *tailfrom = NULL;
523         VALUE_PAIR *found;
524
525         /*
526          *      Point "tailto" to the end of the "to" list.
527          */
528         tailto = to;
529         for(i = *to; i; i = i->next) {
530                 tailto = &i->next;
531         }
532
533         /*
534          *      Loop over the "from" list.
535          */
536         for(i = *from; i; i = next) {
537                 next = i->next;
538
539                 /*
540                  *      Don't move 'fallthrough' over.
541                  */
542                 if (i->attribute == PW_FALL_THROUGH) {
543                         continue;
544                 }
545
546                 /*
547                  *      We've got to xlat the string before moving
548                  *      it over.
549                  */
550                 if (i->flags.do_xlat) {
551                         int rcode;
552                         char buffer[sizeof(i->vp_strvalue)];
553
554                         i->flags.do_xlat = 0;
555                         rcode = radius_xlat(buffer, sizeof(buffer),
556                                             i->vp_strvalue,
557                                             req, NULL);
558
559                         /*
560                          *      Parse the string into a new value.
561                          */
562                         pairparsevalue(i, buffer);
563                 }
564
565                 found = pairfind(*to, i->attribute);
566                 switch (i->operator) {
567
568                         /*
569                          *  If a similar attribute is found,
570                          *  delete it.
571                          */
572                 case T_OP_SUB:          /* -= */
573                         if (found) {
574                                 if (!i->vp_strvalue[0] ||
575                                     (strcmp((char *)found->vp_strvalue,
576                                             (char *)i->vp_strvalue) == 0)){
577                                         pairdelete(to, found->attribute);
578
579                                         /*
580                                          *      'tailto' may have been
581                                          *      deleted...
582                                          */
583                                         tailto = to;
584                                         for(j = *to; j; j = j->next) {
585                                                 tailto = &j->next;
586                                         }
587                                 }
588                         }
589                         tailfrom = i;
590                         continue;
591                         break;
592
593                         /*
594                          *  Add it, if it's not already there.
595                          */
596                 case T_OP_EQ:           /* = */
597                         if (found) {
598                                 tailfrom = i;
599                                 continue; /* with the loop */
600                         }
601                         break;
602
603                         /*
604                          *  If a similar attribute is found,
605                          *  replace it with the new one.  Otherwise,
606                          *  add the new one to the list.
607                          */
608                 case T_OP_SET:          /* := */
609                         if (found) {
610                                 VALUE_PAIR *vp;
611
612                                 vp = found->next;
613                                 memcpy(found, i, sizeof(*found));
614                                 found->next = vp;
615                                 continue;
616                         }
617                         break;
618
619                         /*
620                          *  FIXME: Add support for <=, >=, <, >
621                          *
622                          *  which will mean (for integers)
623                          *  'make the attribute the smaller, etc'
624                          */
625
626                         /*
627                          *  Add the new element to the list, even
628                          *  if similar ones already exist.
629                          */
630                 default:
631                 case T_OP_ADD:          /* += */
632                         break;
633                 }
634
635                 if (tailfrom)
636                         tailfrom->next = next;
637                 else
638                         *from = next;
639
640                 /*
641                  *      If ALL of the 'to' attributes have been deleted,
642                  *      then ensure that the 'tail' is updated to point
643                  *      to the head.
644                  */
645                 if (!*to) {
646                         tailto = to;
647                 }
648                 *tailto = i;
649                 if (i) {
650                         i->next = NULL;
651                         tailto = &i->next;
652                 }
653         } /* loop over the 'from' list */
654 }
655
656 /*
657  *      Create a pair, and add it to a particular list of VPs
658  *
659  *      Note that this function ALWAYS returns.  If we're OOM, then
660  *      it causes the server to exit!
661  */
662 VALUE_PAIR *radius_paircreate(REQUEST *request, VALUE_PAIR **vps,
663                               int attribute, int type)
664 {
665         VALUE_PAIR *vp;
666
667         request = request;      /* -Wunused */
668
669         vp = paircreate(attribute, type);
670         if (!vp) {
671                 radlog(L_ERR, "No memory!");
672                 rad_assert("No memory" == NULL);
673                 _exit(1);
674         }
675
676         if (vps) pairadd(vps, vp);
677
678         return vp;
679 }
680
681 /*
682  *      Create a pair, and add it to a particular list of VPs
683  *
684  *      Note that this function ALWAYS returns.  If we're OOM, then
685  *      it causes the server to exit!
686  */
687 VALUE_PAIR *radius_pairmake(REQUEST *request, VALUE_PAIR **vps,
688                             const char *attribute, const char *value,
689                             int operator)
690 {
691         VALUE_PAIR *vp;
692
693         request = request;      /* -Wunused */
694
695         vp = pairmake(attribute, value, operator);
696         if (!vp) return NULL;
697
698         if (vps) pairadd(vps, vp);
699
700         return vp;
701 }
702