ae7c2f27b733609ff383a4e677bbb19af25f75e4
[freeradius.git] / src / lib / valuepair.c
1 /*
2  * valuepair.c  Functions to handle VALUE_PAIRs
3  *
4  * Version:     $Id$
5  *
6  */
7
8 static const char rcsid[] = "$Id$";
9
10 #include        "autoconf.h"
11
12 #include        <sys/types.h>
13
14 #include        <stdio.h>
15 #include        <stdlib.h>
16 #include        <string.h>
17 #include        <ctype.h>
18
19 #include        "libradius.h"
20
21 #if HAVE_MALLOC_H
22 #  include      <malloc.h>
23 #endif
24
25 #if HAVE_REGEX_H
26 #  include      <regex.h>
27 #endif
28
29 #include        "missing.h"
30
31 static const char *months[] = {
32         "jan", "feb", "mar", "apr", "may", "jun",
33         "jul", "aug", "sep", "oct", "nov", "dec" };
34
35
36 /*
37  *      Create a new valuepair.
38  */
39 VALUE_PAIR *paircreate(int attr, int type)
40 {
41         VALUE_PAIR      *vp;
42         DICT_ATTR       *da;
43
44         if ((vp = malloc(sizeof(VALUE_PAIR))) == NULL) {
45                 librad_log("out of memory");
46                 return NULL;
47         }
48         memset(vp, 0, sizeof(VALUE_PAIR));
49         vp->attribute = attr;
50         vp->operator = T_OP_EQ;
51         vp->type = type;
52         if ((da = dict_attrbyvalue(attr)) != NULL)
53                 strcpy(vp->name, da->name);
54         else
55                 sprintf(vp->name, "Attr-%d", attr);
56         switch (vp->type) {
57                 case PW_TYPE_INTEGER:
58                 case PW_TYPE_IPADDR:
59                 case PW_TYPE_DATE:
60                         vp->length = 4;
61                         break;
62                 default:
63                         vp->length = 0;
64                         break;
65         }
66
67         return vp;
68 }
69
70 /*
71  *      Release the memory used by a list of attribute-value
72  *      pairs, and sets the pair pointer to NULL.
73  */
74 void pairfree(VALUE_PAIR **pair_ptr)
75 {
76         VALUE_PAIR      *next, *pair;
77
78         if (!pair_ptr) return;
79         pair = *pair_ptr;
80
81         while (pair != NULL) {
82                 next = pair->next;
83                 free(pair);
84                 pair = next;
85         }
86
87         *pair_ptr = NULL;
88 }
89
90
91 /*
92  *      Find the pair with the matching attribute
93  */
94 VALUE_PAIR * pairfind(VALUE_PAIR *first, int attr)
95 {
96         while(first && first->attribute != attr)
97                 first = first->next;
98         return first;
99 }
100
101
102 /*
103  *      Delete the pair(s) with the matching attribute
104  */
105 void pairdelete(VALUE_PAIR **first, int attr)
106 {
107         VALUE_PAIR *i, *next;
108         VALUE_PAIR **last = first;
109
110         for(i = *first; i; i = next) {
111                 next = i->next;
112                 if (i->attribute == attr) {
113                         *last = next;
114                         free(i);
115                 } else {
116                         last = &i->next;
117                 }
118         }
119 }
120
121 /*
122  *      Add a pair at the end of a VALUE_PAIR list.
123  */
124 void pairadd(VALUE_PAIR **first, VALUE_PAIR *add)
125 {
126         VALUE_PAIR *i;
127
128         if (*first == NULL) {
129                 *first = add;
130                 return;
131         }
132         for(i = *first; i->next; i = i->next)
133                 ;
134         i->next = add;
135 }
136
137 /*
138  *      Copy just a certain type of pairs.
139  */
140 VALUE_PAIR *paircopy2(VALUE_PAIR *vp, int attr)
141 {
142         VALUE_PAIR      *first, *n, **last;
143
144         first = NULL;
145         last = &first;
146
147         while (vp) {
148                 if (attr >= 0 && vp->attribute != attr) {
149                         vp = vp->next;
150                         continue;
151                 }
152                 if ((n = (VALUE_PAIR *)malloc(sizeof(VALUE_PAIR))) == NULL) {
153                         librad_log("out of memory");
154                         return first;
155                 }
156                 memcpy(n, vp, sizeof(VALUE_PAIR));
157                 n->next = NULL;
158                 *last = n;
159                 last = &n->next;
160                 vp = vp->next;
161         }
162         return first;
163 }
164
165
166 /*
167  *      Copy a pairlist.
168  */
169 VALUE_PAIR *paircopy(VALUE_PAIR *vp)
170 {
171         return paircopy2(vp, -1);
172 }
173
174
175 /*
176  *      Move attributes from one list to the other
177  *      if not already present.
178  */
179 void pairmove(VALUE_PAIR **to, VALUE_PAIR **from)
180 {
181         VALUE_PAIR **tailto, *i, *j, *next;
182         VALUE_PAIR *tailfrom = NULL;
183         VALUE_PAIR *found;
184         int has_password = 0;
185
186         if (*to == NULL) {
187                 *to = *from;
188                 *from = NULL;
189                 return;
190         }
191
192         /*
193          *      First, see if there are any passwords here, and
194          *      point "tailto" to the end of the "to" list.
195          */
196         tailto = to;
197         for(i = *to; i; i = i->next) {
198                 if (i->attribute == PW_PASSWORD ||
199                     i->attribute == PW_CRYPT_PASSWORD)
200                         has_password = 1;
201                 tailto = &i->next;
202         }
203
204         /*
205          *      Loop over the "from" list.
206          */
207         for(i = *from; i; i = next) {
208                 next = i->next;
209                 /*
210                  *      If there was a password in the "to" list,
211                  *      do not move any other password from the
212                  *      "from" to the "to" list.
213                  */
214                 if (has_password &&
215                     (i->attribute == PW_PASSWORD ||
216                      i->attribute == PW_CRYPT_PASSWORD)) {
217                         tailfrom = i;
218                         continue;
219                 }
220                 /*
221                  *      If the attribute is already present in "to",
222                  *      do not move it from "from" to "to". We make
223                  *      an exception for "Hint" which can appear multiple
224                  *      times, and we never move "Fall-Through".
225                  */
226                 if (i->attribute == PW_FALL_THROUGH ||
227                     (i->attribute != PW_HINT && i->attribute != PW_FRAMED_ROUTE)) {
228                   
229                         found = pairfind(*to, i->attribute);
230                         switch (i->operator) {
231
232                           /*
233                            *  If a similar attribute is found,
234                            *  delete it.
235                            */
236                         case T_OP_SUB:          /* -= */
237                                 if (found) {
238                                         if (!i->strvalue[0] ||
239                                             (strcmp((char *)found->strvalue,
240                                                     (char *)i->strvalue) == 0)){
241                                                 pairdelete(to, found->attribute);
242                                                 
243                                                 /*
244                                                  *      'tailto' may have been
245                                                  *      deleted...
246                                                  */
247                                                 tailto = to;
248                                                 for(j = *to; j; j = j->next) {
249                                                         tailto = &j->next;
250                                                 }
251                                         }
252                                 }
253                                 tailfrom = i;
254                                 continue;
255                                 break;
256                                 
257 /* really HAVE_REGEX_H */
258 #if 0 
259                                 /*
260                                  *  Attr-Name =~ "s/find/replace/"
261                                  *
262                                  *  Very bad code.  Barely working,
263                                  *  if at all.
264                                  */
265
266                         case T_OP_REG_EQ:
267                           if (found &&
268                               (i->strvalue[0] == 's')) {
269                             regex_t             reg;
270                             regmatch_t          match[1];
271
272                             char *str;
273                             char *p, *q;
274
275                             p = i->strvalue + 1;
276                             q = strchr(p + 1, *p);
277                             if (!q || (q[strlen(q) - 1] != *p)) {
278                               tailfrom = i;
279                               continue;
280                             }
281                             str = strdup(i->strvalue + 2);
282                             q = strchr(str, *p);
283                             *(q++) = '\0';
284                             q[strlen(q) - 1] = '\0';
285                             
286                             regcomp(&reg, str, 0);
287                             if (regexec(&reg, found->strvalue,
288                                         1, match, 0) == 0) {
289                               fprintf(stderr, "\"%s\" will have %d to %d replaced with %s\n",
290                                       found->strvalue, match[0].rm_so,
291                                       match[0].rm_eo, q);
292
293                             }
294                             regfree(&reg);
295                             free(str);
296                           }
297                           tailfrom = i; /* don't copy it over */
298                           continue;
299                           break;
300 #endif
301                         case T_OP_EQ:           /* = */
302                                 if (found) {
303                                         tailfrom = i;
304                                         continue; /* with the loop */
305                                 }
306                                 break;
307
308                           /*
309                            *  If a similar attribute is found,
310                            *  replace it with the new one.  Otherwise,
311                            *  add the new one to the list.
312                            */
313                         case T_OP_SET:          /* := */
314                                 if (found) {
315                                         pairdelete(to, found->attribute);
316                                         /*
317                                          *      'tailto' may have been
318                                          *      deleted...
319                                          */
320                                         tailto = to;
321                                         for(j = *to; j; j = j->next) {
322                                                 tailto = &j->next;
323                                         }
324                                 }
325                                 break;
326                                 
327
328                            /*
329                             *  Add the new element to the list, even
330                             *  if similar ones already exist.
331                             */
332                         default:
333                         case T_OP_ADD:          /* += */
334                                 break;
335                         }
336                 
337                 }
338                 if (tailfrom)
339                         tailfrom->next = next;
340                 else
341                         *from = next;
342                 
343                 /*
344                  *      If ALL of the 'to' attributes have been deleted,
345                  *      then ensure that the 'tail' is updated to point
346                  *      to the head.
347                  */
348                 if (!*to) {
349                         tailto = to;
350                 }
351                 *tailto = i;
352                 if (i) {
353                         i->next = NULL;
354                         tailto = &i->next;
355                 }
356         }
357 }
358
359 /*
360  *      Move one kind of attributes from one list to the other
361  */
362 void pairmove2(VALUE_PAIR **to, VALUE_PAIR **from, int attr)
363 {
364         VALUE_PAIR *to_tail, *i, *next;
365         VALUE_PAIR *iprev = NULL;
366
367         /*
368          *      Find the last pair in the "to" list and put it in "to_tail".
369          */
370         if (*to != NULL) {
371                 to_tail = *to;
372                 for(i = *to; i; i = i->next)
373                         to_tail = i;
374         } else
375                 to_tail = NULL;
376
377         for(i = *from; i; i = next) {
378                 next = i->next;
379
380
381                 /*
382                  *      If the attribute to move is NOT a VSA, then it
383                  *      ignores any attributes which do not match exactly.
384                  */
385                 if ((attr != PW_VENDOR_SPECIFIC) &&
386                     (i->attribute != attr)) {
387                         iprev = i;
388                         continue;
389                 }
390
391                 /*
392                  *      If the attribute to move IS a VSA, then it ignores
393                  *      any non-VSA attribute.
394                  */
395                 if ((attr == PW_VENDOR_SPECIFIC) &&
396                     (VENDOR(i->attribute) == 0)) {
397                         iprev = i;
398                         continue;
399                 }
400
401                 /*
402                  *      Remove the attribute from the "from" list.
403                  */
404                 if (iprev)
405                         iprev->next = next;
406                 else
407                         *from = next;
408
409                 /*
410                  *      Add the attribute to the "to" list.
411                  */
412                 if (to_tail)
413                         to_tail->next = i;
414                 else
415                         *to = i;
416                 to_tail = i;
417                 i->next = NULL;
418         }
419 }
420
421
422 /*
423  *      Sort of strtok/strsep function.
424  */
425 static char *mystrtok(char **ptr, const char *sep)
426 {
427         char    *res;
428
429         if (**ptr == 0)
430                 return NULL;
431         while (**ptr && strchr(sep, **ptr))
432                 (*ptr)++;
433         if (**ptr == 0)
434                 return NULL;
435         res = *ptr;
436         while (**ptr && strchr(sep, **ptr) == NULL)
437                 (*ptr)++;
438         if (**ptr != 0)
439                 *(*ptr)++ = 0;
440         return res;
441 }
442
443 /*
444  *      Turn printable string into time_t
445  *      Returns -1 on error, 0 on OK.
446  */
447 static int gettime(const char *valstr, time_t *lvalue)
448 {
449         int             i;
450         time_t          t;
451         struct tm       *tm, s_tm;
452         char            buf[64];
453         char            *p;
454         char            *year, *month, *day;
455
456         time(&t);
457         tm = localtime_r(&t, &s_tm);
458
459         strNcpy(buf, valstr, sizeof(buf));
460         for (p = buf; *p; p++)
461                 if (isupper(*p)) *p = tolower(*p);
462
463         p = buf;
464         day = mystrtok(&p, " \t");
465         month = mystrtok(&p, " \t");
466         year = mystrtok(&p, " \t");
467         if (!year || !month || !day) return -1;
468
469         tm->tm_mon = 12;
470         for (i = 0; i < 12; i++) {
471                 if (strncasecmp(months[i], month, 3) == 0) {
472                         tm->tm_mon = i;
473                         break;
474                 }
475         }
476
477         /* month not found? */
478         if (tm->tm_mon == 12) return -1;
479
480         tm->tm_mday = atoi(day);
481         if ((tm->tm_mday < 1) || (tm->tm_mday > 31)) {
482                 return -1;
483         }
484
485         tm->tm_year = atoi(year);
486         if (tm->tm_year >= 1900) tm->tm_year -= 1900;
487
488         *lvalue = mktime(tm);
489         return 0;
490 }
491
492 /*
493  *      Create a VALUE_PAIR from an ASCII attribute and value.
494  */
495 VALUE_PAIR *pairmake(const char *attribute, const char *value, int operator)
496 {
497         DICT_ATTR       *da;
498         DICT_VALUE      *dval;
499         VALUE_PAIR      *vp;
500         char            *p, *s=0;
501         const char      *cp, *cs;
502         char            *tc, *ts;
503         signed char     tag;
504         int             found_tag;
505
506         /*
507          *    Check for tags in 'Attribute:Tag' format.
508          */
509         found_tag = 0;
510         tag = 0;
511
512         ts = strrchr( attribute, ':' );
513         if (ts && ts[1]) {
514                  /* Colon found with something behind it */
515                  if (ts[1] == '*' && ts[2] == 0) {
516                          /* Wildcard tag for check items */
517                          tag = TAG_ANY;
518                          *ts = 0;
519                  } else if ((ts[1] >= 0) && (ts[1] <= '9')) {
520                          /* It's not a wild card tag */
521                          tag = strtol(ts + 1, &tc, 0);
522                          if (tc && !*tc && TAG_VALID_ZERO(tag))
523                                  *ts = 0;
524                          else tag = 0;
525                  } else {
526                          librad_log("Invalid tag for attribute %s", attribute);
527                          return NULL;
528                  }
529                  found_tag = 1;
530         }
531
532         if ((da = dict_attrbyname(attribute)) == NULL) {
533                 librad_log("Unknown attribute %s", attribute);
534                 return NULL;
535         }
536
537         if ((vp = (VALUE_PAIR *)malloc(sizeof(VALUE_PAIR))) == NULL) {
538                 librad_log("out of memory");
539                 return NULL;
540         }
541
542         memset(vp, 0, sizeof(VALUE_PAIR));
543         vp->attribute = da->attr;
544         vp->type = da->type;
545         vp->operator = (operator == 0) ? T_OP_EQ : operator;
546         strcpy(vp->name, da->name);
547         vp->flags = da->flags;
548         vp->next = NULL;
549
550         /*      Check for a tag in the 'Merit' format of:
551          *      :Tag:Value.  Print an error if we already found
552          *      a tag in the Attribute.
553          */
554
555         if (*value == ':' && da->flags.has_tag) {
556                 /* If we already found a tag, this is invalid */
557                 if(found_tag) {
558                         free(vp);
559                         librad_log("Duplicate tag %s for attribute %s",
560                                    value, vp->name);
561                         DEBUG("Duplicate tag %s for attribute %s\n",
562                                    value, vp->name);
563                         return NULL;
564
565                 }
566                 /* Colon found and attribute allows a tag */
567                 if (value[1] == '*' && value[2] == ':') {
568                        /* Wildcard tag for check items */
569                        tag = TAG_ANY;
570                        value += 3;
571                 } else {
572                        /* Real tag */
573                        tag = strtol(value + 1, &tc, 0);
574                        if (tc && *tc==':' && TAG_VALID_ZERO(tag))
575                             value = tc + 1;
576                        else tag = 0;
577                 }
578                 found_tag = 1;
579         }       
580         
581         if(found_tag) {
582           vp->flags.tag = tag;
583         }
584
585         /*
586          *      Even for integers, dates and ip addresses we
587          *      keep the original string in vp->strvalue.
588          */
589         strNcpy((char *)vp->strvalue, value, MAX_STRING_LEN);
590
591         /*
592          *      For =* and !* operators, the value is irrelevant
593          *      so we return now.
594          */
595
596         if( vp->operator == T_OP_CMP_TRUE 
597             || vp->operator == T_OP_CMP_FALSE ) {
598                 return vp;
599         }
600
601         switch(da->type) {
602                 case PW_TYPE_STRING:
603                         vp->length = strlen(value);
604                         if (vp->length >= MAX_STRING_LEN) {
605                           vp->length = MAX_STRING_LEN - 1;
606                         }
607                         break;
608                 case PW_TYPE_IPADDR:
609                         /*
610                          *      FIXME: complain if hostname
611                          *      cannot be resolved, or resolve later!
612                          */
613                         if ((p = strrchr(value, '+')) != NULL && !p[1]) {
614                                 cs = s = strdup(value);
615                                 p = strrchr(s, '+');
616                                 *p = 0;
617                                 vp->flags.addport = 1;
618                         } else {
619                                 p = NULL;
620                                 cs = value;
621                         }
622                         vp->lvalue = librad_dodns ? ip_getaddr(cs) :
623                                                     ip_addr(cs);
624                         vp->length = 4;
625                         if (s) free(s);
626                         break;
627                 case PW_TYPE_INTEGER:
628                         /*
629                          *      If it starts with a digit, it must
630                          *      be a number (or a range).
631                          *
632                          *      Note that ALL integers are unsigned!
633                          */
634                         if (isdigit(*value)) {
635                                 vp->lvalue = atoi(value);
636                                 vp->length = 4;
637                         }
638                         /*
639                          *      Look for the named value for the given
640                          *      attribute.
641                          */
642                         else if ((dval = dict_valbyname(da->attr, value)) == NULL) {
643                                 free(vp);
644                                 librad_log("Unknown value %s for attribute %s",
645                                            value, vp->name);
646                                 return NULL;
647                         }
648                         else {
649                                 vp->lvalue = dval->value;
650                                 vp->length = 4;
651                         }
652                         break;
653
654                 case PW_TYPE_DATE:
655                         if (gettime(value, (time_t *)&vp->lvalue) < 0) {
656                                 free(vp);
657                                 librad_log("failed to parse time string "
658                                            "\"%s\"", value);
659                                 return NULL;
660                         }
661                         vp->length = 4;
662                         break;
663                 case PW_TYPE_ABINARY:
664 #ifdef ASCEND_BINARY
665                         /*
666                          *      Special case to convert filter to binary
667                          */
668                         if ( filterBinary( vp, value ) < 0 ) {
669                           librad_log("failed to parse Ascend binary attribute: %s",
670                                      librad_errstr);
671                           free(vp);
672                           return NULL;
673                         }
674                         break;
675                         /*
676                          *      If Ascend binary is NOT defined,
677                          *      then fall through to raw octets, so that
678                          *      the user can at least make them by hand...
679                          */
680 #endif
681                         /* raw octets: 0x01020304... */
682                 case PW_TYPE_OCTETS:
683                   vp->length = 0;
684                   if (strncasecmp(value, "0x", 2) == 0) {
685                     u_char *us;
686                     cp = value + 2;
687                     us = vp->strvalue;
688                     while (*cp && vp->length < MAX_STRING_LEN) {
689                       unsigned int tmp;
690
691                       if (sscanf(cp, "%02x", &tmp) != 1) break;
692                       cp += 2;
693                       *(us++) = tmp;
694                       vp->length++;
695                     }
696                     *us = '\0';
697                   } else {      /* assume it's a raw string */
698                           vp->length = strlen(value);
699                           if (vp->length >= MAX_STRING_LEN) {
700                                   vp->length = MAX_STRING_LEN - 1;
701                           }
702                   }
703                   break;
704
705                 default:
706                         free(vp);
707                         librad_log("unknown attribute type %d", da->type);
708                         return NULL;
709         }
710         return vp;
711 }
712
713 /*
714  *      Read a valuepair from a buffer, and advance pointer.
715  *      Sets *eol to T_EOL if end of line was encountered.
716  */
717 VALUE_PAIR *pairread(char **ptr, LRAD_TOKEN *eol)
718 {
719         char            buf[64];
720         char            attr[64];
721         char            value[256];
722         char            *p;
723         LRAD_TOKEN      token, t;
724
725         *eol = 0;
726
727         /* Get attribute. */
728         token = gettoken(ptr, attr, sizeof(attr));
729
730         /*  If it's a comment, then exit, as we haven't read a pair */
731         if (token == T_HASH) {
732                 *eol = token;
733                 return NULL;
734         }
735
736         /*  It's not a comment, so it MUST be an attribute */
737         if ((token == T_EOL) ||
738             (attr[0] == 0)) {
739                 librad_log("No token read where we expected an attribute name");
740                 return NULL;
741         }
742
743         /* Now we should have an '=' here. */
744         token = gettoken(ptr, buf, sizeof(buf));
745         if (token < T_EQSTART || token > T_EQEND) {
746                 librad_log("expecting '='");
747                 return NULL;
748         }
749
750         /* Read value.  Note that empty string values are allowed */
751         t = gettoken(ptr, value, sizeof(value));
752         if (t == T_EOL) {
753                 librad_log("failed to get value");
754                 return NULL;
755         }
756
757         /*
758          *      Peek at the next token. Must be T_EOL, T_COMMA, or T_HASH
759          */
760         p = *ptr;
761         t = gettoken(&p, buf, sizeof(buf));
762         if (t != T_EOL && t != T_COMMA && t != T_HASH) {
763                 librad_log("Expected end of line or comma");
764                 return NULL;
765         }
766
767         *eol = t;
768         if (t == T_COMMA) {
769                 *ptr = p;
770         }
771
772         return pairmake(attr, value, token);
773 }
774
775 /*
776  *      Read one line of attribute/value pairs. This might contain
777  *      multiple pairs seperated by comma's.
778  */
779 LRAD_TOKEN userparse(char *buffer, VALUE_PAIR **first_pair)
780 {
781         VALUE_PAIR      *vp;
782         char            *p;
783         LRAD_TOKEN      last_token = T_INVALID;
784         LRAD_TOKEN      previous_token;
785
786         /*
787          *      We allow an empty line.
788          */
789         if (buffer[0] == 0)
790                 return T_EOL;
791
792         p = buffer;
793         do {
794                 previous_token = last_token;
795                 if ((vp = pairread(&p, &last_token)) == NULL) {
796                         return T_INVALID;
797                 }
798                 pairadd(first_pair, vp);
799         } while (*p && (last_token == T_COMMA));
800
801         /*
802          *      Don't tell the caller that there was a comment.
803          */
804         if (last_token == T_HASH) {
805                 return previous_token;
806         }
807
808         /*
809          *      And return the last token which we read.
810          */
811         return last_token;
812 }
813