Initial revision
[freeradius.git] / src / lib / valuepair.c
1 /*
2  * valuepair.c  Functions to handle VALUE_PAIRs
3  *
4  * Version:     @(#)valuepair.c  1.00  19-Jul-1999  miquels@cistron.nl
5  *
6  */
7
8 char valuepair_sccsid[] =
9 "@(#)radius.c   1.00 Copyright 1998-1999 Cistron Internet Services B.V.";
10
11 #include        "autoconf.h"
12
13 #include        <sys/types.h>
14 #include        <sys/time.h>
15
16 #include        <stdio.h>
17 #include        <stdlib.h>
18 #include        <string.h>
19 #include        <time.h>
20 #include        <ctype.h>
21
22 #include        "libradius.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 mathing 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 mathing 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         y = mystrtok(&p, " \t");
335         m = mystrtok(&p, " \t");
336         d = 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 static VALUE_PAIR *pairmake(char *attribute, char *value, int operator)
356 {
357         DICT_ATTR       *da;
358         DICT_VALUE      *dval;
359         VALUE_PAIR      *vp;
360         char            *p, *s;
361
362         if ((da = dict_attrbyname(attribute)) == NULL)
363                 return NULL;
364
365         if ((vp = (VALUE_PAIR *)malloc(sizeof(VALUE_PAIR))) == NULL)
366                 return NULL;
367
368         memset(vp, 0, sizeof(VALUE_PAIR));
369         vp->attribute = da->attr;
370         vp->type = da->type;
371         vp->operator = (operator == 0) ? T_OP_EQ : operator;
372         strcpy(vp->name, da->name);
373
374         /*
375          *      Even for integers, dates and ip addresses we
376          *      keep the original string in vp->strvalue.
377          */
378         strncpy(vp->strvalue, value, MAX_STRING_LEN);
379         vp->strvalue[MAX_STRING_LEN - 1] = 0;
380
381         switch(da->type) {
382                 case PW_TYPE_STRING:
383                         vp->length = strlen(value);
384                         break;
385                 case PW_TYPE_IPADDR:
386                         /*
387                          *      FIXME: complain if hostname
388                          *      cannot be resolved, or resolve later!
389                          */
390                         if ((p = strrchr(value, '+')) != NULL && !p[1]) {
391                                 *p = 0;
392                                 vp->addport = 1;
393                         } else
394                                 p = NULL;
395                         vp->lvalue = librad_dodns ? ip_getaddr(value) :
396                                                     ip_addr(value);
397                         vp->length = 4;
398                         if (p) *p = '+';
399                         break;
400                 case PW_TYPE_INTEGER:
401                         /*
402                          *      For PW_NAS_PORT_ID, allow a
403                          *      port range instead of just a port.
404                          */
405                         if (vp->attribute == PW_NAS_PORT_ID) {
406                                 for(s = value; *s; s++)
407                                         if (!isdigit(*s)) break;
408                                 if (*s) {
409                                         vp->type = PW_TYPE_STRING;
410                                         vp->length = strlen(value);
411                                         break;
412                                 }
413                         }
414                         if (isdigit(*value)) {
415                                 vp->lvalue = atoi(value);
416                                 vp->length = 4;
417                         }
418                         else if ((dval = dict_valbyname(value)) == NULL) {
419                                 free(vp);
420                                 librad_log("unknown value %s", value);
421                                 return NULL;
422                         }
423                         else {
424                                 vp->lvalue = dval->value;
425                                 vp->length = 4;
426                         }
427                         break;
428
429                 case PW_TYPE_DATE:
430                         if ((vp->lvalue = gettime(value)) == (time_t)-1) {
431                                 free(vp);
432                                 return NULL;
433                         }
434                         vp->length = 4;
435                         break;
436                 default:
437                         free(vp);
438                         return NULL;
439         }
440         return vp;
441 }
442
443 /*
444  *      Read a valuepair from a buffer, and advance pointer.
445  *      Sets *eol to 1 if end of line was encountered.
446  */
447 VALUE_PAIR *pairread(char **ptr, int *eol)
448 {
449         char            buf[64];
450         char            attr[64];
451         char            value[256];
452         char            *p;
453         int             token, t;
454
455         *eol = 0;
456
457         /* Get attribute. */
458         gettoken(ptr, attr, sizeof(attr));
459         if (attr[0] == 0)
460                 return NULL;
461
462         /* Now we should have an '=' here. */
463         token = gettoken(ptr, buf, sizeof(buf));
464         if (token < T_EQSTART || token > T_EQEND)
465                 return NULL;
466
467         /* Read value. */
468         gettoken(ptr, value, sizeof(value));
469         if (value[0] == 0)
470                 return NULL;
471
472         /*
473          *      Peek at the next token. Must be T_EOL or T_COMMA.
474          */
475         p = *ptr;
476         t = gettoken(&p, buf, sizeof(buf));
477         if (t != T_EOL && t != T_COMMA)
478                 return NULL;
479
480         if (t == T_COMMA) {
481                 *ptr = p;
482                 /*
483                  *      HACK: should peek again, taking shortcut :)
484                  */
485                 if (*p == 0)
486                         *eol = 1;
487         } else
488                 *eol = 1;
489
490         return pairmake(attr, value, token);
491 }
492
493 /*
494  *      Read one line of attribute/value pairs. This might contain
495  *      multiple pairs seperated by comma's.
496  */
497 int userparse(char *buffer, VALUE_PAIR **first_pair)
498 {
499         VALUE_PAIR      *vp;
500         char            *p;
501         int             eol = 0;
502
503         /*
504          *      We allow an empty line.
505          */
506         if (buffer[0] == 0)
507                 return 0;
508
509         p = buffer;
510         do {
511                 if ((vp = pairread(&p, &eol)) == NULL)
512                         return -1;
513                 pairadd(first_pair, vp);
514         } while (!eol);
515
516         return 0;
517 }
518