import from branch_1_1:
[freeradius.git] / src / lib / dict.c
1 /*
2  * dict.c       Routines to read the dictionary file.
3  *
4  * Version:     $Id$
5  *
6  *   This library is free software; you can redistribute it and/or
7  *   modify it under the terms of the GNU Lesser General Public
8  *   License as published by the Free Software Foundation; either
9  *   version 2.1 of the License, or (at your option) any later version.
10  *
11  *   This library is distributed in the hope that it will be useful,
12  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14  *   Lesser General Public License for more details.
15  *
16  *   You should have received a copy of the GNU Lesser General Public
17  *   License along with this library; if not, write to the Free Software
18  *   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  *
20  * Copyright 2000,2006  The FreeRADIUS server project
21  */
22
23 #include        <freeradius-devel/ident.h>
24 RCSID("$Id$")
25
26 #include        <freeradius-devel/autoconf.h>
27
28 #include        <stdlib.h>
29 #include        <ctype.h>
30 #include        <string.h>
31
32 #ifdef HAVE_MALLOC_H
33 #include        <malloc.h>
34 #endif
35
36 #ifdef HAVE_SYS_STAT_H
37 #include        <sys/stat.h>
38 #endif
39
40 #include        <unistd.h>
41
42 #include        <freeradius-devel/missing.h>
43 #include        <freeradius-devel/libradius.h>
44
45 #define DICT_VALUE_MAX_NAME_LEN (128)
46 #define DICT_VENDOR_MAX_NAME_LEN (128)
47
48 static lrad_hash_table_t *vendors_byname = NULL;
49 static lrad_hash_table_t *vendors_byvalue = NULL;
50
51 static lrad_hash_table_t *attributes_byname = NULL;
52 static lrad_hash_table_t *attributes_byvalue = NULL;
53
54 static lrad_hash_table_t *values_byvalue = NULL;
55 static lrad_hash_table_t *values_byname = NULL;
56
57 /*
58  *      For faster HUP's, we cache the stat information for
59  *      files we've $INCLUDEd
60  */
61 typedef struct dict_stat_t {
62         struct dict_stat_t *next;
63         char               *name;
64         time_t             mtime;
65 } dict_stat_t;
66
67 static char *stat_root_dir = NULL;
68 static char *stat_root_file = NULL;
69
70 static dict_stat_t *stat_head = NULL;
71 static dict_stat_t *stat_tail = NULL;
72
73 typedef struct value_fixup_t {
74         char            attrstr[40];
75         DICT_VALUE      *dval;
76         struct value_fixup_t *next;
77 } value_fixup_t;
78
79
80 /*
81  *      So VALUEs in the dictionary can have forward references.
82  */
83 static value_fixup_t *value_fixup = NULL;
84
85 static const LRAD_NAME_NUMBER type_table[] = {
86         { "string",     PW_TYPE_STRING },
87         { "integer",    PW_TYPE_INTEGER },
88         { "ipaddr",     PW_TYPE_IPADDR },
89         { "date",       PW_TYPE_DATE },
90         { "abinary",    PW_TYPE_ABINARY },
91         { "octets",     PW_TYPE_OCTETS },
92         { "ifid",       PW_TYPE_IFID },
93         { "ipv6addr",   PW_TYPE_IPV6ADDR },
94         { "ipv6prefix", PW_TYPE_IPV6PREFIX },
95         { "byte",       PW_TYPE_BYTE },
96         { "short",      PW_TYPE_SHORT },
97         { NULL, 0 }
98 };
99
100
101 /*
102  *      Create the hash of the name.
103  *
104  *      We copy the hash function here because it's substantially faster.
105  */
106 #define FNV_MAGIC_INIT (0x811c9dc5)
107 #define FNV_MAGIC_PRIME (0x01000193)
108
109 static uint32_t dict_hashname(const char *name)
110 {
111         uint32_t hash = FNV_MAGIC_INIT;
112         const char *p;
113
114         for (p = name; *p != '\0'; p++) {
115                 int c = *(const unsigned char *) p;
116                 if (isalpha(c)) c = tolower(c);
117
118                 hash *= FNV_MAGIC_PRIME;
119                 hash ^= (uint32_t ) (c & 0xff);
120         }
121         
122         return hash;
123 }
124
125
126 /*
127  *      Hash callback functions.
128  */
129 static uint32_t dict_attr_name_hash(const void *data)
130 {
131         return dict_hashname(((const DICT_ATTR *)data)->name);
132 }
133
134 static int dict_attr_name_cmp(const void *one, const void *two)
135 {
136         const DICT_ATTR *a = one;
137         const DICT_ATTR *b = two;
138
139         return strcasecmp(a->name, b->name);
140 }
141
142 static uint32_t dict_attr_value_hash(const void *data)
143 {
144         uint32_t hash;
145         const DICT_ATTR *attr = data;
146
147         hash = lrad_hash(&attr->vendor, sizeof(attr->vendor));
148         return lrad_hash_update(&attr->attr, sizeof(attr->attr), hash);
149 }
150
151 static int dict_attr_value_cmp(const void *one, const void *two)
152 {
153         const DICT_ATTR *a = one;
154         const DICT_ATTR *b = two;
155
156         if (a->vendor < b->vendor) return -1;
157         if (a->vendor > b->vendor) return +1;
158
159         return a->attr - b->attr;
160 }
161
162 static uint32_t dict_vendor_name_hash(const void *data)
163 {
164         return dict_hashname(((const DICT_VENDOR *)data)->name);
165 }
166
167 static int dict_vendor_name_cmp(const void *one, const void *two)
168 {
169         const DICT_VENDOR *a = one;
170         const DICT_VENDOR *b = two;
171
172         return strcasecmp(a->name, b->name);
173 }
174
175 static uint32_t dict_vendor_value_hash(const void *data)
176 {
177         return lrad_hash(&(((const DICT_VENDOR *)data)->vendorpec),
178                          sizeof(((const DICT_VENDOR *)data)->vendorpec));
179 }
180
181 static int dict_vendor_value_cmp(const void *one, const void *two)
182 {
183         const DICT_VENDOR *a = one;
184         const DICT_VENDOR *b = two;
185
186         return a->vendorpec - b->vendorpec;
187 }
188
189 static uint32_t dict_value_name_hash(const void *data)
190 {
191         uint32_t hash;
192         const DICT_VALUE *dval = data;
193
194         hash = dict_hashname(dval->name);
195         return lrad_hash_update(&dval->attr, sizeof(dval->attr), hash);
196 }
197
198 static int dict_value_name_cmp(const void *one, const void *two)
199 {
200         int rcode;
201         const DICT_VALUE *a = one;
202         const DICT_VALUE *b = two;
203
204         rcode = a->attr - b->attr;
205         if (rcode != 0) return rcode;
206
207         return strcasecmp(a->name, b->name);
208 }
209
210 static uint32_t dict_value_value_hash(const void *data)
211 {
212         uint32_t hash;
213         const DICT_VALUE *dval = data;
214
215         hash = lrad_hash(&dval->attr, sizeof(dval->attr));
216         return lrad_hash_update(&dval->value, sizeof(dval->value), hash);
217 }
218
219 static int dict_value_value_cmp(const void *one, const void *two)
220 {
221         int rcode;
222         const DICT_VALUE *a = one;
223         const DICT_VALUE *b = two;
224
225         rcode = a->attr - b->attr;
226         if (rcode != 0) return rcode;
227
228         return a->value - b->value;
229 }
230
231
232 /*
233  *      Free the list of stat buffers
234  */
235 static void dict_stat_free(void)
236 {
237         dict_stat_t *this, *next;
238
239         free(stat_root_dir);
240         stat_root_dir = NULL;
241         free(stat_root_file);
242         stat_root_file = NULL;
243
244         if (!stat_head) {
245                 stat_tail = NULL;
246                 return;
247         }
248
249         for (this = stat_head; this != NULL; this = next) {
250                 next = this->next;
251                 free(this->name);
252                 free(this);
253         }
254
255         stat_head = stat_tail = NULL;
256 }
257
258
259 /*
260  *      Add an entry to the list of stat buffers.
261  */
262 static void dict_stat_add(const char *name, const struct stat *stat_buf)
263 {
264         dict_stat_t *this;
265
266         this = malloc(sizeof(*this));
267         if (!this) return;
268         memset(this, 0, sizeof(*this));
269
270         this->name = strdup(name);
271         this->mtime = stat_buf->st_mtime;
272
273         if (!stat_head) {
274                 stat_head = stat_tail = this;
275         } else {
276                 stat_tail->next = this;
277                 stat_tail = this;
278         }
279 }
280
281
282 /*
283  *      See if any dictionaries have changed.  If not, don't
284  *      do anything.
285  */
286 static int dict_stat_check(const char *root_dir, const char *root_file)
287 {
288         struct stat buf;
289         dict_stat_t *this;
290
291         if (!stat_root_dir) return 0;
292         if (!stat_root_file) return 0;
293
294         if (strcmp(root_dir, stat_root_dir) != 0) return 0;
295         if (strcmp(root_file, stat_root_file) != 0) return 0;
296
297         if (!stat_head) return 0; /* changed, reload */
298
299         for (this = stat_head; this != NULL; this = this->next) {
300                 if (stat(this->name, &buf) < 0) return 0;
301
302                 if (buf.st_mtime != this->mtime) return 0;
303         }
304
305         return 1;
306 }
307
308
309 /*
310  *      Free the dictionary_attributes and dictionary_values lists.
311  */
312 void dict_free(void)
313 {
314         /*
315          *      Free the tables
316          */
317         lrad_hash_table_free(vendors_byname);
318         lrad_hash_table_free(vendors_byvalue);
319         vendors_byname = NULL;
320         vendors_byvalue = NULL;
321
322         lrad_hash_table_free(attributes_byname);
323         lrad_hash_table_free(attributes_byvalue);
324         attributes_byname = NULL;
325         attributes_byvalue = NULL;
326
327         lrad_hash_table_free(values_byname);
328         lrad_hash_table_free(values_byvalue);
329         values_byname = NULL;
330         values_byvalue = NULL;
331
332         dict_stat_free();
333 }
334
335
336 /*
337  *      Add vendor to the list.
338  */
339 int dict_addvendor(const char *name, int value)
340 {
341         size_t length;
342         DICT_VENDOR *dv;
343
344         if (value >= 32767) {
345                 librad_log("dict_addvendor: Cannot handle vendor ID larger than 65535");
346                 return -1;
347         }
348
349         if ((length = strlen(name)) >= DICT_VENDOR_MAX_NAME_LEN) {
350                 librad_log("dict_addvendor: vendor name too long");
351                 return -1;
352         }
353         
354         if ((dv = malloc(sizeof(*dv) + length)) == NULL) {
355                 librad_log("dict_addvendor: out of memory");
356                 return -1;
357         }
358
359         strcpy(dv->name, name);
360         dv->vendorpec  = value;
361         dv->type = dv->length = 1; /* defaults */
362
363         if (!lrad_hash_table_insert(vendors_byname, dv)) {
364                 DICT_VENDOR *old_dv;
365
366                 old_dv = lrad_hash_table_finddata(vendors_byname, dv);
367                 if (!old_dv) {
368                         librad_log("dict_addvendor: Failed inserting vendor name %s", name);
369                         return -1;
370                 }
371                 if (old_dv->vendorpec != dv->vendorpec) {
372                         librad_log("dict_addvendor: Duplicate vendor name %s", name);
373                         return -1;
374                 }
375
376                 /*
377                  *      Already inserted.  Discard the duplicate entry.
378                  */
379                 free(dv);
380                 return 0;
381         }
382
383         /*
384          *      Insert the SAME pointer (not free'd when this table is
385          *      deleted), into another table.
386          *
387          *      We want this behaviour because we want OLD names for
388          *      the attributes to be read from the configuration
389          *      files, but when we're printing them, (and looking up
390          *      by value) we want to use the NEW name.
391          */
392         if (!lrad_hash_table_replace(vendors_byvalue, dv)) {
393                 librad_log("dict_addvendor: Failed inserting vendor %s",
394                            name);
395                 return -1;
396         }
397
398         return 0;
399 }
400
401 /*
402  *      Add an attribute to the dictionary.
403  */
404 int dict_addattr(const char *name, int vendor, int type, int value,
405                  ATTR_FLAGS flags)
406 {
407         static int      max_attr = 0;
408         DICT_ATTR       *attr;
409
410         if (strlen(name) > (sizeof(attr->name) -1)) {
411                 librad_log("dict_addattr: attribute name too long");
412                 return -1;
413         }
414
415         /*
416          *      If the value is '-1', that means use a pre-existing
417          *      one (if it already exists).  If one does NOT already exist,
418          *      then create a new attribute, with a non-conflicting value,
419          *      and use that.
420          */
421         if (value == -1) {
422                 if (dict_attrbyname(name)) {
423                         return 0; /* exists, don't add it again */
424                 }
425
426                 value = ++max_attr;
427
428         } else if (vendor == 0) {
429                 /*
430                  *  Update 'max_attr'
431                  */
432                 if (value > max_attr) {
433                         max_attr = value;
434                 }
435         }
436
437         if (value < 0) {
438                 librad_log("dict_addattr: ATTRIBUTE has invalid number (less than zero)");
439                 return -1;
440         }
441
442         if (value >= 65536) {
443                 librad_log("dict_addattr: ATTRIBUTE has invalid number (larger than 65535).");
444                 return -1;
445         }
446
447         if (vendor) {
448                 DICT_VENDOR *dv = dict_vendorbyvalue(vendor);
449
450                 /*
451                  *      If the vendor isn't defined, die/
452                  */
453                 if (!dv) {
454                         librad_log("dict_addattr: Unknown vendor");
455                         return -1;
456                 }
457
458                 /*
459                  *      FIXME: Switch over dv->type, and limit things
460                  *      properly.
461                  */
462                 if ((dv->type == 1) && (value >= 256)) {
463                         librad_log("dict_addattr: ATTRIBUTE has invalid number (larger than 255).");
464                         return -1;
465                 } /* else 256..65535 are allowed */
466         }
467
468         /*
469          *      Create a new attribute for the list
470          */
471         if ((attr = malloc(sizeof(*attr))) == NULL) {
472                 librad_log("dict_addattr: out of memory");
473                 return -1;
474         }
475
476         strcpy(attr->name, name);
477         attr->attr = value;
478         attr->attr |= (vendor << 16); /* FIXME: hack */
479         attr->vendor = vendor;
480         attr->type = type;
481         attr->flags = flags;
482         attr->vendor = vendor;
483
484         /*
485          *      Insert the attribute, only if it's not a duplicate.
486          */
487         if (!lrad_hash_table_insert(attributes_byname, attr)) {
488                 DICT_ATTR       *a;
489
490                 /*
491                  *      If the attribute has identical number, then
492                  *      ignore the duplicate.
493                  */
494                 a = lrad_hash_table_finddata(attributes_byname, attr);
495                 if (a && (strcasecmp(a->name, attr->name) == 0)) {
496                         if (a->attr != attr->attr) {
497                                 librad_log("dict_addattr: Duplicate attribute name %s", name);
498                                 free(attr);
499                                 return -1;
500                         }
501
502                         /*
503                          *      Same name, same vendor, same attr,
504                          *      maybe the flags and/or type is
505                          *      different.  Let the new value
506                          *      over-ride the old one.
507                          */
508                 }
509
510
511                 lrad_hash_table_delete(attributes_byvalue, a);
512
513                 if (!lrad_hash_table_replace(attributes_byname, attr)) {
514                         librad_log("dict_addattr: Internal error storing attribute %s", name);
515                         free(attr);
516                         return -1;
517                 }
518         }
519
520         /*
521          *      Insert the SAME pointer (not free'd when this entry is
522          *      deleted), into another table.
523          *
524          *      We want this behaviour because we want OLD names for
525          *      the attributes to be read from the configuration
526          *      files, but when we're printing them, (and looking up
527          *      by value) we want to use the NEW name.
528          */
529         if (!lrad_hash_table_replace(attributes_byvalue, attr)) {
530                 librad_log("dict_addattr: Failed inserting attribute name %s", name);
531                 return -1;
532         }
533
534         return 0;
535 }
536
537
538 /*
539  *      Add a value for an attribute to the dictionary.
540  */
541 int dict_addvalue(const char *namestr, const char *attrstr, int value)
542 {
543         size_t          length;
544         DICT_ATTR       *dattr;
545         DICT_VALUE      *dval;
546
547         if (!*namestr) {
548                 librad_log("dict_addvalue: empty names are not permitted");
549                 return -1;
550         }
551
552         if ((length = strlen(namestr)) >= DICT_VALUE_MAX_NAME_LEN) {
553                 librad_log("dict_addvalue: value name too long");
554                 return -1;
555         }
556
557         if ((dval = malloc(sizeof(*dval) + length)) == NULL) {
558                 librad_log("dict_addvalue: out of memory");
559                 return -1;
560         }
561         memset(dval, 0, sizeof(*dval));
562
563         strcpy(dval->name, namestr);
564         dval->value = value;
565
566         /*
567          *      Remember which attribute is associated with this
568          *      value, if possible.
569          */
570         dattr = dict_attrbyname(attrstr);
571         if (dattr) {
572                 if (dattr->flags.has_value_alias) {
573                         librad_log("dict_addvalue: Cannot add VALUE for ATTRIBUTE \"%s\": It already has a VALUE-ALIAS", attrstr);
574                         return -1;
575                 }
576
577                 dval->attr = dattr->attr;
578
579                 /*
580                  *      Enforce valid values
581                  *
582                  *      Don't worry about fixups...
583                  */
584                 switch (dattr->type) {
585                         case PW_TYPE_BYTE:
586                                 if (value > 255) {
587                                         librad_log("dict_addvalue: ATTRIBUTEs of type 'byte' cannot have VALUEs larger than 255");
588                                         return -1;
589                                 }
590                                 break;
591                         case PW_TYPE_SHORT:
592                                 if (value > 65535) {
593                                         librad_log("dict_addvalue: ATTRIBUTEs of type 'short' cannot have VALUEs larger than 65535");
594                                         return -1;
595                                 }
596                                 break;
597
598                                 /*
599                                  *      Allow octets for now, because
600                                  *      of dictionary.cablelabs
601                                  */
602                         case PW_TYPE_OCTETS:
603
604                         case PW_TYPE_INTEGER:
605                                 break;
606
607                         default:
608                                 librad_log("dict_addvalue: VALUEs cannot be defined for attributes of type '%s'",
609                                            lrad_int2str(type_table, dattr->type, "?Unknown?"));
610                                 return -1;
611                 }
612
613                 dattr->flags.has_value = 1;
614         } else {
615                 value_fixup_t *fixup;
616                 
617                 fixup = (value_fixup_t *) malloc(sizeof(*fixup));
618                 if (!fixup) {
619                         librad_log("dict_addvalue: out of memory");
620                         return -1;
621                 }
622                 memset(fixup, 0, sizeof(*fixup));
623
624                 strlcpy(fixup->attrstr, attrstr, sizeof(fixup->attrstr));
625                 fixup->dval = dval;
626                 
627                 /*
628                  *      Insert to the head of the list.
629                  */
630                 fixup->next = value_fixup;
631                 value_fixup = fixup;
632
633                 return 0;
634         }
635
636         /*
637          *      Add the value into the dictionary.
638          */
639         if (!lrad_hash_table_insert(values_byname, dval)) {
640                 if (dattr) {
641                         DICT_VALUE *old;
642                         
643                         /*
644                          *      Suppress duplicates with the same
645                          *      name and value.  There are lots in
646                          *      dictionary.ascend.
647                          */
648                         old = dict_valbyname(dattr->attr, namestr);
649                         if (old && (old->value == dval->value)) {
650                                 free(dval);
651                                 return 0;
652                         }
653                 }
654
655                 librad_log("dict_addvalue: Duplicate value name %s for attribute %s", namestr, attrstr);
656                 return -1;
657         }
658
659         /*
660          *      There are multiple VALUE's, keyed by attribute, so we
661          *      take care of that here.
662          */
663         if (!lrad_hash_table_replace(values_byvalue, dval)) {
664                 librad_log("dict_addvalue: Failed inserting value %s",
665                            namestr);
666                 return -1;
667         }
668
669         return 0;
670 }
671
672 /*
673  *      Process the ATTRIBUTE command
674  */
675 static int process_attribute(const char* fn, const int line,
676                              const int block_vendor, char **argv,
677                              int argc)
678 {
679         int             vendor = 0;
680         int             value;
681         int             type;
682         char            *s, *c;
683         ATTR_FLAGS      flags;
684
685         if ((argc < 3) || (argc > 4)) {
686                 librad_log("dict_init: %s[%d]: invalid ATTRIBUTE line",
687                         fn, line);
688                 return -1;
689         }
690
691         /*
692          *      Validate all entries
693          */
694         if (!isdigit((int) argv[1][0])) {
695                 librad_log("dict_init: %s[%d]: invalid value", fn, line);
696                 return -1;
697         }
698         sscanf(argv[1], "%i", &value);
699
700         /*
701          *      find the type of the attribute.
702          */
703         type = lrad_str2int(type_table, argv[2], -1);
704         if (type < 0) {
705                 librad_log("dict_init: %s[%d]: invalid type \"%s\"",
706                         fn, line, argv[2]);
707                 return -1;
708         }
709
710         /*
711          *      Only look up the vendor if the string
712          *      is non-empty.
713          */
714         memset(&flags, 0, sizeof(flags));
715         if (argc == 4) {
716                 /*
717                  *      FIXME: replace strtok with str2argv
718                  */
719                 s = strtok(argv[3], ",");
720                 while (s) {
721                         if (strcmp(s, "has_tag") == 0 ||
722                             strcmp(s, "has_tag=1") == 0) {
723                                 /* Boolean flag, means this is a
724                                    tagged attribute */
725                                 flags.has_tag = 1;
726                                 
727                         } else if (strncmp(s, "encrypt=", 8) == 0) {
728                                 /* Encryption method, defaults to 0 (none).
729                                    Currently valid is just type 2,
730                                    Tunnel-Password style, which can only
731                                    be applied to strings. */
732                                 flags.encrypt = strtol(s + 8, &c, 0);
733                                 if (*c) {
734                                         librad_log( "dict_init: %s[%d] invalid option %s",
735                                                     fn, line, s);
736                                         return -1;
737                                 }
738
739                         } else if (strncmp(s, "array", 8) == 0) {
740                                 flags.array = 1;
741
742                                 switch (type) {
743                                         case PW_TYPE_IPADDR:
744                                         case PW_TYPE_BYTE:
745                                         case PW_TYPE_SHORT:
746                                         case PW_TYPE_INTEGER:
747                                         case PW_TYPE_DATE:
748                                                 break;
749                                         
750                                         default:
751                                                 librad_log( "dict_init: %s[%d] Only IP addresses can have the \"array\" flag set.",
752                                                             fn, line);
753                                                 return -1;
754                                 }
755
756                         } else if (block_vendor) {
757                                 librad_log( "dict_init: %s[%d]: unknown option \"%s\"",
758                                             fn, line, s);
759                                 return -1;
760
761                         } else {
762                                 /* Must be a vendor 'flag'... */
763                                 if (strncmp(s, "vendor=", 7) == 0) {
764                                         /* New format */
765                                         s += 7;
766                                 }
767                                 
768                                 vendor = dict_vendorbyname(s);
769                                 if (!vendor) {
770                                         librad_log( "dict_init: %s[%d]: unknown vendor \"%s\"",
771                                                     fn, line, s);
772                                         return -1;
773                                 }
774                                 if (block_vendor && argv[3][0] &&
775                                     (block_vendor != vendor)) {
776                                         librad_log("dict_init: %s[%d]: mismatched vendor %s within BEGIN-VENDOR/END-VENDOR block",
777                                                    fn, line, argv[3]);
778                                         return -1;
779                                 }
780                         }
781                         s = strtok(NULL, ",");
782                 }
783         }
784
785         if (block_vendor) vendor = block_vendor;
786
787         /*
788          *      Special checks for tags, they make our life much more
789          *      difficult.
790          */
791         if (flags.has_tag) {
792                 /*
793                  *      Only string, octets, and integer can be tagged.
794                  */
795                 switch (type) {
796                 case PW_TYPE_STRING:
797                 case PW_TYPE_INTEGER:
798                         break;
799
800                 default:
801                         librad_log("dict_init: %s[%d]: Attributes of type %s cannot be tagged.",
802                                    fn, line,
803                                    lrad_int2str(type_table, type, "?Unknown?"));
804                         return -1;
805                         
806                 }
807         }
808
809         /*
810          *      Add it in.
811          */
812         if (dict_addattr(argv[0], vendor, type, value, flags) < 0) {
813                 librad_log("dict_init: %s[%d]: %s",
814                            fn, line, librad_errstr);
815                 return -1;
816         }
817
818         return 0;
819 }
820
821
822 /*
823  *      Process the VALUE command
824  */
825 static int process_value(const char* fn, const int line, char **argv,
826                          int argc)
827 {
828         int     value;
829
830         if (argc != 3) {
831                 librad_log("dict_init: %s[%d]: invalid VALUE line",
832                         fn, line);
833                 return -1;
834         }
835         /*
836          *      For Compatibility, skip "Server-Config"
837          */
838         if (strcasecmp(argv[0], "Server-Config") == 0)
839                 return 0;
840
841         /*
842          *      Validate all entries
843          */
844         if (!isdigit((int) argv[2][0])) {
845                 librad_log("dict_init: %s[%d]: invalid value",
846                         fn, line);
847                 return -1;
848         }
849         sscanf(argv[2], "%i", &value);
850
851         if (dict_addvalue(argv[1], argv[0], value) < 0) {
852                 librad_log("dict_init: %s[%d]: %s",
853                            fn, line, librad_errstr);
854                 return -1;
855         }
856
857         return 0;
858 }
859
860
861 /*
862  *      Process the VALUE-ALIAS command
863  *
864  *      This allows VALUE mappings to be shared among multiple
865  *      attributes.
866  */
867 static int process_value_alias(const char* fn, const int line, char **argv,
868                                int argc)
869 {
870         DICT_ATTR *my_da, *da;
871         DICT_VALUE *dval;
872
873         if (argc != 2) {
874                 librad_log("dict_init: %s[%d]: invalid VALUE-ALIAS line",
875                         fn, line);
876                 return -1;
877         }
878
879         my_da = dict_attrbyname(argv[0]);
880         if (!my_da) {
881                 librad_log("dict_init: %s[%d]: ATTRIBUTE \"%s\" does not exist",
882                            fn, line, argv[1]);
883                 return -1;
884         }
885
886         if (my_da->flags.has_value) {
887                 librad_log("dict_init: %s[%d]: Cannot add VALUE-ALIAS to ATTRIBUTE \"%s\" with pre-existing VALUE",
888                            fn, line, argv[0]);
889                 return -1;
890         }
891
892         if (my_da->flags.has_value_alias) {
893                 librad_log("dict_init: %s[%d]: Cannot add VALUE-ALIAS to ATTRIBUTE \"%s\" with pre-existing VALUE-ALIAS",
894                            fn, line, argv[0]);
895                 return -1;
896         }
897
898         da = dict_attrbyname(argv[1]);
899         if (!da) {
900                 librad_log("dict_init: %s[%d]: Cannot find ATTRIBUTE \"%s\" for alias",
901                            fn, line, argv[1]);
902                 return -1;
903         }
904
905         if (!da->flags.has_value) {
906                 librad_log("dict_init: %s[%d]: VALUE-ALIAS cannot refer to ATTRIBUTE %s: It has no values",
907                            fn, line, argv[1]);
908                 return -1;
909         }
910
911         if (da->flags.has_value_alias) {
912                 librad_log("dict_init: %s[%d]: Cannot add VALUE-ALIAS to ATTRIBUTE \"%s\" which itself has a VALUE-ALIAS",
913                            fn, line, argv[1]);
914                 return -1;
915         }
916
917         if (my_da->type != da->type) {
918                 librad_log("dict_init: %s[%d]: Cannot add VALUE-ALIAS between attributes of differing type",
919                            fn, line);
920                 return -1;
921         }
922
923         if ((dval = malloc(sizeof(*dval))) == NULL) {
924                 librad_log("dict_addvalue: out of memory");
925                 return -1;
926         }
927         memset(dval, 0, sizeof(*dval));
928
929         dval->name[0] = '\0';   /* empty name */
930         dval->attr = my_da->attr;
931         dval->value = da->attr;
932
933         if (!lrad_hash_table_insert(values_byname, dval)) {
934                 librad_log("dict_init: %s[%d]: Error create alias",
935                            fn, line);
936                 free(dval);
937                 return -1;
938         }
939
940         return 0;
941 }
942
943
944 /*
945  *      Process the VENDOR command
946  */
947 static int process_vendor(const char* fn, const int line, char **argv,
948                           int argc)
949 {
950         int     value;
951         const   char *format = NULL;
952
953         if ((argc < 2) || (argc > 3)) {
954                 librad_log( "dict_init: %s[%d] invalid VENDOR entry",
955                             fn, line);
956                 return -1;
957         }
958
959         /*
960          *       Validate all entries
961          */
962         if (!isdigit((int) argv[1][0])) {
963                 librad_log("dict_init: %s[%d]: invalid value",
964                         fn, line);
965                 return -1;
966         }
967         value = atoi(argv[1]);
968
969         /* Create a new VENDOR entry for the list */
970         if (dict_addvendor(argv[0], value) < 0) {
971                 librad_log("dict_init: %s[%d]: %s",
972                            fn, line, librad_errstr);
973                 return -1;
974         }
975
976         /*
977          *      Look for a format statement
978          */
979         if (argc == 3) {
980                 format = argv[2];
981
982         } else if (value == VENDORPEC_USR) { /* catch dictionary screw-ups */
983                 format = "format=4,0";
984
985         } else if (value == VENDORPEC_LUCENT) {
986                 format = "format=2,1";
987
988         } else if (value == VENDORPEC_STARENT) {
989                 format = "format=2,2";
990
991         } /* else no fixups to do */
992
993         if (format) {
994                 int type, length;
995                 const char *p;
996                 DICT_VENDOR *dv;
997
998                 if (strncasecmp(format, "format=", 7) != 0) {
999                         librad_log("dict_init: %s[%d]: Invalid format for VENDOR.  Expected \"format=\", got \"%s\"",
1000                                    fn, line, format);
1001                         return -1;
1002                 }
1003
1004                 p = format + 7;
1005                 if ((strlen(p) != 3) || 
1006                     !isdigit((int) p[0]) ||
1007                     (p[1] != ',') ||
1008                     !isdigit((int) p[2])) {
1009                         librad_log("dict_init: %s[%d]: Invalid format for VENDOR.  Expected text like \"1,1\", got \"%s\"",
1010                                    fn, line, p);
1011                         return -1;
1012                 }
1013
1014                 type = (int) (p[0] - '0');
1015                 length = (int) (p[2] - '0');
1016
1017                 dv = dict_vendorbyvalue(value);
1018                 if (!dv) {
1019                         librad_log("dict_init: %s[%d]: Failed adding format for VENDOR",
1020                                    fn, line);
1021                         return -1;
1022                 }
1023
1024                 if ((type != 1) && (type != 2) && (type != 4)) {
1025                         librad_log("dict_init: %s[%d]: invalid type value %d for VENDOR",
1026                                    fn, line, type);
1027                         return -1;
1028                 }
1029
1030                 if ((length != 0) && (length != 1) && (length != 2)) {
1031                         librad_log("dict_init: %s[%d]: invalid length value %d for VENDOR",
1032                                    fn, line, length);
1033                         return -1;
1034                 }
1035
1036                 dv->type = type;
1037                 dv->length = length;
1038         }
1039
1040         return 0;
1041 }
1042
1043 /*
1044  *      String split routine.  Splits an input string IN PLACE
1045  *      into pieces, based on spaces.
1046  */
1047 static int str2argv(char *str, char **argv, int max_argc)
1048 {
1049         int argc = 0;
1050
1051         while (*str) {
1052                 if (argc >= max_argc) return argc;
1053
1054                 /*
1055                  *      Chop out comments early.
1056                  */
1057                 if (*str == '#') {
1058                         *str = '\0';
1059                         break;
1060                 }
1061
1062                 while ((*str == ' ') ||
1063                        (*str == '\t') ||
1064                        (*str == '\r') ||
1065                        (*str == '\n')) *(str++) = '\0';
1066
1067                 if (!*str) return argc;
1068
1069                 argv[argc] = str;
1070                 argc++;
1071
1072                 while (*str &&
1073                        (*str != ' ') &&
1074                        (*str != '\t') &&
1075                        (*str != '\r') &&
1076                        (*str != '\n')) str++;
1077         }
1078
1079         return argc;
1080 }
1081
1082 #define MAX_ARGV (16)
1083
1084 /*
1085  *      Initialize the dictionary.
1086  */
1087 static int my_dict_init(const char *dir, const char *fn,
1088                         const char *src_file, int src_line)
1089 {
1090         FILE    *fp;
1091         char    dirtmp[256];
1092         char    buf[256];
1093         char    *p;
1094         int     line = 0;
1095         int     vendor;
1096         int     block_vendor;
1097         struct stat statbuf;
1098         char    *argv[MAX_ARGV];
1099         int     argc;
1100
1101         if (strlen(fn) >= sizeof(dirtmp) / 2 ||
1102             strlen(dir) >= sizeof(dirtmp) / 2) {
1103                 librad_log("dict_init: filename name too long");
1104                 return -1;
1105         }
1106
1107         /*
1108          *      First see if fn is relative to dir. If so, create
1109          *      new filename. If not, remember the absolute dir.
1110          */
1111         if ((p = strrchr(fn, '/')) != NULL) {
1112                 strcpy(dirtmp, fn);
1113                 dirtmp[p - fn] = 0;
1114                 dir = dirtmp;
1115         } else if (dir && dir[0] && strcmp(dir, ".") != 0) {
1116                 snprintf(dirtmp, sizeof(dirtmp), "%s/%s", dir, fn);
1117                 fn = dirtmp;
1118         }
1119
1120         if ((fp = fopen(fn, "r")) == NULL) {
1121                 if (!src_file) {
1122                         librad_log("dict_init: Couldn't open dictionary \"%s\": %s",
1123                                    fn, strerror(errno));
1124                 } else {
1125                         librad_log("dict_init: %s[%d]: Couldn't open dictionary \"%s\": %s",
1126                                    src_file, src_line, fn, strerror(errno));
1127                 }
1128                 return -1;
1129         }
1130
1131         stat(fn, &statbuf); /* fopen() guarantees this will succeed */
1132         if (!S_ISREG(statbuf.st_mode)) {
1133                 fclose(fp);
1134                 librad_log("dict_init: Dictionary \"%s\" is not a regular file",
1135                            fn);
1136                 return -1;        
1137         }
1138
1139         /*
1140          *      Globally writable dictionaries means that users can control
1141          *      the server configuration with little difficulty.
1142          */
1143         if ((statbuf.st_mode & S_IWOTH) != 0) {
1144                 fclose(fp);
1145                 librad_log("dict_init: Dictionary \"%s\" is globally writable.  Refusing to start due to insecure configuration.",
1146                            fn);
1147                 return -1;
1148         }
1149
1150         dict_stat_add(fn, &statbuf);
1151
1152         /*
1153          *      Seed the random pool with data.
1154          */
1155         lrad_rand_seed(&statbuf, sizeof(statbuf));
1156
1157         block_vendor = 0;
1158
1159         while (fgets(buf, sizeof(buf), fp) != NULL) {
1160                 line++;
1161                 if (buf[0] == '#' || buf[0] == 0 ||
1162                     buf[0] == '\n' || buf[0] == '\r')
1163                         continue;
1164
1165                 /*
1166                  *  Comment characters should NOT be appearing anywhere but
1167                  *  as start of a comment;
1168                  */
1169                 p = strchr(buf, '#');
1170                 if (p) *p = '\0';
1171
1172                 argc = str2argv(buf, argv, MAX_ARGV);
1173                 if (argc == 0) continue;
1174
1175                 if (argc == 1) {
1176                         librad_log( "dict_init: %s[%d] invalid entry",
1177                                     fn, line);
1178                         fclose(fp);
1179                         return -1;
1180                 }
1181
1182                 if (0) {
1183                         int i;
1184
1185                         fprintf(stderr, "ARGC = %d\n",argc);
1186                         for (i = 0; i < argc; i++) {
1187                                 fprintf(stderr, "\t%s\n", argv[i]);
1188                         }
1189                 }
1190
1191                 /*
1192                  *      See if we need to import another dictionary.
1193                  */
1194                 if (strcasecmp(argv[0], "$INCLUDE") == 0) {
1195                         if (my_dict_init(dir, argv[1], fn, line) < 0) {
1196                                 fclose(fp);
1197                                 return -1;
1198                         }
1199                         continue;
1200                 } /* $INCLUDE */
1201
1202                 /*
1203                  *      Perhaps this is an attribute.
1204                  */
1205                 if (strcasecmp(argv[0], "ATTRIBUTE") == 0) {
1206                         if (process_attribute(fn, line, block_vendor,
1207                                               argv + 1, argc - 1) == -1) {
1208                                 fclose(fp);
1209                                 return -1;
1210                         }
1211                         continue;
1212                 }
1213
1214                 /*
1215                  *      Process VALUE lines.
1216                  */
1217                 if (strcasecmp(argv[0], "VALUE") == 0) {
1218                         if (process_value(fn, line,
1219                                           argv + 1, argc - 1) == -1) {
1220                                 fclose(fp);
1221                                 return -1;
1222                         }
1223                         continue;
1224                 }
1225
1226                 if (strcasecmp(argv[0], "VALUE-ALIAS") == 0) {
1227                         if (process_value_alias(fn, line,
1228                                                 argv + 1, argc - 1) == -1) {
1229                                 fclose(fp);
1230                                 return -1;
1231                         }
1232                         continue;
1233                 }
1234
1235                 /*
1236                  *      Process VENDOR lines.
1237                  */
1238                 if (strcasecmp(argv[0], "VENDOR") == 0) {
1239                         if (process_vendor(fn, line,
1240                                            argv + 1, argc - 1) == -1) {
1241                                 fclose(fp);
1242                                 return -1;
1243                         }
1244                         continue;
1245                 }
1246
1247                 if (strcasecmp(argv[0], "BEGIN-VENDOR") == 0) {
1248                         if (argc != 2) {
1249                                 librad_log(
1250                                 "dict_init: %s[%d] invalid BEGIN-VENDOR entry",
1251                                         fn, line);
1252                                 fclose(fp);
1253                                 return -1;
1254                         }
1255
1256                         vendor = dict_vendorbyname(argv[1]);
1257                         if (!vendor) {
1258                                 librad_log(
1259                                         "dict_init: %s[%d]: unknown vendor %s",
1260                                         fn, line, argv[1]);
1261                                 fclose(fp);
1262                                 return -1;
1263                         }
1264                         block_vendor = vendor;
1265                         continue;
1266                 } /* BEGIN-VENDOR */
1267
1268                 if (strcasecmp(argv[0], "END-VENDOR") == 0) {
1269                         if (argc != 2) {
1270                                 librad_log(
1271                                 "dict_init: %s[%d] invalid END-VENDOR entry",
1272                                         fn, line);
1273                                 fclose(fp);
1274                                 return -1;
1275                         }
1276
1277                         vendor = dict_vendorbyname(argv[1]);
1278                         if (!vendor) {
1279                                 librad_log(
1280                                         "dict_init: %s[%d]: unknown vendor %s",
1281                                         fn, line, argv[1]);
1282                                 fclose(fp);
1283                                 return -1;
1284                         }
1285
1286                         if (vendor != block_vendor) {
1287                                 librad_log(
1288                                         "dict_init: %s[%d]: END-VENDOR %s does not match any previous BEGIN-VENDOR",
1289                                         fn, line, argv[1]);
1290                                 fclose(fp);
1291                                 return -1;
1292                         }
1293                         block_vendor = 0;
1294                         continue;
1295                 } /* END-VENDOR */
1296
1297                 /*
1298                  *      Any other string: We don't recognize it.
1299                  */
1300                 librad_log(
1301                         "dict_init: %s[%d] invalid keyword \"%s\"",
1302                         fn, line, argv[0]);
1303                 fclose(fp);
1304                 return -1;
1305         }
1306         fclose(fp);
1307         return 0;
1308 }
1309
1310
1311 /*
1312  *      Empty callback for hash table initialization.
1313  */
1314 static int null_callback(void *ctx, void *data)
1315 {
1316         ctx = ctx;              /* -Wunused */
1317         data = data;            /* -Wunused */
1318
1319         return 0;
1320 }
1321
1322
1323 /*
1324  *      Initialize the directory, then fix the attr member of
1325  *      all attributes.
1326  */
1327 int dict_init(const char *dir, const char *fn)
1328 {
1329         /*
1330          *      Check if we need to change anything.  If not, don't do
1331          *      anything.
1332          */
1333         if (dict_stat_check(dir, fn)) {
1334                 return 0;
1335         }
1336
1337         /*
1338          *      Free the dictionaries, and the stat cache.
1339          */
1340         dict_free();
1341         stat_root_dir = strdup(dir);
1342         stat_root_file = strdup(fn);
1343
1344         /*
1345          *      Create the table of vendor by name.   There MAY NOT
1346          *      be multiple vendors of the same name.
1347          *
1348          *      Each vendor is malloc'd, so the free function is free.
1349          */
1350         vendors_byname = lrad_hash_table_create(dict_vendor_name_hash,
1351                                                 dict_vendor_name_cmp,
1352                                                 free);
1353         if (!vendors_byname) {
1354                 return -1;
1355         }
1356
1357         /*
1358          *      Create the table of vendors by value.  There MAY
1359          *      be vendors of the same value.  If there are, we
1360          *      pick the latest one.
1361          */
1362         vendors_byvalue = lrad_hash_table_create(dict_vendor_value_hash,
1363                                                  dict_vendor_value_cmp,
1364                                                  NULL);
1365         if (!vendors_byvalue) {
1366                 return -1;
1367         }
1368
1369         /*
1370          *      Create the table of attributes by name.   There MAY NOT
1371          *      be multiple attributes of the same name.
1372          *
1373          *      Each attribute is malloc'd, so the free function is free.
1374          */
1375         attributes_byname = lrad_hash_table_create(dict_attr_name_hash,
1376                                                    dict_attr_name_cmp,
1377                                                    free);
1378         if (!attributes_byname) {
1379                 return -1;
1380         }
1381
1382         /*
1383          *      Create the table of attributes by value.  There MAY
1384          *      be attributes of the same value.  If there are, we
1385          *      pick the latest one.
1386          */
1387         attributes_byvalue = lrad_hash_table_create(dict_attr_value_hash,
1388                                                     dict_attr_value_cmp,
1389                                                     NULL);
1390         if (!attributes_byvalue) {
1391                 return -1;
1392         }
1393
1394         values_byname = lrad_hash_table_create(dict_value_name_hash,
1395                                                dict_value_name_cmp,
1396                                                free);
1397         if (!values_byname) {
1398                 return -1;
1399         }
1400
1401         values_byvalue = lrad_hash_table_create(dict_value_value_hash,
1402                                                 dict_value_value_cmp,
1403                                                 NULL);
1404         if (!values_byvalue) {
1405                 return -1;
1406         }
1407
1408         value_fixup = NULL;     /* just to be safe. */
1409
1410         if (my_dict_init(dir, fn, NULL, 0) < 0)
1411                 return -1;
1412
1413         if (value_fixup) {
1414                 DICT_ATTR *a;
1415                 value_fixup_t *this, *next;
1416
1417                 for (this = value_fixup; this != NULL; this = next) {
1418                         next = this->next;
1419
1420                         a = dict_attrbyname(this->attrstr);
1421                         if (!a) {
1422                                 librad_log(
1423                                         "dict_init: No ATTRIBUTE \"%s\" defined for VALUE \"%s\"",
1424                                         this->attrstr, this->dval->name);
1425                                 return -1; /* leak, but they should die... */
1426                         }
1427
1428                         this->dval->attr = a->attr;
1429
1430                         /*
1431                          *      Add the value into the dictionary.
1432                          */
1433                         if (!lrad_hash_table_replace(values_byname,
1434                                                      this->dval)) {
1435                                 librad_log("dict_addvalue: Duplicate value name %s for attribute %s", this->dval->name, a->name);
1436                                 return -1;
1437                         }
1438                         
1439                         /*
1440                          *      Allow them to use the old name, but
1441                          *      prefer the new name when printing
1442                          *      values.
1443                          */
1444                         if (!lrad_hash_table_finddata(values_byvalue, this->dval)) {
1445                                 lrad_hash_table_replace(values_byvalue,
1446                                                         this->dval);
1447                         }
1448                         free(this);
1449
1450                         /*
1451                          *      Just so we don't lose track of things.
1452                          */
1453                         value_fixup = next;
1454                 }
1455         }
1456
1457         /*
1458          *      Walk over all of the hash tables to ensure they're
1459          *      initialized.  We do this because the threads may perform
1460          *      lookups, and we don't want multi-threaded re-ordering
1461          *      of the table entries.  That would be bad.
1462          */
1463         lrad_hash_table_walk(vendors_byname, null_callback, NULL);
1464         lrad_hash_table_walk(vendors_byvalue, null_callback, NULL);
1465
1466         lrad_hash_table_walk(attributes_byname, null_callback, NULL);
1467         lrad_hash_table_walk(attributes_byvalue, null_callback, NULL);
1468         
1469         lrad_hash_table_walk(values_byvalue, null_callback, NULL);
1470         lrad_hash_table_walk(values_byname, null_callback, NULL);
1471
1472         return 0;
1473 }
1474
1475 /*
1476  *      Get an attribute by its numerical value.
1477  */
1478 DICT_ATTR *dict_attrbyvalue(int attr)
1479 {
1480         DICT_ATTR dattr;
1481
1482         dattr.attr = attr;
1483         dattr.vendor = VENDOR(attr) & 0x7fff;
1484
1485         return lrad_hash_table_finddata(attributes_byvalue, &dattr);
1486 }
1487
1488 /*
1489  *      Get an attribute by its name.
1490  */
1491 DICT_ATTR *dict_attrbyname(const char *name)
1492 {
1493         DICT_ATTR dattr;
1494
1495         if (!name) return NULL;
1496
1497         strlcpy(dattr.name, name, sizeof(dattr.name));
1498
1499         return lrad_hash_table_finddata(attributes_byname, &dattr);
1500 }
1501
1502 /*
1503  *      Associate a value with an attribute and return it.
1504  */
1505 DICT_VALUE *dict_valbyattr(int attr, int value)
1506 {
1507         DICT_VALUE dval, *dv;
1508
1509         /*
1510          *      First, look up aliases.
1511          */
1512         dval.attr = attr;
1513         dval.name[0] = '\0';
1514
1515         /*
1516          *      Look up the attribute alias target, and use
1517          *      the correct attribute number if found.
1518          */
1519         dv = lrad_hash_table_finddata(values_byname, &dval);
1520         if (dv) dval.attr = dv->value;
1521
1522         dval.value = value;
1523         
1524         return lrad_hash_table_finddata(values_byvalue, &dval);
1525 }
1526
1527 /*
1528  *      Get a value by its name, keyed off of an attribute.
1529  */
1530 DICT_VALUE *dict_valbyname(int attr, const char *name)
1531 {
1532         DICT_VALUE *my_dv, *dv;
1533         uint32_t buffer[(sizeof(*my_dv) + DICT_VALUE_MAX_NAME_LEN + 3)/4];
1534
1535         if (!name) return NULL;
1536
1537         my_dv = (DICT_VALUE *) buffer;
1538         my_dv->attr = attr;
1539         my_dv->name[0] = '\0';
1540
1541         /*
1542          *      Look up the attribute alias target, and use
1543          *      the correct attribute number if found.
1544          */
1545         dv = lrad_hash_table_finddata(values_byname, my_dv);
1546         if (dv) my_dv->attr = dv->value;
1547
1548         strlcpy(my_dv->name, name, DICT_VALUE_MAX_NAME_LEN);
1549
1550         return lrad_hash_table_finddata(values_byname, my_dv);
1551 }
1552
1553 /*
1554  *      Get the vendor PEC based on the vendor name
1555  *
1556  *      This is efficient only for small numbers of vendors.
1557  */
1558 int dict_vendorbyname(const char *name)
1559 {
1560         DICT_VENDOR *dv;
1561         uint32_t buffer[(sizeof(*dv) + DICT_VENDOR_MAX_NAME_LEN + 3)/4];
1562
1563         if (!name) return 0;
1564
1565         dv = (DICT_VENDOR *) buffer;
1566         strlcpy(dv->name, name, DICT_VENDOR_MAX_NAME_LEN);
1567         
1568         dv = lrad_hash_table_finddata(vendors_byname, dv);
1569         if (!dv) return 0;
1570
1571         return dv->vendorpec;
1572 }
1573
1574 /*
1575  *      Return the vendor struct based on the PEC.
1576  */
1577 DICT_VENDOR *dict_vendorbyvalue(int vendorpec)
1578 {
1579         DICT_VENDOR dv;
1580
1581         dv.vendorpec = vendorpec;
1582
1583         return lrad_hash_table_finddata(vendors_byvalue, &dv);
1584 }