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