Don't allow globally writable dictionaries. This allows any
[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          *      Yet another hack for Diameter-encoded attributes:
486          */
487         if (attr->flags.diameter) attr->attr |= (1 << 31);
488
489         /*
490          *      Insert the attribute, only if it's not a duplicate.
491          */
492         if (!lrad_hash_table_insert(attributes_byname, attr)) {
493                 DICT_ATTR       *a;
494
495                 /*
496                  *      If the attribute has identical number, then
497                  *      ignore the duplicate.
498                  */
499                 a = lrad_hash_table_finddata(attributes_byname, attr);
500                 if (a && (strcasecmp(a->name, attr->name) == 0)) {
501                         if (a->attr != attr->attr) {
502                                 librad_log("dict_addattr: Duplicate attribute name %s", name);
503                                 return -1;
504                         }
505
506                         /*
507                          *      Same name, same vendor, same attr,
508                          *      maybe the flags and/or type is
509                          *      different.  Let the new value
510                          *      over-ride the old one.
511                          */
512                 }
513         }
514
515         /*
516          *      Insert the SAME pointer (not free'd when this entry is
517          *      deleted), into another table.
518          *
519          *      We want this behaviour because we want OLD names for
520          *      the attributes to be read from the configuration
521          *      files, but when we're printing them, (and looking up
522          *      by value) we want to use the NEW name.
523          */
524         if (!lrad_hash_table_replace(attributes_byvalue, attr)) {
525                 librad_log("dict_addattr: Failed inserting attribute name %s", name);
526                 return -1;
527         }
528
529         return 0;
530 }
531
532
533 /*
534  *      Add a value for an attribute to the dictionary.
535  */
536 int dict_addvalue(const char *namestr, const char *attrstr, int value)
537 {
538         size_t          length;
539         DICT_ATTR       *dattr;
540         DICT_VALUE      *dval;
541
542         if (!*namestr) {
543                 librad_log("dict_addvalue: empty names are not permitted");
544                 return -1;
545         }
546
547         if ((length = strlen(namestr)) >= DICT_VALUE_MAX_NAME_LEN) {
548                 librad_log("dict_addvalue: value name too long");
549                 return -1;
550         }
551
552         if ((dval = malloc(sizeof(*dval) + length)) == NULL) {
553                 librad_log("dict_addvalue: out of memory");
554                 return -1;
555         }
556         memset(dval, 0, sizeof(*dval));
557
558         strcpy(dval->name, namestr);
559         dval->value = value;
560
561         /*
562          *      Remember which attribute is associated with this
563          *      value, if possible.
564          */
565         dattr = dict_attrbyname(attrstr);
566         if (dattr) {
567                 if (dattr->flags.has_value_alias) {
568                         librad_log("dict_addvalue: Cannot add VALUE for ATTRIBUTE \"%s\": It already has a VALUE-ALIAS", attrstr);
569                         return -1;
570                 }
571
572                 dval->attr = dattr->attr;
573
574                 /*
575                  *      Enforce valid values
576                  *
577                  *      Don't worry about fixups...
578                  */
579                 switch (dattr->type) {
580                         case PW_TYPE_BYTE:
581                                 if (value > 255) {
582                                         librad_log("dict_addvalue: ATTRIBUTEs of type 'byte' cannot have VALUEs larger than 255");
583                                         return -1;
584                                 }
585                                 break;
586                         case PW_TYPE_SHORT:
587                                 if (value > 65535) {
588                                         librad_log("dict_addvalue: ATTRIBUTEs of type 'short' cannot have VALUEs larger than 65535");
589                                         return -1;
590                                 }
591                                 break;
592
593                                 /*
594                                  *      Allow octets for now, because
595                                  *      of dictionary.cablelabs
596                                  */
597                         case PW_TYPE_OCTETS:
598
599                         case PW_TYPE_INTEGER:
600                                 break;
601
602                         default:
603                                 librad_log("dict_addvalue: VALUEs cannot be defined for attributes of type '%s'",
604                                            lrad_int2str(type_table, dattr->type, "?Unknown?"));
605                                 return -1;
606                 }
607
608                 dattr->flags.has_value = 1;
609         } else {
610                 value_fixup_t *fixup;
611                 
612                 fixup = (value_fixup_t *) malloc(sizeof(*fixup));
613                 if (!fixup) {
614                         librad_log("dict_addvalue: out of memory");
615                         return -1;
616                 }
617                 memset(fixup, 0, sizeof(*fixup));
618
619                 strNcpy(fixup->attrstr, attrstr, sizeof(fixup->attrstr));
620                 fixup->dval = dval;
621                 
622                 /*
623                  *      Insert to the head of the list.
624                  */
625                 fixup->next = value_fixup;
626                 value_fixup = fixup;
627
628                 return 0;
629         }
630
631         /*
632          *      Add the value into the dictionary.
633          */
634         if (!lrad_hash_table_insert(values_byname, dval)) {
635                 if (dattr) {
636                         DICT_VALUE *old;
637                         
638                         /*
639                          *      Suppress duplicates with the same
640                          *      name and value.  There are lots in
641                          *      dictionary.ascend.
642                          */
643                         old = dict_valbyname(dattr->attr, namestr);
644                         if (old && (old->value == dval->value)) {
645                                 free(dval);
646                                 return 0;
647                         }
648                 }
649
650                 librad_log("dict_addvalue: Duplicate value name %s for attribute %s", namestr, attrstr);
651                 return -1;
652         }
653
654         /*
655          *      There are multiple VALUE's, keyed by attribute, so we
656          *      take care of that here.
657          */
658         if (!lrad_hash_table_replace(values_byvalue, dval)) {
659                 librad_log("dict_addvalue: Failed inserting value %s",
660                            namestr);
661                 return -1;
662         }
663
664         return 0;
665 }
666
667 /*
668  *      Process the ATTRIBUTE command
669  */
670 static int process_attribute(const char* fn, const int line,
671                              const int block_vendor, char **argv,
672                              int argc)
673 {
674         int             vendor = 0;
675         int             value;
676         int             type;
677         char            *s, *c;
678         ATTR_FLAGS      flags;
679
680         if ((argc < 3) || (argc > 4)) {
681                 librad_log("dict_init: %s[%d]: invalid ATTRIBUTE line",
682                         fn, line);
683                 return -1;
684         }
685
686         /*
687          *      Validate all entries
688          */
689         if (!isdigit((int) argv[1][0])) {
690                 librad_log("dict_init: %s[%d]: invalid value", fn, line);
691                 return -1;
692         }
693         sscanf(argv[1], "%i", &value);
694
695         /*
696          *      find the type of the attribute.
697          */
698         type = lrad_str2int(type_table, argv[2], -1);
699         if (type < 0) {
700                 librad_log("dict_init: %s[%d]: invalid type \"%s\"",
701                         fn, line, argv[2]);
702                 return -1;
703         }
704
705         /*
706          *      Only look up the vendor if the string
707          *      is non-empty.
708          */
709         memset(&flags, 0, sizeof(flags));
710         if (argc == 4) {
711                 /*
712                  *      FIXME: replace strtok with str2argv
713                  */
714                 s = strtok(argv[3], ",");
715                 while (s) {
716                         if (strcmp(s, "has_tag") == 0 ||
717                             strcmp(s, "has_tag=1") == 0) {
718                                 /* Boolean flag, means this is a
719                                    tagged attribute */
720                                 flags.has_tag = 1;
721                                 
722                         } else if (strncmp(s, "encrypt=", 8) == 0) {
723                                 /* Encryption method, defaults to 0 (none).
724                                    Currently valid is just type 2,
725                                    Tunnel-Password style, which can only
726                                    be applied to strings. */
727                                 flags.encrypt = strtol(s + 8, &c, 0);
728                                 if (*c) {
729                                         librad_log( "dict_init: %s[%d] invalid option %s",
730                                                     fn, line, s);
731                                         return -1;
732                                 }
733
734                         } else if (strncmp(s, "diameter", 8) == 0) {
735                                 flags.diameter = 1;
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         strNcpy(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         strNcpy(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         strNcpy(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 }