Pulled from branch_1_1, with formatting
[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                 if (!lrad_hash_table_replace(attributes_byname, attr)) {
512                         librad_log("dict_addattr: Internal error storing attribute %s", name);
513                         free(attr);
514                         return -1;
515                 }
516         }
517
518         /*
519          *      Insert the SAME pointer (not free'd when this entry is
520          *      deleted), into another table.
521          *
522          *      We want this behaviour because we want OLD names for
523          *      the attributes to be read from the configuration
524          *      files, but when we're printing them, (and looking up
525          *      by value) we want to use the NEW name.
526          */
527         if (!lrad_hash_table_replace(attributes_byvalue, attr)) {
528                 librad_log("dict_addattr: Failed inserting attribute name %s", name);
529                 return -1;
530         }
531
532         return 0;
533 }
534
535
536 /*
537  *      Add a value for an attribute to the dictionary.
538  */
539 int dict_addvalue(const char *namestr, const char *attrstr, int value)
540 {
541         size_t          length;
542         DICT_ATTR       *dattr;
543         DICT_VALUE      *dval;
544
545         if (!*namestr) {
546                 librad_log("dict_addvalue: empty names are not permitted");
547                 return -1;
548         }
549
550         if ((length = strlen(namestr)) >= DICT_VALUE_MAX_NAME_LEN) {
551                 librad_log("dict_addvalue: value name too long");
552                 return -1;
553         }
554
555         if ((dval = malloc(sizeof(*dval) + length)) == NULL) {
556                 librad_log("dict_addvalue: out of memory");
557                 return -1;
558         }
559         memset(dval, 0, sizeof(*dval));
560
561         strcpy(dval->name, namestr);
562         dval->value = value;
563
564         /*
565          *      Remember which attribute is associated with this
566          *      value, if possible.
567          */
568         dattr = dict_attrbyname(attrstr);
569         if (dattr) {
570                 if (dattr->flags.has_value_alias) {
571                         librad_log("dict_addvalue: Cannot add VALUE for ATTRIBUTE \"%s\": It already has a VALUE-ALIAS", attrstr);
572                         return -1;
573                 }
574
575                 dval->attr = dattr->attr;
576
577                 /*
578                  *      Enforce valid values
579                  *
580                  *      Don't worry about fixups...
581                  */
582                 switch (dattr->type) {
583                         case PW_TYPE_BYTE:
584                                 if (value > 255) {
585                                         librad_log("dict_addvalue: ATTRIBUTEs of type 'byte' cannot have VALUEs larger than 255");
586                                         return -1;
587                                 }
588                                 break;
589                         case PW_TYPE_SHORT:
590                                 if (value > 65535) {
591                                         librad_log("dict_addvalue: ATTRIBUTEs of type 'short' cannot have VALUEs larger than 65535");
592                                         return -1;
593                                 }
594                                 break;
595
596                                 /*
597                                  *      Allow octets for now, because
598                                  *      of dictionary.cablelabs
599                                  */
600                         case PW_TYPE_OCTETS:
601
602                         case PW_TYPE_INTEGER:
603                                 break;
604
605                         default:
606                                 librad_log("dict_addvalue: VALUEs cannot be defined for attributes of type '%s'",
607                                            lrad_int2str(type_table, dattr->type, "?Unknown?"));
608                                 return -1;
609                 }
610
611                 dattr->flags.has_value = 1;
612         } else {
613                 value_fixup_t *fixup;
614                 
615                 fixup = (value_fixup_t *) malloc(sizeof(*fixup));
616                 if (!fixup) {
617                         librad_log("dict_addvalue: out of memory");
618                         return -1;
619                 }
620                 memset(fixup, 0, sizeof(*fixup));
621
622                 strlcpy(fixup->attrstr, attrstr, sizeof(fixup->attrstr));
623                 fixup->dval = dval;
624                 
625                 /*
626                  *      Insert to the head of the list.
627                  */
628                 fixup->next = value_fixup;
629                 value_fixup = fixup;
630
631                 return 0;
632         }
633
634         /*
635          *      Add the value into the dictionary.
636          */
637         if (!lrad_hash_table_insert(values_byname, dval)) {
638                 if (dattr) {
639                         DICT_VALUE *old;
640                         
641                         /*
642                          *      Suppress duplicates with the same
643                          *      name and value.  There are lots in
644                          *      dictionary.ascend.
645                          */
646                         old = dict_valbyname(dattr->attr, namestr);
647                         if (old && (old->value == dval->value)) {
648                                 free(dval);
649                                 return 0;
650                         }
651                 }
652
653                 librad_log("dict_addvalue: Duplicate value name %s for attribute %s", namestr, attrstr);
654                 return -1;
655         }
656
657         /*
658          *      There are multiple VALUE's, keyed by attribute, so we
659          *      take care of that here.
660          */
661         if (!lrad_hash_table_replace(values_byvalue, dval)) {
662                 librad_log("dict_addvalue: Failed inserting value %s",
663                            namestr);
664                 return -1;
665         }
666
667         return 0;
668 }
669
670 /*
671  *      Process the ATTRIBUTE command
672  */
673 static int process_attribute(const char* fn, const int line,
674                              const int block_vendor, char **argv,
675                              int argc)
676 {
677         int             vendor = 0;
678         int             value;
679         int             type;
680         char            *s, *c;
681         ATTR_FLAGS      flags;
682
683         if ((argc < 3) || (argc > 4)) {
684                 librad_log("dict_init: %s[%d]: invalid ATTRIBUTE line",
685                         fn, line);
686                 return -1;
687         }
688
689         /*
690          *      Validate all entries
691          */
692         if (!isdigit((int) argv[1][0])) {
693                 librad_log("dict_init: %s[%d]: invalid value", fn, line);
694                 return -1;
695         }
696         sscanf(argv[1], "%i", &value);
697
698         /*
699          *      find the type of the attribute.
700          */
701         type = lrad_str2int(type_table, argv[2], -1);
702         if (type < 0) {
703                 librad_log("dict_init: %s[%d]: invalid type \"%s\"",
704                         fn, line, argv[2]);
705                 return -1;
706         }
707
708         /*
709          *      Only look up the vendor if the string
710          *      is non-empty.
711          */
712         memset(&flags, 0, sizeof(flags));
713         if (argc == 4) {
714                 /*
715                  *      FIXME: replace strtok with str2argv
716                  */
717                 s = strtok(argv[3], ",");
718                 while (s) {
719                         if (strcmp(s, "has_tag") == 0 ||
720                             strcmp(s, "has_tag=1") == 0) {
721                                 /* Boolean flag, means this is a
722                                    tagged attribute */
723                                 flags.has_tag = 1;
724                                 
725                         } else if (strncmp(s, "encrypt=", 8) == 0) {
726                                 /* Encryption method, defaults to 0 (none).
727                                    Currently valid is just type 2,
728                                    Tunnel-Password style, which can only
729                                    be applied to strings. */
730                                 flags.encrypt = strtol(s + 8, &c, 0);
731                                 if (*c) {
732                                         librad_log( "dict_init: %s[%d] invalid option %s",
733                                                     fn, line, s);
734                                         return -1;
735                                 }
736
737                         } else if (strncmp(s, "array", 8) == 0) {
738                                 flags.array = 1;
739
740                                 switch (type) {
741                                         case PW_TYPE_IPADDR:
742                                         case PW_TYPE_BYTE:
743                                         case PW_TYPE_SHORT:
744                                         case PW_TYPE_INTEGER:
745                                         case PW_TYPE_DATE:
746                                                 break;
747                                         
748                                         default:
749                                                 librad_log( "dict_init: %s[%d] Only IP addresses can have the \"array\" flag set.",
750                                                             fn, line);
751                                                 return -1;
752                                 }
753
754                         } else if (block_vendor) {
755                                 librad_log( "dict_init: %s[%d]: unknown option \"%s\"",
756                                             fn, line, s);
757                                 return -1;
758
759                         } else {
760                                 /* Must be a vendor 'flag'... */
761                                 if (strncmp(s, "vendor=", 7) == 0) {
762                                         /* New format */
763                                         s += 7;
764                                 }
765                                 
766                                 vendor = dict_vendorbyname(s);
767                                 if (!vendor) {
768                                         librad_log( "dict_init: %s[%d]: unknown vendor \"%s\"",
769                                                     fn, line, s);
770                                         return -1;
771                                 }
772                                 if (block_vendor && argv[3][0] &&
773                                     (block_vendor != vendor)) {
774                                         librad_log("dict_init: %s[%d]: mismatched vendor %s within BEGIN-VENDOR/END-VENDOR block",
775                                                    fn, line, argv[3]);
776                                         return -1;
777                                 }
778                         }
779                         s = strtok(NULL, ",");
780                 }
781         }
782
783         if (block_vendor) vendor = block_vendor;
784
785         /*
786          *      Special checks for tags, they make our life much more
787          *      difficult.
788          */
789         if (flags.has_tag) {
790                 /*
791                  *      Only string, octets, and integer can be tagged.
792                  */
793                 switch (type) {
794                 case PW_TYPE_STRING:
795                 case PW_TYPE_INTEGER:
796                         break;
797
798                 default:
799                         librad_log("dict_init: %s[%d]: Attributes of type %s cannot be tagged.",
800                                    fn, line,
801                                    lrad_int2str(type_table, type, "?Unknown?"));
802                         return -1;
803                         
804                 }
805         }
806
807         /*
808          *      Add it in.
809          */
810         if (dict_addattr(argv[0], vendor, type, value, flags) < 0) {
811                 librad_log("dict_init: %s[%d]: %s",
812                            fn, line, librad_errstr);
813                 return -1;
814         }
815
816         return 0;
817 }
818
819
820 /*
821  *      Process the VALUE command
822  */
823 static int process_value(const char* fn, const int line, char **argv,
824                          int argc)
825 {
826         int     value;
827
828         if (argc != 3) {
829                 librad_log("dict_init: %s[%d]: invalid VALUE line",
830                         fn, line);
831                 return -1;
832         }
833         /*
834          *      For Compatibility, skip "Server-Config"
835          */
836         if (strcasecmp(argv[0], "Server-Config") == 0)
837                 return 0;
838
839         /*
840          *      Validate all entries
841          */
842         if (!isdigit((int) argv[2][0])) {
843                 librad_log("dict_init: %s[%d]: invalid value",
844                         fn, line);
845                 return -1;
846         }
847         sscanf(argv[2], "%i", &value);
848
849         if (dict_addvalue(argv[1], argv[0], value) < 0) {
850                 librad_log("dict_init: %s[%d]: %s",
851                            fn, line, librad_errstr);
852                 return -1;
853         }
854
855         return 0;
856 }
857
858
859 /*
860  *      Process the VALUE-ALIAS command
861  *
862  *      This allows VALUE mappings to be shared among multiple
863  *      attributes.
864  */
865 static int process_value_alias(const char* fn, const int line, char **argv,
866                                int argc)
867 {
868         DICT_ATTR *my_da, *da;
869         DICT_VALUE *dval;
870
871         if (argc != 2) {
872                 librad_log("dict_init: %s[%d]: invalid VALUE-ALIAS line",
873                         fn, line);
874                 return -1;
875         }
876
877         my_da = dict_attrbyname(argv[0]);
878         if (!my_da) {
879                 librad_log("dict_init: %s[%d]: ATTRIBUTE \"%s\" does not exist",
880                            fn, line, argv[1]);
881                 return -1;
882         }
883
884         if (my_da->flags.has_value) {
885                 librad_log("dict_init: %s[%d]: Cannot add VALUE-ALIAS to ATTRIBUTE \"%s\" with pre-existing VALUE",
886                            fn, line, argv[0]);
887                 return -1;
888         }
889
890         if (my_da->flags.has_value_alias) {
891                 librad_log("dict_init: %s[%d]: Cannot add VALUE-ALIAS to ATTRIBUTE \"%s\" with pre-existing VALUE-ALIAS",
892                            fn, line, argv[0]);
893                 return -1;
894         }
895
896         da = dict_attrbyname(argv[1]);
897         if (!da) {
898                 librad_log("dict_init: %s[%d]: Cannot find ATTRIBUTE \"%s\" for alias",
899                            fn, line, argv[1]);
900                 return -1;
901         }
902
903         if (!da->flags.has_value) {
904                 librad_log("dict_init: %s[%d]: VALUE-ALIAS cannot refer to ATTRIBUTE %s: It has no values",
905                            fn, line, argv[1]);
906                 return -1;
907         }
908
909         if (da->flags.has_value_alias) {
910                 librad_log("dict_init: %s[%d]: Cannot add VALUE-ALIAS to ATTRIBUTE \"%s\" which itself has a VALUE-ALIAS",
911                            fn, line, argv[1]);
912                 return -1;
913         }
914
915         if (my_da->type != da->type) {
916                 librad_log("dict_init: %s[%d]: Cannot add VALUE-ALIAS between attributes of differing type",
917                            fn, line);
918                 return -1;
919         }
920
921         if ((dval = malloc(sizeof(*dval))) == NULL) {
922                 librad_log("dict_addvalue: out of memory");
923                 return -1;
924         }
925         memset(dval, 0, sizeof(*dval));
926
927         dval->name[0] = '\0';   /* empty name */
928         dval->attr = my_da->attr;
929         dval->value = da->attr;
930
931         if (!lrad_hash_table_insert(values_byname, dval)) {
932                 librad_log("dict_init: %s[%d]: Error create alias",
933                            fn, line);
934                 free(dval);
935                 return -1;
936         }
937
938         return 0;
939 }
940
941
942 /*
943  *      Process the VENDOR command
944  */
945 static int process_vendor(const char* fn, const int line, char **argv,
946                           int argc)
947 {
948         int     value;
949         const   char *format = NULL;
950
951         if ((argc < 2) || (argc > 3)) {
952                 librad_log( "dict_init: %s[%d] invalid VENDOR entry",
953                             fn, line);
954                 return -1;
955         }
956
957         /*
958          *       Validate all entries
959          */
960         if (!isdigit((int) argv[1][0])) {
961                 librad_log("dict_init: %s[%d]: invalid value",
962                         fn, line);
963                 return -1;
964         }
965         value = atoi(argv[1]);
966
967         /* Create a new VENDOR entry for the list */
968         if (dict_addvendor(argv[0], value) < 0) {
969                 librad_log("dict_init: %s[%d]: %s",
970                            fn, line, librad_errstr);
971                 return -1;
972         }
973
974         /*
975          *      Look for a format statement
976          */
977         if (argc == 3) {
978                 format = argv[2];
979
980         } else if (value == VENDORPEC_USR) { /* catch dictionary screw-ups */
981                 format = "format=4,0";
982
983         } else if (value == VENDORPEC_LUCENT) {
984                 format = "format=2,1";
985
986         } else if (value == VENDORPEC_STARENT) {
987                 format = "format=2,2";
988
989         } /* else no fixups to do */
990
991         if (format) {
992                 int type, length;
993                 const char *p;
994                 DICT_VENDOR *dv;
995
996                 if (strncasecmp(format, "format=", 7) != 0) {
997                         librad_log("dict_init: %s[%d]: Invalid format for VENDOR.  Expected \"format=\", got \"%s\"",
998                                    fn, line, format);
999                         return -1;
1000                 }
1001
1002                 p = format + 7;
1003                 if ((strlen(p) != 3) || 
1004                     !isdigit((int) p[0]) ||
1005                     (p[1] != ',') ||
1006                     !isdigit((int) p[2])) {
1007                         librad_log("dict_init: %s[%d]: Invalid format for VENDOR.  Expected text like \"1,1\", got \"%s\"",
1008                                    fn, line, p);
1009                         return -1;
1010                 }
1011
1012                 type = (int) (p[0] - '0');
1013                 length = (int) (p[2] - '0');
1014
1015                 dv = dict_vendorbyvalue(value);
1016                 if (!dv) {
1017                         librad_log("dict_init: %s[%d]: Failed adding format for VENDOR",
1018                                    fn, line);
1019                         return -1;
1020                 }
1021
1022                 if ((type != 1) && (type != 2) && (type != 4)) {
1023                         librad_log("dict_init: %s[%d]: invalid type value %d for VENDOR",
1024                                    fn, line, type);
1025                         return -1;
1026                 }
1027
1028                 if ((length != 0) && (length != 1) && (length != 2)) {
1029                         librad_log("dict_init: %s[%d]: invalid length value %d for VENDOR",
1030                                    fn, line, length);
1031                         return -1;
1032                 }
1033
1034                 dv->type = type;
1035                 dv->length = length;
1036         }
1037
1038         return 0;
1039 }
1040
1041 /*
1042  *      String split routine.  Splits an input string IN PLACE
1043  *      into pieces, based on spaces.
1044  */
1045 static int str2argv(char *str, char **argv, int max_argc)
1046 {
1047         int argc = 0;
1048
1049         while (*str) {
1050                 if (argc >= max_argc) return argc;
1051
1052                 /*
1053                  *      Chop out comments early.
1054                  */
1055                 if (*str == '#') {
1056                         *str = '\0';
1057                         break;
1058                 }
1059
1060                 while ((*str == ' ') ||
1061                        (*str == '\t') ||
1062                        (*str == '\r') ||
1063                        (*str == '\n')) *(str++) = '\0';
1064
1065                 if (!*str) return argc;
1066
1067                 argv[argc] = str;
1068                 argc++;
1069
1070                 while (*str &&
1071                        (*str != ' ') &&
1072                        (*str != '\t') &&
1073                        (*str != '\r') &&
1074                        (*str != '\n')) str++;
1075         }
1076
1077         return argc;
1078 }
1079
1080 #define MAX_ARGV (16)
1081
1082 /*
1083  *      Initialize the dictionary.
1084  */
1085 static int my_dict_init(const char *dir, const char *fn,
1086                         const char *src_file, int src_line)
1087 {
1088         FILE    *fp;
1089         char    dirtmp[256];
1090         char    buf[256];
1091         char    *p;
1092         int     line = 0;
1093         int     vendor;
1094         int     block_vendor;
1095         struct stat statbuf;
1096         char    *argv[MAX_ARGV];
1097         int     argc;
1098
1099         if (strlen(fn) >= sizeof(dirtmp) / 2 ||
1100             strlen(dir) >= sizeof(dirtmp) / 2) {
1101                 librad_log("dict_init: filename name too long");
1102                 return -1;
1103         }
1104
1105         /*
1106          *      First see if fn is relative to dir. If so, create
1107          *      new filename. If not, remember the absolute dir.
1108          */
1109         if ((p = strrchr(fn, '/')) != NULL) {
1110                 strcpy(dirtmp, fn);
1111                 dirtmp[p - fn] = 0;
1112                 dir = dirtmp;
1113         } else if (dir && dir[0] && strcmp(dir, ".") != 0) {
1114                 snprintf(dirtmp, sizeof(dirtmp), "%s/%s", dir, fn);
1115                 fn = dirtmp;
1116         }
1117
1118         if ((fp = fopen(fn, "r")) == NULL) {
1119                 if (!src_file) {
1120                         librad_log("dict_init: Couldn't open dictionary \"%s\": %s",
1121                                    fn, strerror(errno));
1122                 } else {
1123                         librad_log("dict_init: %s[%d]: Couldn't open dictionary \"%s\": %s",
1124                                    src_file, src_line, fn, strerror(errno));
1125                 }
1126                 return -1;
1127         }
1128
1129         stat(fn, &statbuf); /* fopen() guarantees this will succeed */
1130         if (!S_ISREG(statbuf.st_mode)) {
1131                 fclose(fp);
1132                 librad_log("dict_init: Dictionary \"%s\" is not a regular file",
1133                            fn);
1134                 return -1;        
1135         }
1136
1137         /*
1138          *      Globally writable dictionaries means that users can control
1139          *      the server configuration with little difficulty.
1140          */
1141         if ((statbuf.st_mode & S_IWOTH) != 0) {
1142                 fclose(fp);
1143                 librad_log("dict_init: Dictionary \"%s\" is globally writable.  Refusing to start due to insecure configuration.",
1144                            fn);
1145                 return -1;
1146         }
1147
1148         dict_stat_add(fn, &statbuf);
1149
1150         /*
1151          *      Seed the random pool with data.
1152          */
1153         lrad_rand_seed(&statbuf, sizeof(statbuf));
1154
1155         block_vendor = 0;
1156
1157         while (fgets(buf, sizeof(buf), fp) != NULL) {
1158                 line++;
1159                 if (buf[0] == '#' || buf[0] == 0 ||
1160                     buf[0] == '\n' || buf[0] == '\r')
1161                         continue;
1162
1163                 /*
1164                  *  Comment characters should NOT be appearing anywhere but
1165                  *  as start of a comment;
1166                  */
1167                 p = strchr(buf, '#');
1168                 if (p) *p = '\0';
1169
1170                 argc = str2argv(buf, argv, MAX_ARGV);
1171                 if (argc == 0) continue;
1172
1173                 if (argc == 1) {
1174                         librad_log( "dict_init: %s[%d] invalid entry",
1175                                     fn, line);
1176                         fclose(fp);
1177                         return -1;
1178                 }
1179
1180                 if (0) {
1181                         int i;
1182
1183                         fprintf(stderr, "ARGC = %d\n",argc);
1184                         for (i = 0; i < argc; i++) {
1185                                 fprintf(stderr, "\t%s\n", argv[i]);
1186                         }
1187                 }
1188
1189                 /*
1190                  *      See if we need to import another dictionary.
1191                  */
1192                 if (strcasecmp(argv[0], "$INCLUDE") == 0) {
1193                         if (my_dict_init(dir, argv[1], fn, line) < 0) {
1194                                 fclose(fp);
1195                                 return -1;
1196                         }
1197                         continue;
1198                 } /* $INCLUDE */
1199
1200                 /*
1201                  *      Perhaps this is an attribute.
1202                  */
1203                 if (strcasecmp(argv[0], "ATTRIBUTE") == 0) {
1204                         if (process_attribute(fn, line, block_vendor,
1205                                               argv + 1, argc - 1) == -1) {
1206                                 fclose(fp);
1207                                 return -1;
1208                         }
1209                         continue;
1210                 }
1211
1212                 /*
1213                  *      Process VALUE lines.
1214                  */
1215                 if (strcasecmp(argv[0], "VALUE") == 0) {
1216                         if (process_value(fn, line,
1217                                           argv + 1, argc - 1) == -1) {
1218                                 fclose(fp);
1219                                 return -1;
1220                         }
1221                         continue;
1222                 }
1223
1224                 if (strcasecmp(argv[0], "VALUE-ALIAS") == 0) {
1225                         if (process_value_alias(fn, line,
1226                                                 argv + 1, argc - 1) == -1) {
1227                                 fclose(fp);
1228                                 return -1;
1229                         }
1230                         continue;
1231                 }
1232
1233                 /*
1234                  *      Process VENDOR lines.
1235                  */
1236                 if (strcasecmp(argv[0], "VENDOR") == 0) {
1237                         if (process_vendor(fn, line,
1238                                            argv + 1, argc - 1) == -1) {
1239                                 fclose(fp);
1240                                 return -1;
1241                         }
1242                         continue;
1243                 }
1244
1245                 if (strcasecmp(argv[0], "BEGIN-VENDOR") == 0) {
1246                         if (argc != 2) {
1247                                 librad_log(
1248                                 "dict_init: %s[%d] invalid BEGIN-VENDOR entry",
1249                                         fn, line);
1250                                 fclose(fp);
1251                                 return -1;
1252                         }
1253
1254                         vendor = dict_vendorbyname(argv[1]);
1255                         if (!vendor) {
1256                                 librad_log(
1257                                         "dict_init: %s[%d]: unknown vendor %s",
1258                                         fn, line, argv[1]);
1259                                 fclose(fp);
1260                                 return -1;
1261                         }
1262                         block_vendor = vendor;
1263                         continue;
1264                 } /* BEGIN-VENDOR */
1265
1266                 if (strcasecmp(argv[0], "END-VENDOR") == 0) {
1267                         if (argc != 2) {
1268                                 librad_log(
1269                                 "dict_init: %s[%d] invalid END-VENDOR entry",
1270                                         fn, line);
1271                                 fclose(fp);
1272                                 return -1;
1273                         }
1274
1275                         vendor = dict_vendorbyname(argv[1]);
1276                         if (!vendor) {
1277                                 librad_log(
1278                                         "dict_init: %s[%d]: unknown vendor %s",
1279                                         fn, line, argv[1]);
1280                                 fclose(fp);
1281                                 return -1;
1282                         }
1283
1284                         if (vendor != block_vendor) {
1285                                 librad_log(
1286                                         "dict_init: %s[%d]: END-VENDOR %s does not match any previous BEGIN-VENDOR",
1287                                         fn, line, argv[1]);
1288                                 fclose(fp);
1289                                 return -1;
1290                         }
1291                         block_vendor = 0;
1292                         continue;
1293                 } /* END-VENDOR */
1294
1295                 /*
1296                  *      Any other string: We don't recognize it.
1297                  */
1298                 librad_log(
1299                         "dict_init: %s[%d] invalid keyword \"%s\"",
1300                         fn, line, argv[0]);
1301                 fclose(fp);
1302                 return -1;
1303         }
1304         fclose(fp);
1305         return 0;
1306 }
1307
1308
1309 /*
1310  *      Empty callback for hash table initialization.
1311  */
1312 static int null_callback(void *ctx, void *data)
1313 {
1314         ctx = ctx;              /* -Wunused */
1315         data = data;            /* -Wunused */
1316
1317         return 0;
1318 }
1319
1320
1321 /*
1322  *      Initialize the directory, then fix the attr member of
1323  *      all attributes.
1324  */
1325 int dict_init(const char *dir, const char *fn)
1326 {
1327         /*
1328          *      Check if we need to change anything.  If not, don't do
1329          *      anything.
1330          */
1331         if (dict_stat_check(dir, fn)) {
1332                 return 0;
1333         }
1334
1335         /*
1336          *      Free the dictionaries, and the stat cache.
1337          */
1338         dict_free();
1339         stat_root_dir = strdup(dir);
1340         stat_root_file = strdup(fn);
1341
1342         /*
1343          *      Create the table of vendor by name.   There MAY NOT
1344          *      be multiple vendors of the same name.
1345          *
1346          *      Each vendor is malloc'd, so the free function is free.
1347          */
1348         vendors_byname = lrad_hash_table_create(dict_vendor_name_hash,
1349                                                 dict_vendor_name_cmp,
1350                                                 free);
1351         if (!vendors_byname) {
1352                 return -1;
1353         }
1354
1355         /*
1356          *      Create the table of vendors by value.  There MAY
1357          *      be vendors of the same value.  If there are, we
1358          *      pick the latest one.
1359          */
1360         vendors_byvalue = lrad_hash_table_create(dict_vendor_value_hash,
1361                                                  dict_vendor_value_cmp,
1362                                                  NULL);
1363         if (!vendors_byvalue) {
1364                 return -1;
1365         }
1366
1367         /*
1368          *      Create the table of attributes by name.   There MAY NOT
1369          *      be multiple attributes of the same name.
1370          *
1371          *      Each attribute is malloc'd, so the free function is free.
1372          */
1373         attributes_byname = lrad_hash_table_create(dict_attr_name_hash,
1374                                                    dict_attr_name_cmp,
1375                                                    free);
1376         if (!attributes_byname) {
1377                 return -1;
1378         }
1379
1380         /*
1381          *      Create the table of attributes by value.  There MAY
1382          *      be attributes of the same value.  If there are, we
1383          *      pick the latest one.
1384          */
1385         attributes_byvalue = lrad_hash_table_create(dict_attr_value_hash,
1386                                                     dict_attr_value_cmp,
1387                                                     NULL);
1388         if (!attributes_byvalue) {
1389                 return -1;
1390         }
1391
1392         values_byname = lrad_hash_table_create(dict_value_name_hash,
1393                                                dict_value_name_cmp,
1394                                                free);
1395         if (!values_byname) {
1396                 return -1;
1397         }
1398
1399         values_byvalue = lrad_hash_table_create(dict_value_value_hash,
1400                                                 dict_value_value_cmp,
1401                                                 NULL);
1402         if (!values_byvalue) {
1403                 return -1;
1404         }
1405
1406         value_fixup = NULL;     /* just to be safe. */
1407
1408         if (my_dict_init(dir, fn, NULL, 0) < 0)
1409                 return -1;
1410
1411         if (value_fixup) {
1412                 DICT_ATTR *a;
1413                 value_fixup_t *this, *next;
1414
1415                 for (this = value_fixup; this != NULL; this = next) {
1416                         next = this->next;
1417
1418                         a = dict_attrbyname(this->attrstr);
1419                         if (!a) {
1420                                 librad_log(
1421                                         "dict_init: No ATTRIBUTE \"%s\" defined for VALUE \"%s\"",
1422                                         this->attrstr, this->dval->name);
1423                                 return -1; /* leak, but they should die... */
1424                         }
1425
1426                         this->dval->attr = a->attr;
1427
1428                         /*
1429                          *      Add the value into the dictionary.
1430                          */
1431                         if (!lrad_hash_table_replace(values_byname,
1432                                                      this->dval)) {
1433                                 librad_log("dict_addvalue: Duplicate value name %s for attribute %s", this->dval->name, a->name);
1434                                 return -1;
1435                         }
1436                         
1437                         /*
1438                          *      Allow them to use the old name, but
1439                          *      prefer the new name when printing
1440                          *      values.
1441                          */
1442                         if (!lrad_hash_table_finddata(values_byvalue, this->dval)) {
1443                                 lrad_hash_table_replace(values_byvalue,
1444                                                         this->dval);
1445                         }
1446                         free(this);
1447
1448                         /*
1449                          *      Just so we don't lose track of things.
1450                          */
1451                         value_fixup = next;
1452                 }
1453         }
1454
1455         /*
1456          *      Walk over all of the hash tables to ensure they're
1457          *      initialized.  We do this because the threads may perform
1458          *      lookups, and we don't want multi-threaded re-ordering
1459          *      of the table entries.  That would be bad.
1460          */
1461         lrad_hash_table_walk(vendors_byname, null_callback, NULL);
1462         lrad_hash_table_walk(vendors_byvalue, null_callback, NULL);
1463
1464         lrad_hash_table_walk(attributes_byname, null_callback, NULL);
1465         lrad_hash_table_walk(attributes_byvalue, null_callback, NULL);
1466         
1467         lrad_hash_table_walk(values_byvalue, null_callback, NULL);
1468         lrad_hash_table_walk(values_byname, null_callback, NULL);
1469
1470         return 0;
1471 }
1472
1473 /*
1474  *      Get an attribute by its numerical value.
1475  */
1476 DICT_ATTR *dict_attrbyvalue(int attr)
1477 {
1478         DICT_ATTR dattr;
1479
1480         dattr.attr = attr;
1481         dattr.vendor = VENDOR(attr) & 0x7fff;
1482
1483         return lrad_hash_table_finddata(attributes_byvalue, &dattr);
1484 }
1485
1486 /*
1487  *      Get an attribute by its name.
1488  */
1489 DICT_ATTR *dict_attrbyname(const char *name)
1490 {
1491         DICT_ATTR dattr;
1492
1493         if (!name) return NULL;
1494
1495         strlcpy(dattr.name, name, sizeof(dattr.name));
1496
1497         return lrad_hash_table_finddata(attributes_byname, &dattr);
1498 }
1499
1500 /*
1501  *      Associate a value with an attribute and return it.
1502  */
1503 DICT_VALUE *dict_valbyattr(int attr, int value)
1504 {
1505         DICT_VALUE dval, *dv;
1506
1507         /*
1508          *      First, look up aliases.
1509          */
1510         dval.attr = attr;
1511         dval.name[0] = '\0';
1512
1513         /*
1514          *      Look up the attribute alias target, and use
1515          *      the correct attribute number if found.
1516          */
1517         dv = lrad_hash_table_finddata(values_byname, &dval);
1518         if (dv) dval.attr = dv->value;
1519
1520         dval.value = value;
1521         
1522         return lrad_hash_table_finddata(values_byvalue, &dval);
1523 }
1524
1525 /*
1526  *      Get a value by its name, keyed off of an attribute.
1527  */
1528 DICT_VALUE *dict_valbyname(int attr, const char *name)
1529 {
1530         DICT_VALUE *my_dv, *dv;
1531         uint32_t buffer[(sizeof(*my_dv) + DICT_VALUE_MAX_NAME_LEN + 3)/4];
1532
1533         if (!name) return NULL;
1534
1535         my_dv = (DICT_VALUE *) buffer;
1536         my_dv->attr = attr;
1537         my_dv->name[0] = '\0';
1538
1539         /*
1540          *      Look up the attribute alias target, and use
1541          *      the correct attribute number if found.
1542          */
1543         dv = lrad_hash_table_finddata(values_byname, my_dv);
1544         if (dv) my_dv->attr = dv->value;
1545
1546         strlcpy(my_dv->name, name, DICT_VALUE_MAX_NAME_LEN);
1547
1548         return lrad_hash_table_finddata(values_byname, my_dv);
1549 }
1550
1551 /*
1552  *      Get the vendor PEC based on the vendor name
1553  *
1554  *      This is efficient only for small numbers of vendors.
1555  */
1556 int dict_vendorbyname(const char *name)
1557 {
1558         DICT_VENDOR *dv;
1559         uint32_t buffer[(sizeof(*dv) + DICT_VENDOR_MAX_NAME_LEN + 3)/4];
1560
1561         if (!name) return 0;
1562
1563         dv = (DICT_VENDOR *) buffer;
1564         strlcpy(dv->name, name, DICT_VENDOR_MAX_NAME_LEN);
1565         
1566         dv = lrad_hash_table_finddata(vendors_byname, dv);
1567         if (!dv) return 0;
1568
1569         return dv->vendorpec;
1570 }
1571
1572 /*
1573  *      Return the vendor struct based on the PEC.
1574  */
1575 DICT_VENDOR *dict_vendorbyvalue(int vendorpec)
1576 {
1577         DICT_VENDOR dv;
1578
1579         dv.vendorpec = vendorpec;
1580
1581         return lrad_hash_table_finddata(vendors_byvalue, &dv);
1582 }