9ec3cc290212dd7e1622f286deea8202cdc645c5
[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 #include        <sys/time.h>
14
15 #include        <stdio.h>
16 #include        <stdlib.h>
17 #include        <string.h>
18 #include        <time.h>
19 #include        <ctype.h>
20
21 #include        "libradius.h"
22 #include    "missing.h"
23
24 #if HAVE_MALLOC_H
25 #  include      <malloc.h>
26 #endif
27
28
29 static char *months[] = {
30         "jan", "feb", "mar", "apr", "may", "jun",
31         "jul", "aug", "sep", "oct", "nov", "dec" };
32
33
34 /*
35  *      Create a new valuepair.
36  */
37 VALUE_PAIR *paircreate(int attr, int type)
38 {
39         VALUE_PAIR      *vp;
40         DICT_ATTR       *da;
41
42         if ((vp = malloc(sizeof(VALUE_PAIR))) == NULL)
43                 return NULL;
44         memset(vp, 0, sizeof(VALUE_PAIR));
45         vp->attribute = attr;
46         vp->type = type;
47         if ((da = dict_attrbyvalue(attr)) != NULL)
48                 strcpy(vp->name, da->name);
49         else
50                 sprintf(vp->name, "Attr-%d", attr);
51         switch (vp->type) {
52                 case PW_TYPE_INTEGER:
53                 case PW_TYPE_IPADDR:
54                 case PW_TYPE_DATE:
55                         vp->length = 4;
56                         break;
57                 default:
58                         vp->length = 0;
59                         break;
60         }
61
62         return vp;
63 }
64
65 /*
66  *      Release the memory used by a list of attribute-value
67  *      pairs.
68  */
69 void pairfree(VALUE_PAIR *pair)
70 {
71         VALUE_PAIR      *next;
72
73         while(pair != NULL) {
74                 next = pair->next;
75                 free(pair);
76                 pair = next;
77         }
78 }
79
80
81 /*
82  *      Find the pair with the matching attribute
83  */
84 VALUE_PAIR * pairfind(VALUE_PAIR *first, int attr)
85 {
86         while(first && first->attribute != attr)
87                 first = first->next;
88         return first;
89 }
90
91
92 /*
93  *      Delete the pair(s) with the matching attribute
94  */
95 void pairdelete(VALUE_PAIR **first, int attr)
96 {
97         VALUE_PAIR *i, *next, *last = NULL;
98
99         for(i = *first; i; i = next) {
100                 next = i->next;
101                 if (i->attribute == attr) {
102                         if (last)
103                                 last->next = next;
104                         else
105                                 *first = next;
106                         free(i);
107                 } else
108                         last = i;
109         }
110 }
111
112 /*
113  *      Add a pair at the end of a VALUE_PAIR list.
114  */
115 void pairadd(VALUE_PAIR **first, VALUE_PAIR *new)
116 {
117         VALUE_PAIR *i;
118
119         new->next = NULL;
120         if (*first == NULL) {
121                 *first = new;
122                 return;
123         }
124         for(i = *first; i->next; i = i->next)
125                 ;
126         i->next = new;
127 }
128
129 /*
130  *      Copy just a certain type of pairs.
131  */
132 VALUE_PAIR *paircopy2(VALUE_PAIR *vp, int attr)
133 {
134         VALUE_PAIR      *first, *n, *last;
135
136         first = NULL;
137         last = NULL;
138
139         while (vp) {
140                 if (attr >= 0 && vp->attribute != attr) {
141                         vp = vp->next;
142                         continue;
143                 }
144                 if ((n = (VALUE_PAIR *)malloc(sizeof(VALUE_PAIR))) == NULL)
145                         return first;
146                 memcpy(n, vp, sizeof(VALUE_PAIR));
147                 n->next = NULL;
148                 if (last == NULL)
149                         first = n;
150                 else
151                         last->next = n;
152                 last = n;
153                 vp = vp->next;
154         }
155         return first;
156 }
157
158
159 /*
160  *      Copy a pairlist.
161  */
162 VALUE_PAIR *paircopy(VALUE_PAIR *vp)
163 {
164         return paircopy2(vp, -1);
165 }
166
167
168 /*
169  *      Move attributes from one list to the other
170  *      if not already present.
171  */
172 void pairmove(VALUE_PAIR **to, VALUE_PAIR **from)
173 {
174         VALUE_PAIR *tailto, *i, *next;
175         VALUE_PAIR *tailfrom = NULL;
176         int has_password = 0;
177
178         if (*to == NULL) {
179                 *to = *from;
180                 *from = NULL;
181                 return;
182         }
183
184         /*
185          *      First, see if there are any passwords here, and
186          *      point "tailto" to the end of the "to" list.
187          */
188         tailto = *to;
189         for(i = *to; i; i = i->next) {
190                 if (i->attribute == PW_PASSWORD ||
191                 /*
192                  *      FIXME: this seems to be needed with PAM support
193                  *      to keep it around the Auth-Type = Pam stuff.
194                  *      Perhaps we should only do this if Auth-Type = Pam?
195                  */
196 #ifdef WITH_PAM
197                     i->attribute == PAM_AUTH_ATTR ||
198 #endif
199                     i->attribute == PW_CRYPT_PASSWORD)
200                         has_password = 1;
201                 tailto = i;
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 #ifdef WITH_PAM
217                      i->attribute == PAM_AUTH_ATTR ||
218 #endif
219                      i->attribute == PW_CRYPT_PASSWORD)) {
220                         tailfrom = i;
221                         continue;
222                 }
223                 /*
224                  *      If the attribute is already present in "to",
225                  *      do not move it from "from" to "to". We make
226                  *      an exception for "Hint" which can appear multiple
227                  *      times, and we never move "Fall-Through".
228                  */
229                 if (i->attribute == PW_FALL_THROUGH ||
230                     (i->attribute != PW_HINT && i->attribute != PW_FRAMED_ROUTE
231                      && pairfind(*to, i->attribute) != 0)) {
232                         tailfrom = i;
233                         continue;
234                 }
235                 if (tailfrom)
236                         tailfrom->next = next;
237                 else
238                         *from = next;
239                 tailto->next = i;
240                 i->next = NULL;
241                 tailto = i;
242         }
243 }
244
245 /*
246  *      Move one kind of attributes from one list to the other
247  */
248 void pairmove2(VALUE_PAIR **to, VALUE_PAIR **from, int attr)
249 {
250         VALUE_PAIR *to_tail, *i, *next;
251         VALUE_PAIR *iprev = NULL;
252
253         /*
254          *      Find the last pair in the "to" list and put it in "to_tail".
255          */
256         if (*to != NULL) {
257                 to_tail = *to;
258                 for(i = *to; i; i = i->next)
259                         to_tail = i;
260         } else
261                 to_tail = NULL;
262
263         for(i = *from; i; i = next) {
264                 next = i->next;
265
266                 if (i->attribute != attr) {
267                         iprev = i;
268                         continue;
269                 }
270
271                 /*
272                  *      Remove the attribute from the "from" list.
273                  */
274                 if (iprev)
275                         iprev->next = next;
276                 else
277                         *from = next;
278
279                 /*
280                  *      Add the attribute to the "to" list.
281                  */
282                 if (to_tail)
283                         to_tail->next = i;
284                 else
285                         *to = i;
286                 to_tail = i;
287                 i->next = NULL;
288         }
289 }
290
291
292 /*
293  *      Sort of strtok/strsep function.
294  */
295 static char *mystrtok(char **ptr, char *sep)
296 {
297         char    *res;
298
299         if (**ptr == 0)
300                 return NULL;
301         while (**ptr && strchr(sep, **ptr))
302                 (*ptr)++;
303         if (**ptr == 0)
304                 return NULL;
305         res = *ptr;
306         while (**ptr && strchr(sep, **ptr) == NULL)
307                 (*ptr)++;
308         if (**ptr != 0)
309                 *(*ptr)++ = 0;
310         return res;
311 }
312
313 /*
314  *      Turn printable string into time_t
315  */
316 static time_t gettime(char *valstr)
317 {
318         int             i;
319         time_t          t;
320         struct tm       *tm;
321         char            buf[32];
322         char            *p;
323         char            *y, *m, *d;
324
325         time(&t);
326         tm = localtime(&t);
327
328         strncpy(buf, valstr, sizeof(buf));
329         buf[sizeof(buf) - 1] = 0;
330         for (p = buf; *p; p++)
331                 if (isupper(*p)) *p = tolower(*p);
332
333         p = buf;
334         d = mystrtok(&p, " \t");
335         m = mystrtok(&p, " \t");
336         y = mystrtok(&p, " \t");
337         if (!y || !m || !d) return 0;
338
339         for (i = 0; i < 12; i++) {
340                 if (strncmp(months[i], y, 3) == 0) {
341                         tm->tm_mon = i;
342                         i = 13;
343                 }
344         }
345         tm->tm_mday = atoi(m);
346         tm->tm_year = atoi(y);
347         if (tm->tm_year >= 1900) tm->tm_year -= 1900;
348
349         return mktime(tm);
350 }
351
352 /*
353  *      Create a VALUE_PAIR from an ASCII attribute and value.
354  */
355 VALUE_PAIR *pairmake(char *attribute, char *value, int operator)
356 {
357         DICT_ATTR       *da;
358         DICT_VALUE      *dval;
359         VALUE_PAIR      *vp;
360         u_char          *p, *s;
361
362         if ((da = dict_attrbyname(attribute)) == NULL) {
363                 librad_log("unknown attribute %s", attribute);
364                 return NULL;
365         }
366
367         if ((vp = (VALUE_PAIR *)malloc(sizeof(VALUE_PAIR))) == NULL) {
368                 librad_log("out of memory");
369                 return NULL;
370         }
371
372         memset(vp, 0, sizeof(VALUE_PAIR));
373         vp->attribute = da->attr;
374         vp->type = da->type;
375         vp->operator = (operator == 0) ? T_OP_EQ : operator;
376         strcpy(vp->name, da->name);
377
378         /*
379          *      Even for integers, dates and ip addresses we
380          *      keep the original string in vp->strvalue.
381          */
382         strncpy(vp->strvalue, value, MAX_STRING_LEN);
383         vp->strvalue[MAX_STRING_LEN - 1] = 0;
384
385         switch(da->type) {
386                 case PW_TYPE_STRING:
387                         vp->length = strlen(value);
388                         if (vp->length >= MAX_STRING_LEN) {
389                           vp->length = MAX_STRING_LEN - 1;
390                         }
391                         break;
392                 case PW_TYPE_IPADDR:
393                         /*
394                          *      FIXME: complain if hostname
395                          *      cannot be resolved, or resolve later!
396                          */
397                         if ((p = strrchr(value, '+')) != NULL && !p[1]) {
398                                 *p = 0;
399                                 vp->addport = 1;
400                         } else
401                                 p = NULL;
402                         vp->lvalue = librad_dodns ? ip_getaddr(value) :
403                                                     ip_addr(value);
404                         vp->length = 4;
405                         if (p) *p = '+';
406                         break;
407                 case PW_TYPE_INTEGER:
408                         /*
409                          *      For PW_NAS_PORT_ID, allow a
410                          *      port range instead of just a port.
411                          */
412                         if (vp->attribute == PW_NAS_PORT_ID) {
413                                 for(s = value; *s; s++)
414                                         if (!isdigit(*s)) break;
415                                 if (*s) {
416                                         vp->type = PW_TYPE_STRING;
417                                         vp->length = strlen(value);
418                                         break;
419                                 }
420                         }
421                         if (isdigit(*value)) {
422                                 vp->lvalue = atoi(value);
423                                 vp->length = 4;
424                         }
425                         else if ((dval = dict_valbyname(value)) == NULL) {
426                                 free(vp);
427                                 librad_log("unknown value %s", value);
428                                 return NULL;
429                         }
430                         else {
431                                 vp->lvalue = dval->value;
432                                 vp->length = 4;
433                         }
434                         break;
435
436                 case PW_TYPE_DATE:
437                         if ((vp->lvalue = gettime(value)) == (time_t)-1) {
438                                 free(vp);
439                                 librad_log("failed to get time");
440                                 return NULL;
441                         }
442                         vp->length = 4;
443                         break;
444                 case PW_TYPE_ABINARY:
445 #ifdef ASCEND_BINARY
446                         /*
447                          * special case to convert filter to binary
448                          */
449                         if ( filterBinary( vp, value ) < 0 ) {
450                           librad_log("failed to parse Ascend binary attribute: %s",
451                                      librad_errstr);
452                           free(vp);
453                           return NULL;
454                         }
455                         break;
456
457 #endif
458                         /* raw octets: 0x01020304... */
459                 case PW_TYPE_OCTETS:
460                   vp->length = 0;
461                   if (strncasecmp(value, "0x", 2) == 0) {
462                     p = value + 2;
463                     s = vp->strvalue;
464                     while (*p && vp->length < MAX_STRING_LEN) {
465                       unsigned int tmp;
466
467                       if (sscanf(p, "%02x", &tmp) != 1) break;
468                       p += 2;
469                       *(s++) = tmp;
470                       vp->length++;
471                     }
472                     *s = '\0';
473                   }
474                   break;
475
476                 default:
477                         free(vp);
478                         librad_log("unknown attribute type %d", da->type);
479                         return NULL;
480         }
481         return vp;
482 }
483
484 /*
485  *      Read a valuepair from a buffer, and advance pointer.
486  *      Sets *eol to 1 if end of line was encountered.
487  */
488 VALUE_PAIR *pairread(char **ptr, int *eol)
489 {
490         char            buf[64];
491         char            attr[64];
492         char            value[256];
493         char            *p;
494         int             token, t;
495
496         *eol = 0;
497
498         /* Get attribute. */
499         gettoken(ptr, attr, sizeof(attr));
500         if (attr[0] == 0) {
501                 librad_log("No token read");
502                 return NULL;
503         }
504
505         /* Now we should have an '=' here. */
506         token = gettoken(ptr, buf, sizeof(buf));
507         if (token < T_EQSTART || token > T_EQEND) {
508                 librad_log("expecting '='");
509                 return NULL;
510         }
511
512         /* Read value. */
513         gettoken(ptr, value, sizeof(value));
514         if (value[0] == 0) {
515                 librad_log("failed to get value");
516                 return NULL;
517         }
518
519         /*
520          *      Peek at the next token. Must be T_EOL or T_COMMA.
521          */
522         p = *ptr;
523         t = gettoken(&p, buf, sizeof(buf));
524         if (t != T_EOL && t != T_COMMA) {
525                 librad_log("Expected end of line or comma");
526                 return NULL;
527         }
528
529         if (t == T_COMMA) {
530                 *ptr = p;
531                 /*
532                  *      HACK: should peek again, taking shortcut :)
533                  */
534                 if (*p == 0)
535                         *eol = 1;
536         } else {
537                 *eol = 1;
538         }
539
540         return pairmake(attr, value, token);
541 }
542
543 /*
544  *      Read one line of attribute/value pairs. This might contain
545  *      multiple pairs seperated by comma's.
546  */
547 int userparse(char *buffer, VALUE_PAIR **first_pair)
548 {
549         VALUE_PAIR      *vp;
550         char            *p;
551         int             eol = 0;
552
553         /*
554          *      We allow an empty line.
555          */
556         if (buffer[0] == 0)
557                 return 0;
558
559         p = buffer;
560         do {
561                 if ((vp = pairread(&p, &eol)) == NULL)
562                         return -1;
563                 pairadd(first_pair, vp);
564         } while (!eol);
565
566         return 0;
567 }
568