]> git.ipfire.org Git - thirdparty/freeradius-server.git/blob
1516561
[thirdparty/freeradius-server.git] /
1 /*
2 * This program is free software; you can redistribute it and/or modify
3 * it under the terms of the GNU General Public License as published by
4 * the Free Software Foundation; either version 2 of the License, or
5 * (at your option) any later version.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software
14 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
15 */
16
17 /** Multi-protocol AVP dictionary API
18 *
19 * @file src/lib/util/dict.c
20 *
21 * @copyright 2000,2006 The FreeRADIUS server project
22 */
23 RCSID("$Id$")
24
25 #include "dict.h"
26
27 #include <freeradius-devel/radius/defs.h>
28 #include <freeradius-devel/util/conf.h>
29 #include <freeradius-devel/util/hash.h>
30 #include <freeradius-devel/util/misc.h>
31 #include <freeradius-devel/util/proto.h>
32 #include <freeradius-devel/util/rand.h>
33 #include <freeradius-devel/util/syserror.h>
34 #include <freeradius-devel/util/talloc.h>
35
36 #include <ctype.h>
37 #ifdef HAVE_SYS_STAT_H
38 # include <sys/stat.h>
39 #endif
40
41 #define MAX_ARGV (16)
42
43 #define DICT_POOL_SIZE (1024 * 1024 * 2)
44 #define DICT_FIXUP_POOL_SIZE (1024 * 1024 * 1)
45
46 static TALLOC_CTX *dict_ctx;
47 static fr_hash_table_t *protocol_by_name = NULL; //!< Hash containing names of all the registered protocols.
48 static fr_hash_table_t *protocol_by_num = NULL; //!< Hash containing numbers of all the registered protocols.
49 static char *default_dict_dir; //!< The default location for loading dictionaries if one
50 ///< wasn't provided.
51
52 /** Magic internal dictionary
53 *
54 * Internal dictionary is checked in addition to the protocol dictionary
55 * when resolving attribute names.
56 *
57 * This is because internal attributes are valid for every
58 * protocol.
59 */
60 fr_dict_t *fr_dict_internal = NULL; //!< Internal server dictionary.
61
62 typedef struct dict_enum_fixup_s dict_enum_fixup_t;
63
64 /** A temporary enum value, which we'll resolve later
65 *
66 */
67 struct dict_enum_fixup_s {
68 char *attribute; //!< we couldn't find (and will need to resolve later).
69 char *alias; //!< Raw enum name.
70 char *value; //!< Raw enum value. We can't do anything with this until
71 //!< we know the attribute type, which we only find out later.
72
73 dict_enum_fixup_t *next; //!< Next in the linked list of fixups.
74 };
75
76 /** Vendors and attribute names
77 *
78 * It's very likely that the same vendors will operate in multiple
79 * protocol spaces, but number their attributes differently, so we need
80 * per protocol dictionaries.
81 *
82 * There would also be conflicts for DHCP(v6)/RADIUS attributes etc...
83 */
84 struct fr_dict {
85 bool in_protocol_by_name; //!< Whether the dictionary has been inserted into the
86 ///< protocol_by_name hash.
87 bool in_protocol_by_num; //!< Whether the dictionary has been inserted into the
88 //!< protocol_by_num table.
89
90 bool autoloaded; //!< manual vs autoload
91
92 fr_hash_table_t *vendors_by_name; //!< Lookup vendor by name.
93 fr_hash_table_t *vendors_by_num; //!< Lookup vendor by PEN.
94
95 fr_hash_table_t *attributes_by_name; //!< Allow attribute lookup by unique name.
96
97 fr_hash_table_t *attributes_combo; //!< Lookup variants of polymorphic attributes.
98
99 fr_hash_table_t *values_by_da; //!< Lookup an attribute enum by its value.
100 fr_hash_table_t *values_by_alias; //!< Lookup an attribute enum by its alias name.
101
102 fr_dict_attr_t *root; //!< Root attribute of this dictionary.
103
104 TALLOC_CTX *pool; //!< Talloc memory pool to reduce allocs.
105 ///< in the dictionary.
106 };
107
108 /** Map data types to min / max data sizes
109 */
110 size_t const dict_attr_sizes[FR_TYPE_MAX + 1][2] = {
111 [FR_TYPE_INVALID] = {~0, 0}, //!< Ensure array starts at 0 (umm?)
112
113 [FR_TYPE_STRING] = {0, ~0},
114 [FR_TYPE_OCTETS] = {0, ~0},
115
116 [FR_TYPE_IPV4_ADDR] = {4, 4},
117 [FR_TYPE_IPV4_PREFIX] = {6, 6},
118 [FR_TYPE_IPV6_ADDR] = {16, 16},
119 [FR_TYPE_IPV6_PREFIX] = {2, 18},
120 [FR_TYPE_COMBO_IP_ADDR] = {4, 16},
121 [FR_TYPE_IFID] = {8, 8},
122 [FR_TYPE_ETHERNET] = {6, 6},
123
124 [FR_TYPE_BOOL] = {1, 1},
125 [FR_TYPE_UINT8] = {1, 1},
126 [FR_TYPE_UINT16] = {2, 2},
127 [FR_TYPE_UINT32] = {4, 4},
128 [FR_TYPE_UINT64] = {8, 8},
129 [FR_TYPE_SIZE] = {sizeof(size_t), sizeof(size_t)},
130 [FR_TYPE_INT32] = {4, 4},
131
132 [FR_TYPE_DATE] = {4, 4},
133 [FR_TYPE_ABINARY] = {32, ~0},
134
135 [FR_TYPE_TLV] = {2, ~0},
136 [FR_TYPE_STRUCT] = {1, ~0},
137
138 [FR_TYPE_EXTENDED] = {2, ~0},
139 [FR_TYPE_LONG_EXTENDED] = {3, ~0},
140
141 [FR_TYPE_VSA] = {4, ~0},
142 [FR_TYPE_EVS] = {6, ~0},
143
144 [FR_TYPE_MAX] = {~0, 0} //!< Ensure array covers all types.
145 };
146
147 /** Characters allowed in dictionary names
148 *
149 */
150 bool const fr_dict_attr_allowed_chars[UINT8_MAX] = {
151 ['-'] = true, ['.'] = true, ['/'] = true, ['_'] = true,
152 ['0'] = true, ['1'] = true, ['2'] = true, ['3'] = true, ['4'] = true,
153 ['5'] = true, ['6'] = true, ['7'] = true, ['8'] = true, ['9'] = true,
154 ['A'] = true, ['B'] = true, ['C'] = true, ['D'] = true, ['E'] = true,
155 ['F'] = true, ['G'] = true, ['H'] = true, ['I'] = true, ['J'] = true,
156 ['K'] = true, ['L'] = true, ['M'] = true, ['N'] = true, ['O'] = true,
157 ['P'] = true, ['Q'] = true, ['R'] = true, ['S'] = true, ['T'] = true,
158 ['U'] = true, ['V'] = true, ['W'] = true, ['X'] = true, ['Y'] = true,
159 ['Z'] = true,
160 ['a'] = true, ['b'] = true, ['c'] = true, ['d'] = true, ['e'] = true,
161 ['f'] = true, ['g'] = true, ['h'] = true, ['i'] = true, ['j'] = true,
162 ['k'] = true, ['l'] = true, ['m'] = true, ['n'] = true, ['o'] = true,
163 ['p'] = true, ['q'] = true, ['r'] = true, ['s'] = true, ['t'] = true,
164 ['u'] = true, ['v'] = true, ['w'] = true, ['x'] = true, ['y'] = true,
165 ['z'] = true
166 };
167
168 /** Structural data types
169 *
170 */
171 bool const fr_dict_non_data_types[FR_TYPE_MAX + 1] = {
172 [FR_TYPE_TLV] = true,
173 [FR_TYPE_STRUCT] = true,
174 [FR_TYPE_EXTENDED] = true,
175 [FR_TYPE_LONG_EXTENDED] = true,
176 [FR_TYPE_VSA] = true,
177 [FR_TYPE_EVS] = true,
178 [FR_TYPE_VENDOR] = true
179 };
180
181 /*
182 * Create the hash of the name.
183 *
184 * We copy the hash function here because it's substantially faster.
185 */
186 #define FNV_MAGIC_INIT (0x811c9dc5)
187 #define FNV_MAGIC_PRIME (0x01000193)
188
189 /** Set the internal dictionary if none was provided
190 *
191 * @param _dict Dict pointer to check/set.
192 * @param _ret Value to return if no dictionaries are available.
193 */
194 #define INTERNAL_IF_NULL(_dict, _ret) \
195 do { \
196 if (!(_dict)) { \
197 _dict = fr_dict_internal; \
198 if (unlikely(!(_dict))) { \
199 fr_strerror_printf("No dictionaries available for attribute resolution"); \
200 return (_ret); \
201 } \
202 } \
203 } while (0)
204
205 /** Empty callback for hash table initialization
206 *
207 */
208 static int hash_null_callback(UNUSED void *ctx, UNUSED void *data)
209 {
210 return 0;
211 }
212
213 static void hash_pool_free(void *to_free)
214 {
215 talloc_free(to_free);
216 }
217
218 /** Apply a simple (case insensitive) hashing function to the name of an attribute, vendor or protocol
219 *
220 * @param[in] name of the attribute, vendor or protocol.
221 * @param[in] len length of the input string.
222 *
223 * @return the hashed derived from the name.
224 */
225 static uint32_t dict_hash_name(char const *name, size_t len)
226 {
227 uint32_t hash = FNV_MAGIC_INIT;
228
229 char const *p = name, *q = name + len;
230
231 while (p < q) {
232 int c = *(unsigned char const *)p;
233 if (isalpha(c)) c = tolower(c);
234
235 hash *= FNV_MAGIC_PRIME;
236 hash ^= (uint32_t)(c & 0xff);
237 p++;
238 }
239
240 return hash;
241 }
242
243 /** Wrap name hash function for fr_dict_protocol_t
244 *
245 * @param[in] data fr_dict_attr_t to hash.
246 * @return the hash derived from the name of the attribute.
247 */
248 static uint32_t dict_protocol_name_hash(void const *data)
249 {
250 char const *name;
251
252 name = ((fr_dict_t const *)data)->root->name;
253
254 return dict_hash_name(name, strlen(name));
255 }
256
257 /** Compare two protocol names
258 *
259 */
260 static int dict_protocol_name_cmp(void const *one, void const *two)
261 {
262 fr_dict_t const *a = one;
263 fr_dict_t const *b = two;
264
265 return strcasecmp(a->root->name, b->root->name);
266 }
267
268 /** Hash a protocol number
269 *
270 */
271 static uint32_t dict_protocol_num_hash(void const *data)
272 {
273 return fr_hash(&(((fr_dict_t const *)data)->root->attr), sizeof(((fr_dict_t const *)data)->root->attr));
274 }
275
276 /** Compare two protocol numbers
277 *
278 */
279 static int dict_protocol_num_cmp(void const *one, void const *two)
280 {
281 fr_dict_t const *a = one;
282 fr_dict_t const *b = two;
283
284 return a->root->attr - b->root->attr;
285 }
286
287 /** Wrap name hash function for fr_dict_attr_t
288 *
289 * @param data fr_dict_attr_t to hash.
290 * @return the hash derived from the name of the attribute.
291 */
292 static uint32_t dict_attr_name_hash(void const *data)
293 {
294 char const *name;
295
296 name = ((fr_dict_attr_t const *)data)->name;
297
298 return dict_hash_name(name, strlen(name));
299 }
300
301 /** Compare two attribute names
302 *
303 */
304 static int dict_attr_name_cmp(void const *one, void const *two)
305 {
306 fr_dict_attr_t const *a = one, *b = two;
307
308 return strcasecmp(a->name, b->name);
309 }
310
311 /** Hash a combo attribute
312 *
313 */
314 static uint32_t dict_attr_combo_hash(void const *data)
315 {
316 uint32_t hash;
317 fr_dict_attr_t const *attr = data;
318
319 hash = fr_hash(&attr->parent, sizeof(attr->parent)); //-V568
320 hash = fr_hash_update(&attr->type, sizeof(attr->type), hash);
321 return fr_hash_update(&attr->attr, sizeof(attr->attr), hash);
322 }
323
324 /** Compare two combo attribute entries
325 *
326 */
327 static int dict_attr_combo_cmp(void const *one, void const *two)
328 {
329 fr_dict_attr_t const *a = one, *b = two;
330 int ret;
331
332 ret = (a->parent < b->parent) - (a->parent > b->parent);
333 if (ret != 0) return ret;
334
335 ret = (a->type < b->type) - (a->type > b->type);
336 if (ret != 0) return ret;
337
338 return (a->attr > b->attr) - (a->attr < b->attr);
339 }
340
341 /** Wrap name hash function for fr_dict_vendor_t
342 *
343 * @param data fr_dict_vendor_t to hash.
344 * @return the hash derived from the name of the attribute.
345 */
346 static uint32_t dict_vendor_name_hash(void const *data)
347 {
348 char const *name;
349
350 name = ((fr_dict_vendor_t const *)data)->name;
351
352 return dict_hash_name(name, strlen(name));
353 }
354
355 /** Compare two attribute names
356 *
357 */
358 static int dict_vendor_name_cmp(void const *one, void const *two)
359 {
360 fr_dict_vendor_t const *a = one;
361 fr_dict_vendor_t const *b = two;
362
363 return strcasecmp(a->name, b->name);
364 }
365
366 /** Hash a vendor number
367 *
368 */
369 static uint32_t dict_vendor_pen_hash(void const *data)
370 {
371 return fr_hash(&(((fr_dict_vendor_t const *)data)->pen),
372 sizeof(((fr_dict_vendor_t const *)data)->pen));
373 }
374
375 /** Compare two vendor numbers
376 *
377 */
378 static int dict_vendor_pen_cmp(void const *one, void const *two)
379 {
380 fr_dict_vendor_t const *a = one;
381 fr_dict_vendor_t const *b = two;
382
383 return a->pen - b->pen;
384 }
385
386 /** Hash a dictionary name
387 *
388 */
389 static uint32_t dict_enum_alias_hash(void const *data)
390 {
391 uint32_t hash;
392 fr_dict_enum_t const *enumv = data;
393
394 hash = dict_hash_name((void const *)enumv->alias, enumv->alias_len);
395
396 return fr_hash_update(&enumv->da, sizeof(enumv->da), hash); //-V568
397 }
398
399 /** Compare two dictionary attribute enum values
400 *
401 */
402 static int dict_enum_alias_cmp(void const *one, void const *two)
403 {
404 int rcode;
405 fr_dict_enum_t const *a = one;
406 fr_dict_enum_t const *b = two;
407
408 rcode = a->da - b->da;
409 if (rcode != 0) return rcode;
410
411 return strcasecmp(a->alias, b->alias);
412 }
413
414 /** Hash a dictionary enum value
415 *
416 */
417 static uint32_t dict_enum_value_hash(void const *data)
418 {
419 uint32_t hash = 0;
420 fr_dict_enum_t const *enumv = data;
421
422 hash = fr_hash_update((void const *)&enumv->da, sizeof(void *), hash); /* Cast to quiet static analysis */
423 return fr_hash_update((void const *)enumv->value, sizeof(void *), hash);
424 }
425
426 /** Compare two dictionary enum values
427 *
428 */
429 static int dict_enum_value_cmp(void const *one, void const *two)
430 {
431 fr_dict_enum_t const *a = one;
432 fr_dict_enum_t const *b = two;
433
434 return fr_value_box_cmp(a->value, b->value);
435 }
436
437 /** Validate a new attribute definition
438 *
439 * @todo we need to check length of none vendor attributes.
440 *
441 * @param[in] dict of protocol context we're operating in.
442 * If NULL the internal dictionary will be used.
443 * @param[in] parent to add attribute under.
444 * @param[in] name of the attribute.
445 * @param[in] attr number.
446 * @param[in] type of attribute.
447 * @param[in] flags to set in the attribute.
448 * @return
449 * - true if attribute definition is valid.
450 * - false if attribute definition is not valid.
451 */
452 static bool dict_attr_fields_valid(fr_dict_t *dict, fr_dict_attr_t const *parent,
453 char const *name, int *attr, fr_type_t type, fr_dict_attr_flags_t *flags)
454 {
455 fr_dict_attr_t const *v;
456
457 if (!fr_cond_assert(parent)) return false;
458
459 if (fr_dict_valid_name(name, -1) <= 0) return false;
460
461 /******************** sanity check attribute number ********************/
462
463 if (parent->flags.is_root) {
464 static unsigned int max_attr = UINT8_MAX + 1;
465
466 if (*attr == -1) {
467 if (fr_dict_attr_by_name(dict, name)) return false; /* exists, don't add it again */
468 *attr = ++max_attr;
469 flags->internal = 1;
470
471 } else if (*attr <= 0) {
472 fr_strerror_printf("ATTRIBUTE number %i is invalid, must be greater than zero", *attr);
473 return false;
474
475 } else if ((unsigned int) *attr > max_attr) {
476 max_attr = *attr;
477 }
478
479 /*
480 * Auto-set internal flags for raddb/dictionary.
481 * So that the end user doesn't have to know
482 * about internal implementation of the server.
483 */
484 if ((parent->flags.type_size == 1) &&
485 (*attr >= 3000) && (*attr < 4000)) {
486 flags->internal = true;
487 }
488 }
489
490 /*
491 * Any other negative attribute number is wrong.
492 */
493 if (*attr < 0) {
494 fr_strerror_printf("ATTRIBUTE number %i is invalid, must be greater than zero", *attr);
495 return false;
496 }
497
498 /*
499 * type_size is used to limit the maximum attribute number, so it's checked first.
500 */
501 if (flags->type_size) {
502 if ((type != FR_TYPE_TLV) && (type != FR_TYPE_VENDOR)) {
503 fr_strerror_printf("The 'format=' flag can only be used with attributes of type 'tlv'");
504 return false;
505 }
506
507 if ((flags->type_size != 1) &&
508 (flags->type_size != 2) &&
509 (flags->type_size != 4)) {
510 fr_strerror_printf("The 'format=' flag can only be used with attributes of type size 1,2 or 4");
511 return false;
512 }
513 }
514
515 /*
516 * If attributes have number greater than 255, do sanity checks.
517 *
518 * We assume that the root attribute is of type TLV, with
519 * the appropriate flags set for attributes in this
520 * space.
521 */
522 if ((*attr > UINT8_MAX) && !flags->internal) {
523 for (v = parent; v != NULL; v = v->parent) {
524 if ((v->type == FR_TYPE_TLV) || (v->type == FR_TYPE_VENDOR)) {
525 if ((v->flags.type_size < 4) &&
526 (*attr >= (1 << (8 * v->flags.type_size)))) {
527 fr_strerror_printf("Attributes must have value between 1..%u",
528 (1 << (8 * v->flags.type_size)) - 1);
529 return false;
530 }
531 break;
532 }
533 }
534 }
535
536 /******************** sanity check flags ********************/
537
538 /*
539 * virtual attributes are special.
540 */
541 if (flags->virtual) {
542 if (!parent->flags.is_root) {
543 fr_strerror_printf("The 'virtual' flag can only be used for normal attributes");
544 return false;
545 }
546
547 if (*attr <= (1 << (8 * parent->flags.type_size))) {
548 fr_strerror_printf("The 'virtual' flag can only be used for non-protocol attributes");
549 return false;
550 }
551 }
552
553 /*
554 * Tags can only be used in a few limited situations.
555 */
556 if (flags->has_tag) {
557 if ((type != FR_TYPE_UINT32) && (type != FR_TYPE_STRING)) {
558 fr_strerror_printf("The 'has_tag' flag can only be used for attributes of type 'integer' "
559 "or 'string'");
560 return false;
561 }
562
563 if (!(parent->flags.is_root ||
564 ((parent->type == FR_TYPE_VENDOR) &&
565 (parent->parent && parent->parent->type == FR_TYPE_VSA)))) {
566 fr_strerror_printf("The 'has_tag' flag can only be used with RFC and VSA attributes");
567 return false;
568 }
569
570 if (flags->array || flags->has_value || flags->concat || flags->virtual || flags->length) {
571 fr_strerror_printf("The 'has_tag' flag cannot be used with any other flag");
572 return false;
573 }
574
575 if (flags->encrypt && (flags->encrypt != FLAG_ENCRYPT_TUNNEL_PASSWORD)) {
576 fr_strerror_printf("The 'has_tag' flag can only be used with 'encrypt=2'");
577 return false;
578 }
579 }
580
581 /*
582 * 'concat' can only be used in a few limited situations.
583 */
584 if (flags->concat) {
585 if (type != FR_TYPE_OCTETS) {
586 fr_strerror_printf("The 'concat' flag can only be used for attributes of type 'octets'");
587 return false;
588 }
589
590 if (!parent->flags.is_root) {
591 fr_strerror_printf("The 'concat' flag can only be used with RFC attributes");
592 return false;
593 }
594
595 if (flags->array || flags->internal || flags->has_value || flags->virtual ||
596 flags->encrypt || flags->length) {
597 fr_strerror_printf("The 'concat' flag cannot be used any other flag");
598 return false;
599 }
600 }
601
602 /*
603 * 'octets[n]' can only be used in a few limited situations.
604 */
605 if (flags->length) {
606 if (flags->has_value || flags->virtual) {
607 fr_strerror_printf("The 'octets[...]' syntax cannot be used any other flag");
608 return false;
609 }
610
611 if (flags->length > 253) {
612 fr_strerror_printf("Invalid length %d", flags->length);
613 return NULL;
614 }
615
616 if ((type == FR_TYPE_TLV) || (type == FR_TYPE_VENDOR)) {
617 if ((flags->length != 1) &&
618 (flags->length != 2) &&
619 (flags->length != 4)) {
620 fr_strerror_printf("The 'length' flag can only be used with attributes of TLV lengths of 1,2 or 4");
621 return false;
622 }
623 } else if (type != FR_TYPE_OCTETS) {
624 fr_strerror_printf("The 'length' flag can only be set for attributes of type 'octets' or 'struct'");
625 return false;
626 }
627 }
628
629 /*
630 * Allow arrays anywhere.
631 */
632 if (flags->array) {
633 switch (type) {
634 default:
635 fr_strerror_printf("The 'array' flag cannot be used with attributes of type '%s'",
636 fr_int2str(fr_value_box_type_table, type, "<UNKNOWN>"));
637 return false;
638
639 case FR_TYPE_IPV4_ADDR:
640 case FR_TYPE_IPV6_ADDR:
641 case FR_TYPE_UINT8:
642 case FR_TYPE_UINT16:
643 case FR_TYPE_UINT32:
644 case FR_TYPE_DATE:
645 case FR_TYPE_STRING:
646 case FR_TYPE_OCTETS:
647 break;
648 }
649
650 if (flags->internal || flags->has_value || flags->encrypt || flags->virtual) {
651 fr_strerror_printf("The 'array' flag cannot be used any other flag");
652 return false;
653 }
654 }
655
656 /*
657 * 'has_value' should only be set internally. If the
658 * caller sets it, we still sanity check it.
659 */
660 if (flags->has_value) {
661 if (type != FR_TYPE_UINT32) {
662 fr_strerror_printf("The 'has_value' flag can only be used with attributes "
663 "of type 'integer'");
664 return false;
665 }
666
667 if (flags->encrypt || flags->virtual) {
668 fr_strerror_printf("The 'has_value' flag cannot be used with any other flag");
669 return false;
670 }
671 }
672
673 if (flags->encrypt) {
674 /*
675 * Stupid hacks for MS-CHAP-MPPE-Keys. The User-Password
676 * encryption method has no provisions for encoding the
677 * length of the data. For User-Password, the data is
678 * (presumably) all printable non-zero data. For
679 * MS-CHAP-MPPE-Keys, the data is binary crap. So... we
680 * MUST specify a length in the dictionary.
681 */
682 if ((flags->encrypt == FLAG_ENCRYPT_USER_PASSWORD) && (type != FR_TYPE_STRING)) {
683 if (type != FR_TYPE_OCTETS) {
684 fr_strerror_printf("The 'encrypt=1' flag can only be used with "
685 "attributes of type 'string'");
686 return false;
687 }
688
689 if (flags->length == 0) {
690 fr_strerror_printf("The 'encrypt=1' flag MUST be used with an explicit length for "
691 "'octets' data types");
692 return false;
693 }
694 }
695
696 if (flags->encrypt > FLAG_ENCRYPT_OTHER) {
697 fr_strerror_printf("The 'encrypt' flag can only be 0..4");
698 return false;
699 }
700
701 /*
702 * The Tunnel-Password encryption method can be used anywhere.
703 *
704 * We forbid User-Password and Ascend-Send-Secret
705 * methods in the extended space.
706 */
707 if ((flags->encrypt != FLAG_ENCRYPT_TUNNEL_PASSWORD) && !flags->internal && !parent->flags.internal) {
708 for (v = parent; v != NULL; v = v->parent) {
709 switch (v->type) {
710 case FR_TYPE_EXTENDED:
711 case FR_TYPE_LONG_EXTENDED:
712 case FR_TYPE_EVS:
713 fr_strerror_printf("The 'encrypt=%d' flag cannot be used with attributes "
714 "of type '%s'", flags->encrypt,
715 fr_int2str(fr_value_box_type_table, type, "<UNKNOWN>"));
716 return false;
717
718 default:
719 break;
720 }
721
722 }
723 }
724
725 switch (type) {
726 default:
727 encrypt_fail:
728 fr_strerror_printf("The 'encrypt' flag cannot be used with attributes of type '%s'",
729 fr_int2str(fr_value_box_type_table, type, "<UNKNOWN>"));
730 return false;
731
732 case FR_TYPE_TLV:
733 case FR_TYPE_IPV4_ADDR:
734 case FR_TYPE_UINT32:
735 case FR_TYPE_OCTETS:
736 if (flags->encrypt == FLAG_ENCRYPT_ASCEND_SECRET) goto encrypt_fail;
737
738 case FR_TYPE_STRING:
739 break;
740 }
741 }
742
743 /******************** sanity check data types and parents ********************/
744
745 /*
746 * Enforce restrictions on which data types can appear where.
747 */
748 switch (type) {
749 /*
750 * These types may only be parented from the root of the dictionary
751 */
752 case FR_TYPE_EXTENDED:
753 case FR_TYPE_LONG_EXTENDED:
754 // case FR_TYPE_VSA:
755 if (!parent->flags.is_root) {
756 fr_strerror_printf("Attributes of type '%s' can only be used in the RFC space",
757 fr_int2str(fr_value_box_type_table, type, "?Unknown?"));
758 return false;
759 }
760 break;
761
762 /*
763 * EVS may only occur under extended and long extended.
764 */
765 case FR_TYPE_EVS:
766 if ((parent->type != FR_TYPE_EXTENDED) && (parent->type != FR_TYPE_LONG_EXTENDED)) {
767 fr_strerror_printf("Attributes of type 'evs' MUST have a parent of type 'extended', "
768 "instead of '%s'", fr_int2str(fr_value_box_type_table, parent->type, "?Unknown?"));
769 return false;
770 }
771 break;
772
773 case FR_TYPE_VENDOR:
774 if ((parent->type != FR_TYPE_VSA) && (parent->type != FR_TYPE_EVS)) {
775 fr_strerror_printf("Attributes of type 'vendor' MUST have a parent of type 'vsa' or "
776 "'evs', instead of '%s'",
777 fr_int2str(fr_value_box_type_table, parent->type, "?Unknown?"));
778 return false;
779 }
780
781 if (parent->type == FR_TYPE_VSA) {
782 fr_dict_vendor_t const *dv;
783
784 dv = fr_dict_vendor_by_num(dict, *attr);
785 if (dv) {
786 flags->type_size = dv->type;
787 flags->length = dv->length;
788 } else {
789 flags->type_size = 1;
790 flags->length = 1;
791 }
792 } else {
793 flags->type_size = 1;
794 flags->length = 1;
795 }
796 break;
797
798 case FR_TYPE_TLV:
799 /*
800 * Ensure that type_size and length are set.
801 */
802 for (v = parent; v != NULL; v = v->parent) {
803 if ((v->type == FR_TYPE_TLV) || (v->type == FR_TYPE_VENDOR)) {
804 break;
805 }
806 }
807
808 /*
809 * root is always FR_TYPE_TLV, so we're OK.
810 */
811 if (!v) {
812 fr_strerror_printf("Attributes of type '%s' require a parent attribute",
813 fr_int2str(fr_value_box_type_table, type, "?Unknown?"));
814 return false;
815 }
816
817 /*
818 * Over-ride whatever was there before, so we
819 * don't have multiple formats of VSAs.
820 */
821 flags->type_size = v->flags.type_size;
822 flags->length = v->flags.length;
823 break;
824
825 case FR_TYPE_COMBO_IP_ADDR:
826 /*
827 * RFC 6929 says that this is a terrible idea.
828 */
829 for (v = parent; v != NULL; v = v->parent) {
830 if (v->type == FR_TYPE_VSA) {
831 break;
832 }
833 }
834
835 if (!v) {
836 fr_strerror_printf("Attributes of type '%s' can only be used in VSA dictionaries",
837 fr_int2str(fr_value_box_type_table, type, "?Unknown?"));
838 return false;
839 }
840 break;
841
842 case FR_TYPE_INVALID:
843 case FR_TYPE_TIMEVAL:
844 case FR_TYPE_FLOAT64:
845 case FR_TYPE_COMBO_IP_PREFIX:
846 fr_strerror_printf("Attributes of type '%s' cannot be used in dictionaries",
847 fr_int2str(fr_value_box_type_table, type, "?Unknown?"));
848 return false;
849
850 default:
851 break;
852 }
853
854 /*
855 * Force "length" for data types of fixed length;
856 */
857 switch (type) {
858 case FR_TYPE_UINT8:
859 case FR_TYPE_BOOL:
860 flags->length = 1;
861 break;
862
863 case FR_TYPE_UINT16:
864 flags->length = 2;
865 break;
866
867 case FR_TYPE_DATE:
868 case FR_TYPE_IPV4_ADDR:
869 case FR_TYPE_UINT32:
870 case FR_TYPE_INT32:
871 flags->length = 4;
872 break;
873
874 case FR_TYPE_UINT64:
875 flags->length = 8;
876 break;
877
878 case FR_TYPE_SIZE:
879 flags->length = sizeof(size_t);
880 break;
881
882 case FR_TYPE_ETHERNET:
883 flags->length = 6;
884 break;
885
886 case FR_TYPE_IFID:
887 flags->length = 8;
888 break;
889
890 case FR_TYPE_IPV6_ADDR:
891 flags->length = 16;
892 break;
893
894 case FR_TYPE_EXTENDED:
895 if (!parent->flags.is_root || (*attr < 241)) {
896 fr_strerror_printf("Attributes of type 'extended' MUST be "
897 "RFC attributes with value >= 241.");
898 return false;
899 }
900 flags->length = 0;
901 break;
902
903 case FR_TYPE_LONG_EXTENDED:
904 if (!parent->flags.is_root || (*attr < 241)) {
905 fr_strerror_printf("Attributes of type 'long-extended' MUST "
906 "be RFC attributes with value >= 241.");
907 return false;
908 }
909
910 flags->length = 0;
911 break;
912
913 case FR_TYPE_EVS:
914 if (*attr != FR_VENDOR_SPECIFIC) {
915 fr_strerror_printf("Attributes of type 'evs' MUST have attribute code 26, got %i", *attr);
916 return false;
917 }
918
919 flags->length = 0;
920 break;
921
922 /*
923 * The length is calculated from th children, not
924 * input as the flags.
925 */
926 case FR_TYPE_STRUCT:
927 flags->length = 0;
928
929 if (flags->encrypt != FLAG_ENCRYPT_NONE) {
930 fr_strerror_printf("Attributes of type 'struct' MUST NOT be encrypted.");
931 return false;
932 }
933
934 if (flags->internal || flags->has_tag || flags->array || flags->concat || flags->virtual) {
935 fr_strerror_printf("Invalid flag for attribute of type 'struct'");
936 return false;
937 }
938 break;
939
940 case FR_TYPE_STRING:
941 case FR_TYPE_OCTETS:
942 case FR_TYPE_TLV:
943 break;
944
945 default:
946 break;
947 }
948
949 /*
950 * Validate attribute based on parent.
951 */
952 if (parent->type == FR_TYPE_STRUCT) {
953 if (flags->encrypt != FLAG_ENCRYPT_NONE) {
954 fr_strerror_printf("Attributes inside a 'struct' MUST NOT be encrypted.");
955 return false;
956 }
957
958 if (flags->internal || flags->has_tag || flags->array || flags->concat || flags->virtual) {
959 fr_strerror_printf("Invalid flag for attribute inside a 'struct'");
960 return false;
961 }
962
963 if (*attr > 1) {
964 fr_dict_attr_t const *sibling;
965
966 sibling = fr_dict_attr_child_by_num(parent, (*attr) - 1);
967 if (!sibling) {
968 fr_strerror_printf("Children of 'struct' type attributes MUST be numbered consecutively.");
969 return false;
970 }
971
972 if (dict_attr_sizes[sibling->type][1] == ~(size_t) 0) {
973 fr_strerror_printf("Only the last child of a 'struct' attribute can have variable length");
974 return false;
975 }
976
977 } else {
978 /*
979 * The first child can't be variable length, that's stupid.
980 *
981 * STRUCTs will have their length filled in later.
982 */
983 if ((type != FR_TYPE_STRUCT) && (flags->length == 0)) {
984 fr_strerror_printf("Children of 'struct' type attributes MUST have fixed length.");
985 return false;
986 }
987 }
988 }
989
990 return true;
991 }
992
993 /** Allocate a dictionary attribute and assign a name
994 *
995 * @param[in] ctx to allocate attribute in.
996 * @param[in] name to set.
997 * @return
998 * - 0 on success.
999 * - -1 on failure (memory allocation error).
1000 */
1001 static fr_dict_attr_t *dict_attr_alloc_name(TALLOC_CTX *ctx, char const *name)
1002 {
1003 fr_dict_attr_t *da;
1004
1005 if (!name) {
1006 fr_strerror_printf("No attribute name provided");
1007 return NULL;
1008 }
1009
1010 da = talloc_zero(ctx, fr_dict_attr_t);
1011 da->name = talloc_typed_strdup(da, name);
1012 if (!da->name) {
1013 talloc_free(da);
1014 fr_strerror_printf("Out of memory");
1015 return NULL;
1016 }
1017
1018 return da;
1019 }
1020
1021 /** Initialise fields in a dictionary attribute structure
1022 *
1023 * @param[in] da to initialise.
1024 * @param[in] parent of the attribute, if none, should be
1025 * the dictionary root.
1026 * @param[in] attr number.
1027 * @param[in] type of the attribute.
1028 * @param[in] flags to assign.
1029 */
1030 static inline void dict_attr_init(fr_dict_attr_t *da,
1031 fr_dict_attr_t const *parent, int attr,
1032 fr_type_t type, fr_dict_attr_flags_t const *flags)
1033 {
1034 da->attr = attr;
1035 da->type = type;
1036 da->flags = *flags;
1037 da->parent = parent;
1038 da->depth = parent ? parent->depth + 1 : 0;
1039 }
1040
1041 /** Allocate a dictionary attribute on the heap
1042 *
1043 * @param[in] ctx to allocate the attribute in.
1044 * @param[in] parent of the attribute, if none, should be
1045 * the dictionary root.
1046 * @param[in] name of the attribute. If NULL an OID string
1047 * will be created and set as the name.
1048 * @param[in] attr number.
1049 * @param[in] type of the attribute.
1050 * @param[in] flags to assign.
1051 * @return
1052 * - A new fr_dict_attr_t on success.
1053 * - NULL on failure.
1054 */
1055 static fr_dict_attr_t *dict_attr_alloc(TALLOC_CTX *ctx,
1056 fr_dict_attr_t const *parent,
1057 char const *name, int attr,
1058 fr_type_t type, fr_dict_attr_flags_t const *flags)
1059 {
1060 fr_dict_attr_t *n;
1061
1062 if (!fr_cond_assert(parent)) return NULL;
1063
1064 /*
1065 * Allocate a new attribute
1066 */
1067 if (!name) {
1068 char buffer[FR_DICT_ATTR_MAX_NAME_LEN + 1];
1069 char *p = buffer;
1070 size_t len;
1071 fr_dict_attr_t tmp;
1072
1073 memset(&tmp, 0, sizeof(tmp));
1074 dict_attr_init(&tmp, parent, attr, type, flags);
1075
1076 len = snprintf(p, sizeof(buffer), "Attr-");
1077 p += len;
1078
1079 len = fr_dict_print_attr_oid(p, sizeof(buffer) - (p - buffer), NULL, &tmp);
1080 if (is_truncated(len, sizeof(buffer) - (p - buffer))) {
1081 fr_strerror_printf("OID string too long for unknown attribute");
1082 return NULL;
1083 }
1084
1085 n = dict_attr_alloc_name(ctx, buffer);
1086 } else {
1087 n = dict_attr_alloc_name(ctx, name);
1088 }
1089
1090 dict_attr_init(n, parent, attr, type, flags);
1091 DA_VERIFY(n);
1092
1093 return n;
1094 }
1095
1096 /** Copy a an existing attribute
1097 *
1098 * @param[in] ctx to allocate new attribute in.
1099 * @param[in] in attribute to copy.
1100 * @return
1101 * - A copy of the input fr_dict_attr_t on success.
1102 * - NULL on failure.
1103 */
1104 static fr_dict_attr_t *dict_attr_acopy(TALLOC_CTX *ctx, fr_dict_attr_t const *in)
1105 {
1106 fr_dict_attr_t *n;
1107
1108 n = dict_attr_alloc_name(ctx, in->name);
1109 if (!n) return NULL;
1110
1111 dict_attr_init(n, in->parent, in->attr, in->type, &in->flags);
1112 DA_VERIFY(n);
1113
1114 return n;
1115 }
1116
1117 /** Allocate a special "reference" attribute
1118 *
1119 * @param[in] dict of protocol context we're operating in.
1120 * If NULL the internal dictionary will be used.
1121 * @param[in] parent to add attribute under.
1122 * @param[in] name of the attribute.
1123 * @param[in] attr number.
1124 * @param[in] type of attribute.
1125 * @param[in] flags to set in the attribute.
1126 * @param[in] ref This reference attribute is pointing to.
1127 * @return
1128 * - 0 on success.
1129 * - -1 on failure.
1130 */
1131 static fr_dict_attr_t *dict_attr_ref_alloc(fr_dict_t *dict, fr_dict_attr_t const *parent,
1132 char const *name, int attr, fr_type_t type,
1133 fr_dict_attr_flags_t const *flags, fr_dict_attr_t const *ref)
1134 {
1135 fr_dict_attr_ref_t *ref_n;
1136
1137 if (!name) {
1138 fr_strerror_printf("No attribute name provided");
1139 return NULL;
1140 }
1141
1142 ref_n = talloc_zero(dict->pool, fr_dict_attr_ref_t);
1143 ref_n->tlv.name = talloc_typed_strdup(ref_n, name);
1144 if (!ref_n->tlv.name) {
1145 talloc_free(ref_n);
1146 fr_strerror_printf("Out of memory");
1147 return NULL;
1148 }
1149
1150 dict_attr_init(&ref_n->tlv, parent, attr, type, flags);
1151 ref_n->dict = fr_dict_by_da(ref); /* Cache the dictionary */
1152 ref_n->to = ref;
1153
1154 return (fr_dict_attr_t *)ref_n;
1155 }
1156
1157 /** Add a protocol to the global protocol table
1158 *
1159 * Inserts a protocol into the global protocol table. Uses the root attributes
1160 * of the dictionary for comparisons.
1161 *
1162 * @param[in] dict of protocol we're inserting.
1163 * @return
1164 * - 0 on success.
1165 * - -1 on failure.
1166 */
1167 static int dict_protocol_add(fr_dict_t *dict)
1168 {
1169 if (!dict->root) return -1; /* Should always have root */
1170
1171 if (!fr_hash_table_insert(protocol_by_name, dict)) {
1172 fr_dict_t *old_proto;
1173
1174 old_proto = fr_hash_table_finddata(protocol_by_name, dict);
1175 if (!old_proto) {
1176 fr_strerror_printf("%s: Failed inserting protocol name %s", __FUNCTION__, dict->root->name);
1177 return -1;
1178 }
1179
1180 if ((strcmp(old_proto->root->name, dict->root->name) == 0) &&
1181 (old_proto->root->name == dict->root->name)) {
1182 fr_strerror_printf("%s: Duplicate protocol name %s", __FUNCTION__, dict->root->name);
1183 return -1;
1184 }
1185
1186 return 0;
1187 }
1188 dict->in_protocol_by_name = true;
1189
1190 if (!fr_hash_table_insert(protocol_by_num, dict)) {
1191 fr_strerror_printf("%s: Duplicate protocol number %i", __FUNCTION__, dict->root->attr);
1192 return -1;
1193 }
1194 dict->in_protocol_by_num = true;
1195
1196 return 0;
1197 }
1198
1199 /** Add a vendor to the dictionary
1200 *
1201 * Inserts a vendor entry into the vendor hash table. This must be done before adding
1202 * attributes under a VSA.
1203 *
1204 * @param[in] dict of protocol context we're operating in.
1205 * If NULL the internal dictionary will be used.
1206 * @param[in] name of the vendor.
1207 * @param[in] num Vendor's Private Enterprise Number.
1208 * @return
1209 * - 0 on success.
1210 * - -1 on failure.
1211 */
1212 static int dict_vendor_add(fr_dict_t *dict, char const *name, unsigned int num)
1213 {
1214 size_t len;
1215 fr_dict_vendor_t *vendor;
1216
1217 INTERNAL_IF_NULL(dict, -1);
1218
1219 len = strlen(name);
1220 if (len >= FR_DICT_VENDOR_MAX_NAME_LEN) {
1221 fr_strerror_printf("%s: Vendor name too long", __FUNCTION__);
1222 return -1;
1223 }
1224
1225 vendor = talloc_zero(dict, fr_dict_vendor_t);
1226 vendor->name = talloc_typed_strdup(vendor, name);
1227 if (!vendor->name) {
1228 talloc_free(vendor);
1229 fr_strerror_printf("Out of memory");
1230 return -1;
1231 }
1232 vendor->pen = num;
1233 vendor->type = vendor->length = 1; /* defaults */
1234
1235 if (!fr_hash_table_insert(dict->vendors_by_name, vendor)) {
1236 fr_dict_vendor_t const *old_vendor;
1237
1238 old_vendor = fr_hash_table_finddata(dict->vendors_by_name, vendor);
1239 if (!old_vendor) {
1240 fr_strerror_printf("%s: Failed inserting vendor name %s", __FUNCTION__, name);
1241 return -1;
1242 }
1243 if ((strcmp(old_vendor->name, vendor->name) == 0) && (old_vendor->pen != vendor->pen)) {
1244 fr_strerror_printf("%s: Duplicate vendor name %s", __FUNCTION__, name);
1245 return -1;
1246 }
1247
1248 /*
1249 * Already inserted. Discard the duplicate entry.
1250 */
1251 talloc_free(vendor);
1252
1253 return 0;
1254 }
1255
1256 /*
1257 * Insert the SAME pointer (not free'd when this table is
1258 * deleted), into another table.
1259 *
1260 * We want this behaviour because we want OLD names for
1261 * the attributes to be read from the configuration
1262 * files, but when we're printing them, (and looking up
1263 * by value) we want to use the NEW name.
1264 */
1265 if (!fr_hash_table_replace(dict->vendors_by_num, vendor)) {
1266 fr_strerror_printf("%s: Failed inserting vendor %s", __FUNCTION__, name);
1267 return -1;
1268 }
1269
1270 return 0;
1271 }
1272
1273 /** Add a child to a parent.
1274 *
1275 * @param[in] parent we're adding a child to.
1276 * @param[in] child to add to parent.
1277 * @return
1278 * - 0 on success.
1279 * - -1 on failure (memory allocation error).
1280 */
1281 static inline int dict_attr_child_add(fr_dict_attr_t *parent, fr_dict_attr_t *child)
1282 {
1283 fr_dict_attr_t const * const *bin;
1284 fr_dict_attr_t **this;
1285
1286 /*
1287 * Setup fields in the child
1288 */
1289 child->parent = parent;
1290 child->depth = parent->depth + 1;
1291
1292 DA_VERIFY(child);
1293
1294 /*
1295 * We only allocate the pointer array *if* the parent has children.
1296 */
1297 if (!parent->children) parent->children = talloc_zero_array(parent, fr_dict_attr_t const *, UINT8_MAX + 1);
1298 if (!parent->children) {
1299 fr_strerror_printf("Out of memory");
1300 return -1;
1301 }
1302 /*
1303 * Treat the array as a hash of 255 bins, with attributes
1304 * sorted into bins using num % 255.
1305 *
1306 * Although the various protocols may define numbers higher than 255:
1307 *
1308 * RADIUS/DHCPv4 - 1-255
1309 * Diameter/Internal - 1-4294967295
1310 * DHCPv6 - 1-65535
1311 *
1312 * In reality very few will ever use attribute numbers > 500, so for
1313 * the majority of lookups we get O(1) performance.
1314 *
1315 * Attributes are inserted into the bin in order of their attribute
1316 * numbers to allow slightly more efficient lookups.
1317 */
1318 bin = &parent->children[child->attr & 0xff];
1319 for (;;) {
1320 bool child_is_struct = false;
1321 bool bin_is_struct = false;
1322
1323 if (!*bin) break;
1324
1325 /*
1326 * Workaround for vendors that overload the RFC space.
1327 * Structural attributes always take priority.
1328 */
1329 switch (child->type) {
1330 case FR_TYPE_STRUCTURAL:
1331 child_is_struct = true;
1332 break;
1333
1334 default:
1335 break;
1336 }
1337
1338 switch ((*bin)->type) {
1339 case FR_TYPE_STRUCTURAL:
1340 bin_is_struct = true;
1341 break;
1342
1343 default:
1344 break;
1345 }
1346
1347 if (child_is_struct && !bin_is_struct) break;
1348 else if (fr_dict_vendor_num_by_da(child) <= fr_dict_vendor_num_by_da(*bin)) break; /* Prioritise RFC attributes */
1349 else if (child->attr <= (*bin)->attr) break;
1350
1351 bin = &(*bin)->next;
1352 }
1353
1354 memcpy(&this, &bin, sizeof(this));
1355 child->next = *this;
1356 *this = child;
1357
1358 return 0;
1359 }
1360
1361 /** Add an attribute to the name table for the dictionary.
1362 *
1363 * @param[in] dict of protocol context we're operating in.
1364 * If NULL the internal dictionary will be used.
1365 * @param[in] da to add to the name lookup tables.
1366 * @return
1367 * - 0 on success.
1368 * - -1 on failure.
1369 */
1370 static int dict_attr_add_by_name(fr_dict_t *dict, fr_dict_attr_t *da)
1371 {
1372 /*
1373 * Insert the attribute, only if it's not a duplicate.
1374 */
1375 if (!fr_hash_table_insert(dict->attributes_by_name, da)) {
1376 fr_dict_attr_t *a;
1377
1378 /*
1379 * Find the old name. If it's the same name and
1380 * but the parent, or number, or type are
1381 * different, that's an error.
1382 */
1383 a = fr_hash_table_finddata(dict->attributes_by_name, da);
1384 if (a && (strcasecmp(a->name, da->name) == 0)) {
1385 if ((a->attr != da->attr) || (a->type != da->type) || (a->parent != da->parent)) {
1386 fr_strerror_printf("Duplicate attribute name \"%s\"", da->name);
1387 error:
1388 return -1;
1389 }
1390 }
1391
1392 /*
1393 * Otherwise the attribute has been redefined later
1394 * in the dictionary.
1395 *
1396 * The original fr_dict_attr_t remains in the
1397 * dictionary but entry in the name hash table is
1398 * updated to point to the new definition.
1399 */
1400 if (!fr_hash_table_replace(dict->attributes_by_name, da)) {
1401 fr_strerror_printf("Internal error storing attribute");
1402 goto error;
1403 }
1404 }
1405
1406 /*
1407 * Insert copies of the attribute into the
1408 * polymorphic attribute table.
1409 *
1410 * This allows an abstract attribute type
1411 * like combo IP to be resolved to a
1412 * concrete one later.
1413 */
1414 switch (da->type) {
1415 case FR_TYPE_COMBO_IP_ADDR:
1416 {
1417 fr_dict_attr_t *v4, *v6;
1418
1419 v4 = dict_attr_acopy(dict->pool, da);
1420 if (!v4) goto error;
1421 v4->type = FR_TYPE_IPV4_ADDR;
1422
1423 v6 = dict_attr_acopy(dict->pool, da);
1424 if (!v6) goto error;
1425 v6->type = FR_TYPE_IPV6_ADDR;
1426
1427 if (!fr_hash_table_replace(dict->attributes_combo, v4)) {
1428 fr_strerror_printf("Failed inserting IPv4 version of combo attribute");
1429 goto error;
1430 }
1431
1432 if (!fr_hash_table_replace(dict->attributes_combo, v6)) {
1433 fr_strerror_printf("Failed inserting IPv6 version of combo attribute");
1434 goto error;
1435 }
1436 break;
1437 }
1438
1439 case FR_TYPE_COMBO_IP_PREFIX:
1440 {
1441 fr_dict_attr_t *v4, *v6;
1442
1443 v4 = dict_attr_acopy(dict->pool, da);
1444 if (!v4) goto error;
1445 v4->type = FR_TYPE_IPV4_PREFIX;
1446
1447 v6 = dict_attr_acopy(dict->pool, da);
1448 if (!v6) goto error;
1449 v6->type = FR_TYPE_IPV6_PREFIX;
1450
1451 if (!fr_hash_table_replace(dict->attributes_combo, v4)) {
1452 fr_strerror_printf("Failed inserting IPv4 version of combo attribute");
1453 goto error;
1454 }
1455
1456 if (!fr_hash_table_replace(dict->attributes_combo, v6)) {
1457 fr_strerror_printf("Failed inserting IPv6 version of combo attribute");
1458 goto error;
1459 }
1460 break;
1461 }
1462
1463 default:
1464 break;
1465 }
1466
1467 return 0;
1468 }
1469
1470
1471 /** Add an reference to the dictionary
1472 *
1473 * @param[in] dict of protocol context we're operating in.
1474 * If NULL the internal dictionary will be used.
1475 * @param[in] parent to add attribute under.
1476 * @param[in] name of the attribute.
1477 * @param[in] attr number.
1478 * @param[in] type of attribute.
1479 * @param[in] flags to set in the attribute.
1480 * @param[in] ref The attribute we're referencing. May be in a foreign
1481 * dictionary.
1482 * @return
1483 * - 0 on success.
1484 * - -1 on failure.
1485 */
1486 static int dict_attr_ref_add(fr_dict_t *dict, fr_dict_attr_t const *parent,
1487 char const *name, int attr, fr_type_t type, fr_dict_attr_flags_t const *flags,
1488 fr_dict_attr_t const *ref)
1489 {
1490 fr_dict_attr_t *n;
1491 fr_dict_attr_t *mutable;
1492 fr_dict_attr_flags_t our_flags = *flags;
1493
1494 /*
1495 * Check that the definition is valid.
1496 */
1497 if (!dict_attr_fields_valid(dict, parent, name, &attr, type, &our_flags)) return -1;
1498
1499 /*
1500 * Check we're not creating a direct loop
1501 */
1502 if (ref->flags.is_reference) {
1503 fr_dict_attr_ref_t const *to_ref = talloc_get_type_abort_const(ref, fr_dict_attr_ref_t);
1504
1505 if (to_ref->to == ref) {
1506 fr_strerror_printf("Circular reference between \"%s\" and \"%s\"", name, ref->name);
1507 return -1;
1508 }
1509 }
1510
1511 /*
1512 * Check the referenced attribute is a root
1513 * or a TLV attribute.
1514 */
1515 if (!ref->flags.is_root && (ref->type != FR_TYPE_TLV)) {
1516 fr_strerror_printf("Referenced attribute \"%s\" must be of type '%s' not a 'tlv'", ref->name,
1517 fr_int2str(fr_value_box_type_table, ref->type, "<INVALID>"));
1518 return -1;
1519 }
1520
1521 /*
1522 * Reference must go from a TLV to a TLV
1523 */
1524 if (type != FR_TYPE_TLV) {
1525 fr_strerror_printf("Reference attribute must be of type 'tlv', not type '%s'",
1526 fr_int2str(fr_value_box_type_table, type, "<INVALID>"));
1527 return -1;
1528 }
1529
1530 n = dict_attr_ref_alloc(dict->pool, parent, name, attr, type, &our_flags, ref);
1531 if (!n) return -1;
1532
1533 if (dict_attr_add_by_name(dict, n) < 0) {
1534 error:
1535 talloc_free(n);
1536 return -1;
1537 }
1538
1539 /*
1540 * Setup parenting for the attribute
1541 */
1542 memcpy(&mutable, &parent, sizeof(mutable));
1543
1544 /*
1545 * Add in by number
1546 */
1547 if (dict_attr_child_add(mutable, n) < 0) goto error;
1548
1549 return 0;
1550 }
1551
1552 /** Add an attribute to the dictionary
1553 *
1554 * @param[in] dict of protocol context we're operating in.
1555 * If NULL the internal dictionary will be used.
1556 * @param[in] parent to add attribute under.
1557 * @param[in] name of the attribute.
1558 * @param[in] attr number.
1559 * @param[in] type of attribute.
1560 * @param[in] flags to set in the attribute.
1561 * @return
1562 * - 0 on success.
1563 * - -1 on failure.
1564 */
1565 int fr_dict_attr_add(fr_dict_t *dict, fr_dict_attr_t const *parent,
1566 char const *name, int attr, fr_type_t type, fr_dict_attr_flags_t const *flags)
1567 {
1568 fr_dict_attr_t *n;
1569 fr_dict_attr_t const *old;
1570 fr_dict_attr_t *mutable;
1571 fr_dict_attr_flags_t our_flags = *flags;
1572
1573 /*
1574 * Check that the definition is valid.
1575 */
1576 if (!dict_attr_fields_valid(dict, parent, name, &attr, type, &our_flags)) return -1;
1577
1578 /*
1579 * Suppress duplicates.
1580 */
1581 #define FLAGS_EQUAL(_x) (old->flags._x == flags->_x)
1582
1583 old = fr_dict_attr_by_name(dict, name);
1584 if (old) {
1585 if ((old->parent == parent) && (old->attr == (unsigned int) attr) && (old->type == type) &&
1586 FLAGS_EQUAL(has_tag) && FLAGS_EQUAL(array) && FLAGS_EQUAL(concat) && FLAGS_EQUAL(encrypt)) {
1587 return 0;
1588 }
1589
1590 if (old->parent != parent) {
1591 fr_strerror_printf_push("Cannot add duplicate name %s with different parent (old %s, new %s)",
1592 name, old->parent->name, parent->name);
1593 return -1;
1594 }
1595
1596 if (old->attr != (unsigned int) attr) {
1597 fr_strerror_printf_push("Cannot add duplicate name %s with different number (old %u, new %d)",
1598 name, old->attr, attr);
1599 return -1;
1600 }
1601
1602 if (old->type != type) {
1603 fr_strerror_printf_push("Cannot add duplicate name %s with different type (old %s, new %s)",
1604 name,
1605 fr_int2str(fr_value_box_type_table, old->type, "?Unknown?"),
1606 fr_int2str(fr_value_box_type_table, type, "?Unknown?"));
1607 return -1;
1608 }
1609
1610 fr_strerror_printf_push("Cannot add duplicate name %s with different flags",
1611 name);
1612 return -1;
1613 }
1614
1615 n = dict_attr_alloc(dict->pool, parent, name, attr, type, &our_flags);
1616 if (!n) return -1;
1617
1618 if (dict_attr_add_by_name(dict, n) < 0) {
1619 error:
1620 talloc_free(n);
1621 return -1;
1622 }
1623
1624 /*
1625 * Setup parenting for the attribute
1626 */
1627 memcpy(&mutable, &parent, sizeof(mutable));
1628
1629 /*
1630 * Add in by number
1631 */
1632 if (dict_attr_child_add(mutable, n) < 0) goto error;
1633
1634 return 0;
1635 }
1636
1637 /** Add a value alias
1638 *
1639 * Aliases are textual (string) aliases for a given value.
1640 *
1641 * Value aliases are not limited to integers, and may be added for any non-structural
1642 * attribute type.
1643 *
1644 * @param[in] da to add enumeration value to.
1645 * @param[in] alias Name of value alias.
1646 * @param[in] value to associate with alias.
1647 * @param[in] coerce if the type of the value does not match the
1648 * type of the da, attempt to cast it to match
1649 * the type of the da. If this is false and there's
1650 * a type mismatch, we fail.
1651 * We also fail if the value cannot be coerced to
1652 * the attribute type.
1653 * @param[in] takes_precedence This alias should take precedence over previous
1654 * aliases for the same value, when resolving value
1655 * to alias.
1656 * @return
1657 * - 0 on success.
1658 * - -1 on failure.
1659 */
1660 int fr_dict_enum_add_alias(fr_dict_attr_t const *da, char const *alias,
1661 fr_value_box_t const *value,
1662 bool coerce, bool takes_precedence)
1663 {
1664 size_t len;
1665 fr_dict_t *dict;
1666 fr_dict_enum_t *enumv = NULL;
1667 fr_value_box_t *enum_value = NULL;
1668
1669 if (!da) {
1670 fr_strerror_printf("%s: Dictionary attribute not specified", __FUNCTION__);
1671 return -1;
1672 }
1673
1674 if (!*alias) {
1675 fr_strerror_printf("%s: Empty names are not permitted", __FUNCTION__);
1676 return -1;
1677 }
1678
1679 len = strlen(alias);
1680 if (len >= FR_DICT_ENUM_MAX_NAME_LEN) {
1681 fr_strerror_printf("%s: Value name too long", __FUNCTION__);
1682 return -1;
1683 }
1684
1685 dict = fr_dict_by_da(da);
1686
1687 enumv = talloc_zero(dict->pool, fr_dict_enum_t);
1688 if (!enumv) {
1689 fr_strerror_printf("%s: Out of memory", __FUNCTION__);
1690 return -1;
1691 }
1692 enumv->alias = talloc_typed_strdup(enumv, alias);
1693 enumv->alias_len = strlen(alias);
1694 enum_value = fr_value_box_alloc(enumv, da->type, NULL, false);
1695
1696 if (da->type != value->type) {
1697 if (!coerce) {
1698 fr_strerror_printf("%s: Type mismatch between attribute (%s) and enum (%s)",
1699 __FUNCTION__,
1700 fr_int2str(fr_value_box_type_table, da->type, "<INVALID>"),
1701 fr_int2str(fr_value_box_type_table, value->type, "<INVALID>"));
1702 return -1;
1703 }
1704
1705 if (fr_value_box_cast(enumv, enum_value, da->type, NULL, value) < 0) {
1706 fr_strerror_printf_push("%s: Failed coercing enum type (%s) to attribute type (%s)",
1707 __FUNCTION__,
1708 fr_int2str(fr_value_box_type_table, value->type, "<INVALID>"),
1709 fr_int2str(fr_value_box_type_table, da->type, "<INVALID>"));
1710
1711 return -1;
1712 }
1713 } else {
1714 if (fr_value_box_copy(enum_value, enum_value, value) < 0) {
1715 fr_strerror_printf_push("%s: Failed copying value into enum", __FUNCTION__);
1716 return -1;
1717 }
1718 }
1719
1720 enumv->value = enum_value;
1721 enumv->da = da;
1722
1723 /*
1724 * Add the value into the dictionary.
1725 */
1726 {
1727 fr_dict_attr_t *tmp;
1728 memcpy(&tmp, &enumv, sizeof(tmp));
1729
1730 if (!fr_hash_table_insert(dict->values_by_alias, tmp)) {
1731 fr_dict_enum_t *old;
1732
1733 /*
1734 * Suppress duplicates with the same
1735 * name and value. There are lots in
1736 * dictionary.ascend.
1737 */
1738 old = fr_dict_enum_by_alias(da, alias, -1);
1739 if (!fr_cond_assert(old)) return -1;
1740
1741 if (fr_value_box_cmp(old->value, enumv->value) == 0) {
1742 talloc_free(enumv);
1743 return 0;
1744 }
1745
1746 fr_strerror_printf("Duplicate VALUE alias \"%s\" for attribute \"%s\". "
1747 "Old value was \"%pV\", new value was \"%pV\"", alias, da->name,
1748 old->value, enumv->value);
1749 talloc_free(enumv);
1750 return -1;
1751 }
1752 }
1753
1754 /*
1755 * There are multiple VALUE's, keyed by attribute, so we
1756 * take care of that here.
1757 */
1758 if (takes_precedence) {
1759 if (!fr_hash_table_replace(dict->values_by_da, enumv)) {
1760 fr_strerror_printf("%s: Failed inserting value %s", __FUNCTION__, alias);
1761 return -1;
1762 }
1763 } else {
1764 (void) fr_hash_table_insert(dict->values_by_da, enumv);
1765 }
1766
1767 /*
1768 * Mark the attribute up as having an enumv
1769 */
1770 {
1771 fr_dict_attr_t *mutable;
1772
1773 memcpy(&mutable, &da, sizeof(mutable));
1774
1775 mutable->flags.has_value = 1;
1776 }
1777
1778 return 0;
1779 }
1780
1781 /** Add an alias to an integer attribute hashing the alias for the integer value
1782 *
1783 */
1784 int fr_dict_enum_add_alias_next(fr_dict_attr_t const *da, char const *alias)
1785 {
1786 fr_value_box_t v = {
1787 .type = da->type
1788 };
1789 fr_value_box_t s = {
1790 .type = da->type
1791 };
1792
1793 if (fr_dict_enum_by_alias(da, alias, -1)) return 0;
1794
1795 switch (da->type) {
1796 case FR_TYPE_INT8:
1797 v.vb_int8 = s.vb_int8 = fr_hash_string(alias) & INT8_MAX;
1798 break;
1799
1800 case FR_TYPE_INT16:
1801 v.vb_int16 = s.vb_int16 = fr_hash_string(alias) & INT16_MAX;
1802 break;
1803
1804 case FR_TYPE_INT32:
1805 v.vb_int32 = s.vb_int32 = fr_hash_string(alias) & INT32_MAX;
1806 break;
1807
1808 case FR_TYPE_INT64:
1809 v.vb_int64 = s.vb_int64 = fr_hash_string(alias) & INT64_MAX;
1810 break;
1811
1812 case FR_TYPE_UINT8:
1813 v.vb_uint8 = s.vb_uint8 = fr_hash_string(alias) & UINT8_MAX;
1814 break;
1815
1816 case FR_TYPE_UINT16:
1817 v.vb_uint16 = s.vb_uint16 = fr_hash_string(alias) & UINT16_MAX;
1818 break;
1819
1820 case FR_TYPE_UINT32:
1821 v.vb_uint32 = s.vb_uint32 = fr_hash_string(alias) & UINT32_MAX;
1822 break;
1823
1824 case FR_TYPE_UINT64:
1825 v.vb_uint64 = s.vb_uint64 = fr_hash_string(alias) & UINT64_MAX;
1826 break;
1827
1828 default:
1829 fr_strerror_printf("Attribute is wrong type for auto-numbering, expected numeric type, got %s",
1830 fr_int2str(fr_value_box_type_table, da->type, "?Unknown?"));
1831 return -1;
1832 }
1833
1834 /*
1835 * If there's no existing value, add an enum
1836 * with the hash value of the alias.
1837 *
1838 * This helps with debugging as the values
1839 * are consistent.
1840 */
1841 if (!fr_dict_enum_by_value(da, &v)) {
1842 add:
1843 return fr_dict_enum_add_alias(da, alias, &v, false, false);
1844 }
1845
1846 for (;;) {
1847 fr_value_box_increment(&v);
1848
1849 if (fr_value_box_cmp_op(T_OP_CMP_EQ, &v, &s) == 0) {
1850 fr_strerror_printf("No free integer values for enumeration");
1851 return -1;
1852 }
1853
1854 if (!fr_dict_enum_by_value(da, &v)) goto add;
1855 }
1856 /* NEVER REACHED */
1857 }
1858
1859 /** Copy a known or unknown attribute to produce an unknown attribute
1860 *
1861 * Will copy the complete hierarchy down to the first known attribute.
1862 */
1863 fr_dict_attr_t *fr_dict_unknown_acopy(TALLOC_CTX *ctx, fr_dict_attr_t const *da)
1864 {
1865 fr_dict_attr_t *n;
1866 fr_dict_attr_t const *parent;
1867
1868 /*
1869 * Allocate an attribute.
1870 */
1871 n = dict_attr_alloc_name(ctx, da->name);
1872 if (!n) return NULL;
1873
1874 /*
1875 * We want to have parent / child relationships, AND to
1876 * copy all unknown parents, AND to free the unknown
1877 * parents when this 'da' is freed. We therefore talloc
1878 * the parent from the 'da'.
1879 */
1880 if (da->parent->flags.is_unknown) {
1881 parent = fr_dict_unknown_acopy(n, da->parent);
1882 if (!parent) {
1883 talloc_free(n);
1884 return NULL;
1885 }
1886
1887 } else {
1888 parent = da->parent;
1889 }
1890
1891 /*
1892 * Initialize the rest of the fields.
1893 */
1894 dict_attr_init(n, parent, da->attr, da->type, &da->flags);
1895
1896 DA_VERIFY(n);
1897
1898 return n;
1899 }
1900
1901 /** Converts an unknown to a known by adding it to the internal dictionaries.
1902 *
1903 * Does not free old #fr_dict_attr_t, that is left up to the caller.
1904 *
1905 * @param[in] dict of protocol context we're operating in.
1906 * If NULL the internal dictionary will be used.
1907 * @param[in] old unknown attribute to add.
1908 * @return
1909 * - Existing #fr_dict_attr_t if old was found in a dictionary.
1910 * - A new entry representing old.
1911 */
1912 fr_dict_attr_t const *fr_dict_unknown_add(fr_dict_t *dict, fr_dict_attr_t const *old)
1913 {
1914 fr_dict_attr_t const *da;
1915 fr_dict_attr_t const *parent;
1916 fr_dict_attr_flags_t flags;
1917
1918 da = fr_dict_attr_by_name(dict, old->name);
1919 if (da) return da;
1920
1921 /*
1922 * Define the complete unknown hierarchy
1923 */
1924 if (old->parent && old->parent->flags.is_unknown) {
1925 parent = fr_dict_unknown_add(dict, old->parent);
1926 if (!parent) {
1927 fr_strerror_printf_push("Failed adding parent \"%s\"", old->parent->name);
1928 return NULL;
1929 }
1930 } else {
1931 #ifdef __clang_analyzer__
1932 if (!old->parent) return NULL;
1933 #endif
1934 parent = old->parent;
1935 }
1936
1937 memcpy(&flags, &old->flags, sizeof(flags));
1938 flags.is_unknown = false;
1939 flags.is_raw = true;
1940
1941 /*
1942 * If this is a vendor, we skip most of the sanity
1943 * checks and add it to the vendor hash, and add it
1944 * as a child attribute to the Vendor-Specific
1945 * container.
1946 */
1947 if (old->type == FR_TYPE_VENDOR) {
1948 fr_dict_attr_t *mutable, *n;
1949
1950 if (dict_vendor_add(dict, old->name, old->attr) < 0) return NULL;
1951
1952 n = dict_attr_alloc(dict->pool, parent, old->name, old->attr, old->type, &flags);
1953
1954 /*
1955 * Setup parenting for the attribute
1956 */
1957 memcpy(&mutable, &old->parent, sizeof(mutable));
1958 if (dict_attr_child_add(mutable, n) < 0) return NULL;
1959
1960 return n;
1961 }
1962
1963 /*
1964 * Look up the attribute by number. If it doesn't exist,
1965 * add it both by name and by number. If it does exist,
1966 * add it only by name.
1967 */
1968 da = fr_dict_attr_child_by_num(parent, old->attr);
1969 if (da) {
1970 fr_dict_attr_t *n;
1971
1972 n = dict_attr_alloc(dict->pool, parent, old->name, old->attr, old->type, &flags);
1973 if (!n) return NULL;
1974
1975 /*
1976 * Add the unknown by NAME. e.g. if the admin does "Attr-26", we want
1977 * to return "Attr-26", and NOT "Vendor-Specific". The rest of the server
1978 * is responsible for converting "Attr-26 = 0x..." to an actual attribute,
1979 * if it so desires.
1980 */
1981 if (dict_attr_add_by_name(dict, n) < 0) {
1982 talloc_free(n);
1983 return NULL;
1984 }
1985
1986 return n;
1987 }
1988
1989 #ifdef __clang_analyzer__
1990 if (!old->name) return NULL;
1991 #endif
1992
1993 /*
1994 * Add the attribute by both name and number.
1995 */
1996 if (fr_dict_attr_add(dict, parent, old->name, old->attr, old->type, &flags) < 0) return NULL;
1997
1998 /*
1999 * For paranoia, return it by name.
2000 */
2001 return fr_dict_attr_by_name(dict, old->name);
2002 }
2003
2004 /** Free dynamically allocated (unknown attributes)
2005 *
2006 * If the da was dynamically allocated it will be freed, else the function
2007 * will return without doing anything.
2008 *
2009 * @param[in] da to free.
2010 */
2011 void fr_dict_unknown_free(fr_dict_attr_t const **da)
2012 {
2013 fr_dict_attr_t **tmp;
2014
2015 if (!da || !*da) return;
2016
2017 /* Don't free real DAs */
2018 if (!(*da)->flags.is_unknown) {
2019 return;
2020 }
2021
2022 memcpy(&tmp, &da, sizeof(*tmp));
2023 talloc_free(*tmp);
2024
2025 *tmp = NULL;
2026 }
2027
2028 /** Build an unknown vendor, parented by a VSA or EVS attribute
2029 *
2030 * This allows us to complete the path back to the dictionary root in the case
2031 * of unknown attributes with unknown vendors.
2032 *
2033 * @note Will return known vendors attributes where possible. Do not free directly,
2034 * use #fr_dict_unknown_free.
2035 *
2036 * @param[in] ctx to allocate the vendor attribute in.
2037 * @param[out] out Where to write point to new unknown dict attr
2038 * representing the unknown vendor.
2039 * @param[in] parent of the vendor attribute, either an EVS or VSA attribute.
2040 * @param[in] vendor id.
2041 * @return
2042 * - 0 on success.
2043 * - -1 on failure.
2044 */
2045 int fr_dict_unknown_vendor_afrom_num(TALLOC_CTX *ctx, fr_dict_attr_t **out,
2046 fr_dict_attr_t const *parent, unsigned int vendor)
2047 {
2048 fr_dict_attr_flags_t flags = {
2049 .is_unknown = true,
2050 .is_raw = true,
2051 .type_size = true,
2052 .length = true
2053 };
2054
2055 if (!fr_cond_assert(parent)) {
2056 fr_strerror_printf("%s: Invalid argument - parent was NULL", __FUNCTION__);
2057 return -1;
2058 }
2059
2060 *out = NULL;
2061
2062 /*
2063 * Vendor attributes can occur under VSA or EVS attributes.
2064 */
2065 switch (parent->type) {
2066 case FR_TYPE_VSA:
2067 case FR_TYPE_EVS:
2068 if (!fr_cond_assert(!parent->flags.is_unknown)) return -1;
2069
2070 *out = dict_attr_alloc(ctx, parent, NULL, vendor, FR_TYPE_VENDOR, &flags);
2071
2072 return 0;
2073
2074 case FR_TYPE_VENDOR:
2075 if (!fr_cond_assert(!parent->flags.is_unknown)) return -1;
2076 fr_strerror_printf("Unknown vendor cannot be parented by another vendor");
2077 return -1;
2078
2079 default:
2080 fr_strerror_printf("Unknown vendors can only be parented by 'vsa' or 'evs' "
2081 "attributes, not '%s'", fr_int2str(fr_value_box_type_table, parent->type, "?Unknown?"));
2082 return -1;
2083 }
2084 }
2085
2086 /** Allocates an unknown attribute
2087 *
2088 * @note If vendor != 0, an unknown vendor (may) also be created, parented by
2089 * the correct EVS or VSA attribute. This is accessible via da->parent,
2090 * and will be use the unknown da as its talloc parent.
2091 *
2092 * @param[in] ctx to allocate DA in.
2093 * @param[in] parent of the unknown attribute (may also be unknown).
2094 * @param[in] attr number.
2095 * @param[in] vendor number.
2096 * @return 0 on success.
2097 */
2098 fr_dict_attr_t const *fr_dict_unknown_afrom_fields(TALLOC_CTX *ctx, fr_dict_attr_t const *parent,
2099 unsigned int vendor, unsigned int attr)
2100 {
2101 fr_dict_attr_t const *da;
2102 fr_dict_attr_t *n;
2103 fr_dict_attr_t *new_parent = NULL;
2104 fr_dict_attr_flags_t flags = {
2105 .is_unknown = true,
2106 .is_raw = true,
2107 };
2108
2109 if (!fr_cond_assert(parent)) {
2110 fr_strerror_printf("%s: Invalid argument - parent was NULL", __FUNCTION__);
2111 return NULL;
2112 }
2113
2114 /*
2115 * If there's a vendor specified, we check to see
2116 * if the parent is a VSA or EVS, and if it is
2117 * we either lookup the vendor to get the correct
2118 * attribute, or bridge the gap in the tree, with an
2119 * unknown vendor.
2120 *
2121 * We need to do the check, as the parent could be
2122 * a TLV, in which case the vendor should be known
2123 * and we don't need to modify the parent.
2124 */
2125 if (vendor && ((parent->type == FR_TYPE_VSA) || (parent->type == FR_TYPE_EVS))) {
2126 da = fr_dict_attr_child_by_num(parent, vendor);
2127 if (!da) {
2128 if (fr_dict_unknown_vendor_afrom_num(ctx, &new_parent, parent, vendor) < 0) return NULL;
2129 da = new_parent;
2130 }
2131 parent = da;
2132
2133 /*
2134 * Need to clone the unknown hierachy, as unknown
2135 * attributes must parent the complete heirachy,
2136 * and cannot share any parts with any other unknown
2137 * attributes.
2138 */
2139 } else if (parent->flags.is_unknown) {
2140 new_parent = fr_dict_unknown_acopy(ctx, parent);
2141 parent = new_parent;
2142 }
2143
2144 n = dict_attr_alloc(ctx, parent, NULL, attr, FR_TYPE_OCTETS, &flags);
2145 if (!n) return NULL;
2146
2147 /*
2148 * The config files may reference the unknown by name.
2149 * If so, use the pre-defined name instead of an unknown
2150 * one.!
2151 */
2152 da = fr_dict_attr_by_name(fr_dict_by_da(parent), n->name);
2153 if (da) {
2154 fr_dict_unknown_free(&parent);
2155 parent = n;
2156 fr_dict_unknown_free(&parent);
2157 return da;
2158 }
2159
2160 /*
2161 * Ensure the parent is freed at the same time as the
2162 * unknown DA. This should be OK as we never parent
2163 * multiple unknown attributes off the same parent.
2164 */
2165 if (new_parent && new_parent->flags.is_unknown) talloc_steal(n, new_parent);
2166
2167 return n;
2168 }
2169
2170 /** Initialise a fr_dict_attr_t from an ASCII attribute and value
2171 *
2172 * Where the attribute name is in the form:
2173 * - Attr-%d
2174 * - Attr-%d.%d.%d...
2175 *
2176 * @copybrief fr_dict_unknown_afrom_fields
2177 *
2178 * @param[in] ctx to allocate the attribute in.
2179 * @param[out] out Where to write the new attribute to.
2180 * @param[in] parent of the unknown attribute (may also be unknown).
2181 * @param[in] num of the unknown attribute.
2182 * @return
2183 * - 0 on success.
2184 * - -1 on failure.
2185 */
2186 static int dict_unknown_attr_afrom_num(TALLOC_CTX *ctx, fr_dict_attr_t **out,
2187 fr_dict_attr_t const *parent, unsigned long num)
2188 {
2189 fr_dict_attr_t *da;
2190 fr_dict_attr_flags_t flags = {
2191 .is_unknown = true,
2192 .is_raw = true,
2193 };
2194
2195 if (!fr_cond_assert(parent)) {
2196 fr_strerror_printf("%s: Invalid argument - parent was NULL", __FUNCTION__);
2197 return -1;
2198 }
2199
2200 *out = NULL;
2201
2202 da = dict_attr_alloc(ctx, parent, NULL, num, FR_TYPE_OCTETS, &flags);
2203 if (!da) return -1;
2204
2205 *out = da;
2206
2207 return 0;
2208 }
2209
2210 /** Create a fr_dict_attr_t from an ASCII attribute and value
2211 *
2212 * Where the attribute name is in the form:
2213 * - Attr-%d
2214 * - Attr-%d.%d.%d...
2215 *
2216 * @copybrief fr_dict_unknown_afrom_fields
2217 *
2218 * @note If vendor != 0, an unknown vendor (may) also be created, parented by
2219 * the correct EVS or VSA attribute. This is accessible via vp->parent,
2220 * and will be use the unknown da as its talloc parent.
2221 *
2222 * @param[in] ctx to alloc new attribute in.
2223 * @param[out] out Where to write the head of the chain unknown
2224 * dictionary attributes.
2225 * @param[in] parent Attribute to use as the root for resolving OIDs in.
2226 * Usually the root of a protocol dictionary.
2227 * @param[in] oid_str of attribute.
2228 * @return
2229 * - The number of bytes parsed on success.
2230 * - <= 0 on failure. Negative offset indicates parse error position.
2231 */
2232 ssize_t fr_dict_unknown_afrom_oid_str(TALLOC_CTX *ctx, fr_dict_attr_t **out,
2233 fr_dict_attr_t const *parent, char const *oid_str)
2234 {
2235 char const *p = oid_str, *end = oid_str + strlen(oid_str);
2236 fr_dict_attr_t const *our_parent = parent;
2237 fr_dict_attr_t *n = NULL, *our_da;
2238 fr_dict_attr_flags_t flags = {
2239 .is_unknown = true,
2240 .is_raw = true,
2241 };
2242
2243 if (!fr_cond_assert(parent)) {
2244 fr_strerror_printf("%s: Invalid argument - parent was NULL", __FUNCTION__);
2245 return -1;
2246 }
2247
2248 *out = NULL;
2249
2250 if (fr_dict_valid_oid_str(oid_str, -1) < 0) return -1;
2251
2252 /*
2253 * All unknown attributes are of the form "Attr-#.#.#.#"
2254 */
2255 if (strncasecmp(p, "Attr-", 5) != 0) {
2256 fr_strerror_printf("Unknown attribute '%s'", oid_str);
2257 return 0;
2258 }
2259 p += 5;
2260
2261 /*
2262 * Allocate the final attribute first, so that any
2263 * unknown parents can be freed when this da is freed.
2264 *
2265 * See fr_dict_unknown_acopy() for more details.
2266 *
2267 * Note also that we copy the input name, even if it is
2268 * not normalized.
2269 *
2270 * While the name of this attribute is "Attr-#.#.#", one
2271 * or more of the leading components may, in fact, be
2272 * known.
2273 */
2274 n = dict_attr_alloc_name(ctx, oid_str);
2275
2276 /*
2277 * While the name of this attribu
2278 */
2279 do {
2280 unsigned int num;
2281 fr_dict_attr_t const *da = NULL;
2282
2283 if (fr_dict_oid_component(&num, &p) < 0) {
2284 error:
2285 talloc_free(n);
2286 return -(p - oid_str);
2287 }
2288
2289 switch (*p) {
2290 /*
2291 * Structural attribute
2292 */
2293 case '.':
2294 if (!our_parent) goto is_root;
2295
2296 da = fr_dict_attr_child_by_num(our_parent, num);
2297 if (!da) { /* Unknown component */
2298 switch (our_parent->type) {
2299 case FR_TYPE_EVS:
2300 case FR_TYPE_VSA:
2301 da = fr_dict_attr_child_by_num(our_parent, num);
2302 if (!fr_cond_assert(!da || (da->type == FR_TYPE_VENDOR))) goto error;
2303
2304 if (!da) {
2305 if (fr_dict_unknown_vendor_afrom_num(n, &our_da,
2306 our_parent, num) < 0) {
2307 goto error;
2308 }
2309 da = our_da;
2310 }
2311 break;
2312
2313 case FR_TYPE_TLV:
2314 case FR_TYPE_EXTENDED:
2315 case FR_TYPE_LONG_EXTENDED:
2316 is_root:
2317 if (dict_unknown_attr_afrom_num(n, &our_da, our_parent, num) < 0) {
2318 goto error;
2319 }
2320 da = our_da;
2321 break;
2322
2323 /*
2324 * Can't have a FR_TYPE_STRING inside a
2325 * FR_TYPE_STRING (for example)
2326 */
2327 default:
2328 fr_strerror_printf("Parent OID component (%s) in \"%.*s\" specified a "
2329 "non-structural type (%s)", our_parent->name,
2330 (int)(p - oid_str), oid_str,
2331 fr_int2str(fr_value_box_type_table,
2332 our_parent->type, "<INVALID>"));
2333 goto error;
2334 }
2335 }
2336 our_parent = da;
2337 break;
2338
2339 /*
2340 * Leaf attribute
2341 */
2342 case '\0':
2343 dict_attr_init(n, our_parent, num, FR_TYPE_OCTETS, &flags);
2344 break;
2345 }
2346 p++;
2347 } while (p < end);
2348
2349 /*
2350 * @todo - if we really care about normalization, re-print the name here, normalized.
2351 */
2352
2353 DA_VERIFY(n);
2354
2355 *out = n;
2356
2357 return end - oid_str;
2358 }
2359
2360 /** Create a dictionary attribute by name embedded in another string
2361 *
2362 * Find the first invalid attribute name char in the string pointed to by name.
2363 *
2364 * Copy the characters between the start of the name string and the first none
2365 * #fr_dict_attr_allowed_chars char to a buffer and initialise da as an unknown
2366 * attribute.
2367 *
2368 * @param[in] ctx To allocate unknown #fr_dict_attr_t in.
2369 * @param[out] out Where to write the head of the chain unknown
2370 * dictionary attributes.
2371 * @param[in] parent Attribute to use as the root for resolving OIDs in.
2372 * Usually the root of a protocol dictionary.
2373 * @param[in] name string start.
2374 * @return
2375 * - <= 0 on failure.
2376 * - The number of bytes of name consumed on success.
2377 */
2378 ssize_t fr_dict_unknown_afrom_oid_substr(TALLOC_CTX *ctx, fr_dict_attr_t **out,
2379 fr_dict_attr_t const *parent, char const *name)
2380 {
2381 char const *p;
2382 size_t len;
2383 char buffer[FR_DICT_ATTR_MAX_NAME_LEN + 1];
2384 ssize_t slen;
2385
2386 if (!name || !*name) return 0;
2387
2388 /*
2389 * Advance p until we get something that's not part of
2390 * the dictionary attribute name.
2391 */
2392 for (p = name; fr_dict_attr_allowed_chars[(uint8_t)*p] || (*p == '.') || (*p == '-'); p++);
2393
2394 len = p - name;
2395 if (len > FR_DICT_ATTR_MAX_NAME_LEN) {
2396 fr_strerror_printf("Attribute name too long");
2397 return 0;
2398 }
2399 if (len == 0) {
2400 fr_strerror_printf("Invalid attribute name");
2401 return 0;
2402 }
2403 strlcpy(buffer, name, len + 1);
2404
2405 slen = fr_dict_unknown_afrom_oid_str(ctx, out, parent, buffer);
2406 if (slen <= 0) return slen;
2407
2408 return p - name;
2409 }
2410
2411
2412 /** Check to see if we can convert a nested TLV structure to known attributes
2413 *
2414 * @param[in] dict to search in.
2415 * @param[in] da Nested tlv structure to convert.
2416 * @return
2417 * - NULL if we can't.
2418 * - Known attribute if we can.
2419 */
2420 fr_dict_attr_t const *fr_dict_attr_known(fr_dict_t *dict, fr_dict_attr_t const *da)
2421 {
2422 INTERNAL_IF_NULL(dict, NULL);
2423
2424 if (!da->flags.is_unknown) return da; /* It's known */
2425
2426 if (da->parent) {
2427 fr_dict_attr_t const *parent;
2428
2429 parent = fr_dict_attr_known(dict, da->parent);
2430 if (!parent) return NULL;
2431
2432 return fr_dict_attr_child_by_num(parent, da->attr);
2433 }
2434
2435 if (dict->root == da) return dict->root;
2436 return NULL;
2437 }
2438
2439 ssize_t fr_dict_snprint_flags(char *out, size_t outlen, fr_dict_attr_flags_t const *flags)
2440 {
2441 char *p = out, *end = p + outlen;
2442 size_t len;
2443
2444 out[0] = '\0';
2445
2446 #define FLAG_SET(_flag) \
2447 do { \
2448 if (flags->_flag) {\
2449 p += strlcpy(p, STRINGIFY(_flag)",", end - p);\
2450 if (p >= end) return -1;\
2451 }\
2452 } while (0)
2453
2454 FLAG_SET(is_root);
2455 FLAG_SET(is_unknown);
2456 FLAG_SET(is_raw);
2457 FLAG_SET(is_reference);
2458 FLAG_SET(internal);
2459 FLAG_SET(has_tag);
2460 FLAG_SET(array);
2461 FLAG_SET(has_value);
2462 FLAG_SET(concat);
2463 FLAG_SET(virtual);
2464 FLAG_SET(named);
2465
2466 if (flags->encrypt) {
2467 p += snprintf(p, end - p, "encrypt=%i,", flags->encrypt);
2468 if (p >= end) return -1;
2469 }
2470
2471 if (flags->length) {
2472 p += snprintf(p, end - p, "length=%i,", flags->length);
2473 if (p >= end) return -1;
2474 }
2475
2476 if (!out[0]) return -1;
2477
2478 /*
2479 * Trim the comma
2480 */
2481 len = strlen(out);
2482 if (out[len - 1] == ',') out[len - 1] = '\0';
2483
2484 return len;
2485 }
2486
2487 void fr_dict_print(fr_dict_attr_t const *da, int depth)
2488 {
2489 char buff[256];
2490 unsigned int i;
2491 char const *name;
2492
2493 fr_dict_snprint_flags(buff, sizeof(buff), &da->flags);
2494
2495 switch (da->type) {
2496 case FR_TYPE_VSA:
2497 name = "VSA";
2498 break;
2499
2500 case FR_TYPE_EXTENDED:
2501 name = "EXTENDED";
2502 break;
2503
2504 case FR_TYPE_TLV:
2505 name = "TLV";
2506 break;
2507
2508 case FR_TYPE_EVS:
2509 name = "EVS";
2510 break;
2511
2512 case FR_TYPE_VENDOR:
2513 name = "VENDOR";
2514 break;
2515
2516 case FR_TYPE_LONG_EXTENDED:
2517 name = "LONG EXTENDED";
2518 break;
2519
2520 case FR_TYPE_STRUCT:
2521 name = "STRUCT";
2522 break;
2523
2524 case FR_TYPE_GROUP:
2525 name = "GROUP";
2526 break;
2527
2528 default:
2529 name = "ATTRIBUTE";
2530 break;
2531 }
2532
2533 printf("%u%.*s%s \"%s\" vendor: %x (%u), num: %x (%u), type: %s, flags: %s\n", da->depth, depth,
2534 "\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t", name, da->name,
2535 fr_dict_vendor_num_by_da(da), fr_dict_vendor_num_by_da(da), da->attr, da->attr,
2536 fr_int2str(fr_value_box_type_table, da->type, "?Unknown?"), buff);
2537
2538 if (da->children) for (i = 0; i < talloc_array_length(da->children); i++) {
2539 if (da->children[i]) {
2540 fr_dict_attr_t const *bin;
2541
2542 for (bin = da->children[i]; bin; bin = bin->next) fr_dict_print(bin, depth + 1);
2543 }
2544 }
2545 }
2546
2547 /** Find a common ancestor that two TLV type attributes share
2548 *
2549 * @param[in] a first TLV attribute.
2550 * @param[in] b second TLV attribute.
2551 * @param[in] is_ancestor Enforce a->b relationship (a is parent or ancestor of b).
2552 * @return
2553 * - Common ancestor if one exists.
2554 * - NULL if no common ancestor exists.
2555 */
2556 fr_dict_attr_t const *fr_dict_parent_common(fr_dict_attr_t const *a, fr_dict_attr_t const *b, bool is_ancestor)
2557 {
2558 unsigned int i;
2559 fr_dict_attr_t const *p_a, *p_b;
2560
2561 if (!a || !b) return NULL;
2562
2563 if (is_ancestor && (b->depth <= a->depth)) return NULL;
2564
2565 /*
2566 * Find a common depth to work back from
2567 */
2568 if (a->depth > b->depth) {
2569 p_b = b;
2570 for (p_a = a, i = a->depth - b->depth; p_a && (i > 0); p_a = p_a->parent, i--);
2571 } else if (a->depth < b->depth) {
2572 p_a = a;
2573 for (p_b = b, i = b->depth - a->depth; p_b && (i > 0); p_b = p_b->parent, i--);
2574 } else {
2575 p_a = a;
2576 p_b = b;
2577 }
2578
2579 while (p_a && p_b) {
2580 if (p_a == p_b) return p_a;
2581
2582 p_a = p_a->parent;
2583 p_b = p_b->parent;
2584 }
2585
2586 return NULL;
2587 }
2588
2589 /** Process a single OID component
2590 *
2591 * @param[out] out Value of component.
2592 * @param[in] oid string to parse.
2593 * @return
2594 * - 0 on success.
2595 * - -1 on format error.
2596 */
2597 int fr_dict_oid_component(unsigned int *out, char const **oid)
2598 {
2599 char const *p = *oid;
2600 char *q;
2601 unsigned long num;
2602
2603 *out = 0;
2604
2605 num = strtoul(p, &q, 10);
2606 if ((p == q) || (num == ULONG_MAX)) {
2607 fr_strerror_printf("Invalid OID component \"%s\" (%lu)", p, num);
2608 return -1;
2609 }
2610
2611 switch (*q) {
2612 case '\0':
2613 case '.':
2614 *oid = q;
2615 *out = (unsigned int)num;
2616
2617 return 0;
2618
2619 default:
2620 fr_strerror_printf("Unexpected text after OID component");
2621 *out = 0;
2622 return -1;
2623 }
2624 }
2625
2626 /** Build the tlv_stack for the specified DA and encode the path in OID form
2627 *
2628 * @param[out] out Where to write the OID.
2629 * @param[in] outlen Length of the output buffer.
2630 * @param[in] ancestor If not NULL, only print OID portion between
2631 * ancestor and da.
2632 * @param[in] da to print OID string for.
2633 * @return the number of bytes written to the buffer.
2634 */
2635 size_t fr_dict_print_attr_oid(char *out, size_t outlen,
2636 fr_dict_attr_t const *ancestor, fr_dict_attr_t const *da)
2637 {
2638 size_t len;
2639 char *p = out, *end = p + outlen;
2640 int i;
2641 int depth = 0;
2642 fr_dict_attr_t const *tlv_stack[FR_DICT_MAX_TLV_STACK + 1];
2643
2644 if (!outlen) return 0;
2645
2646 /*
2647 * If the ancestor and the DA match, there's
2648 * no OID string to print.
2649 */
2650 if (ancestor == da) {
2651 out[0] = '\0';
2652 return 0;
2653 }
2654
2655 fr_proto_tlv_stack_build(tlv_stack, da);
2656
2657 if (ancestor) {
2658 if (tlv_stack[ancestor->depth - 1] != ancestor) {
2659 fr_strerror_printf("Attribute \"%s\" is not a descendent of \"%s\"", da->name, ancestor->name);
2660 return -1;
2661 }
2662 depth = ancestor->depth;
2663 }
2664
2665 /*
2666 * We don't print the ancestor, we print the OID
2667 * between it and the da.
2668 */
2669 len = snprintf(p, end - p, "%u", tlv_stack[depth]->attr);
2670 if ((p + len) >= end) return p - out;
2671 p += len;
2672
2673
2674 for (i = depth + 1; i < (int)da->depth; i++) {
2675 len = snprintf(p, end - p, ".%u", tlv_stack[i]->attr);
2676 if ((p + len) >= end) return p - out;
2677 p += len;
2678 }
2679
2680 return p - out;
2681 }
2682
2683 /** Get the leaf attribute of an OID string
2684 *
2685 * @note On error, vendor will be set (if present), parent will be the
2686 * maximum depth we managed to resolve to, and attr will be the child
2687 * we failed to resolve.
2688 *
2689 * @param[in] dict of protocol context we're operating in.
2690 * If NULL the internal dictionary will be used.
2691 * @param[out] attr Number we parsed.
2692 * @param[in,out] parent attribute (or root of dictionary).
2693 * Will be updated to the parent directly beneath the leaf.
2694 * @param[in] oid string to parse.
2695 * @return
2696 * - > 0 on success (number of bytes parsed).
2697 * - <= 0 on parse error (negative offset of parse error).
2698 */
2699 ssize_t fr_dict_attr_by_oid(fr_dict_t *dict, fr_dict_attr_t const **parent, unsigned int *attr, char const *oid)
2700 {
2701 char const *p = oid;
2702 unsigned int num = 0;
2703 ssize_t slen;
2704
2705 /*
2706 * It's a partial OID. Grab it, and skip to the next bit.
2707 */
2708 if (p[0] == '.') {
2709 p++;
2710 }
2711
2712 *attr = 0;
2713
2714 if (fr_dict_oid_component(&num, &p) < 0) return oid - p;
2715
2716 /*
2717 * Record progress even if we error out.
2718 *
2719 * Don't change this, you will break things.
2720 */
2721 *attr = num;
2722
2723 switch ((*parent)->type) {
2724 case FR_TYPE_STRUCTURAL:
2725 break;
2726
2727 default:
2728 fr_strerror_printf("Attribute %s (%i) is not a TLV, so cannot contain a child attribute. "
2729 "Error at sub OID \"%s\"", (*parent)->name, (*parent)->attr, oid);
2730 return 0; /* We parsed nothing */
2731 }
2732
2733 /*
2734 * If it's not a vendor type, it must be between 0..8*type_size
2735 *
2736 * @fixme: find the TLV parent, and check it's size
2737 */
2738 if (((*parent)->type != FR_TYPE_VENDOR) && ((*parent)->type != FR_TYPE_VSA) && !(*parent)->flags.is_root &&
2739 (num > UINT8_MAX)) {
2740 fr_strerror_printf("TLV attributes must be between 0..255 inclusive");
2741 return 0;
2742 }
2743
2744 switch (p[0]) {
2745 /*
2746 * We've not hit the leaf yet, so the attribute must be
2747 * defined already.
2748 */
2749 case '.':
2750 {
2751 fr_dict_attr_t const *child;
2752 p++;
2753
2754 child = fr_dict_attr_child_by_num(*parent, num);
2755 if (!child) {
2756 fr_strerror_printf("Unknown attribute \"%i\" in OID string \"%s\"", num, oid);
2757 return 0; /* We parsed nothing */
2758 }
2759
2760 /*
2761 * Record progress even if we error out.
2762 *
2763 * Don't change this, you will break things.
2764 */
2765 *parent = child;
2766
2767 slen = fr_dict_attr_by_oid(dict, parent, attr, p);
2768 if (slen <= 0) return slen - (p - oid);
2769 return slen + (p - oid);
2770 }
2771
2772 /*
2773 * Hit the leaf, this is the attribute we need to define.
2774 */
2775 case '\0':
2776 *attr = num;
2777 return p - oid;
2778
2779 default:
2780 fr_strerror_printf("Malformed OID string, got trailing garbage '%s'", p);
2781 return oid - p;
2782 }
2783 }
2784
2785 /** Return the root attribute of a dictionary
2786 *
2787 * @param dict to return root for.
2788 * @return the root attribute of the dictionary.
2789 */
2790 fr_dict_attr_t const *fr_dict_root(fr_dict_t const *dict)
2791 {
2792 if (!dict) return fr_dict_internal->root; /* Remove me when dictionaries are done */
2793 return dict->root;
2794 }
2795
2796 /** Look up a protocol name embedded in another string
2797 *
2798 * @param[out] out the resolve dictionary or NULL if the dictionary
2799 * couldn't be resolved.
2800 * @param[in] name string start.
2801 * @param[in] dict_def The dictionary to return if no dictionary qualifier was found.
2802 * @return
2803 * - 0 and *out != NULL. Couldn't find a dictionary qualifier, so returned dict_def.
2804 * - <= 0 on error and (*out == NULL) (offset as negative integer)
2805 * - > 0 on success (number of bytes parsed).
2806 */
2807 ssize_t fr_dict_by_protocol_substr(fr_dict_t const **out, char const *name, fr_dict_t const *dict_def)
2808 {
2809 fr_dict_attr_t root;
2810
2811 fr_dict_t *dict;
2812 char const *p;
2813 size_t len;
2814
2815 if (!protocol_by_name || !name || !*name || !out) return 0;
2816
2817 memset(&root, 0, sizeof(root));
2818
2819 /*
2820 * Advance p until we get something that's not part of
2821 * the dictionary attribute name.
2822 */
2823 for (p = name; fr_dict_attr_allowed_chars[(uint8_t)*p] && (*p != '.'); p++);
2824
2825 /*
2826 * If what we stopped at wasn't a '.', then there
2827 * can't be a protocol name in this string.
2828 */
2829 if (*p != '.') {
2830 memcpy(out, &dict_def, sizeof(*out));
2831 return 0;
2832 }
2833
2834 len = p - name;
2835 if (len > FR_DICT_ATTR_MAX_NAME_LEN) {
2836 fr_strerror_printf("Attribute name too long");
2837 return -(FR_DICT_ATTR_MAX_NAME_LEN);
2838 }
2839
2840 root.name = talloc_bstrndup(NULL, name, len);
2841 if (!root.name) {
2842 fr_strerror_printf("Out of memory");
2843 *out = NULL;
2844 return 0;
2845 }
2846 dict = fr_hash_table_finddata(protocol_by_name, &(fr_dict_t){ .root = &root });
2847 talloc_const_free(root.name);
2848
2849 if (!dict) {
2850 fr_strerror_printf("Unknown protocol '%.*s'", (int) len, name);
2851 *out = NULL;
2852 return 0;
2853 }
2854 *out = dict;
2855
2856 return p - name;
2857 }
2858
2859 /** Lookup a protocol by its name
2860 *
2861 * @param[in] name of the protocol to locate.
2862 * @return
2863 * - Attribute matching name.
2864 * - NULL if no matching protocolibute could be found.
2865 */
2866 fr_dict_t *fr_dict_by_protocol_name(char const *name)
2867 {
2868 if (!protocol_by_name || !name) return NULL;
2869
2870 return fr_hash_table_finddata(protocol_by_name, &(fr_dict_t){ .root = &(fr_dict_attr_t){ .name = name } });
2871 }
2872
2873 /** Lookup a protocol by its number.
2874 *
2875 * Returns the #fr_dict_t belonging to the protocol with the specified number
2876 * if any have been registered.
2877 *
2878 * @param[in] num to search for.
2879 * @return dictionary representing the protocol (if it exists).
2880 */
2881 fr_dict_t *fr_dict_by_protocol_num(unsigned int num)
2882 {
2883 if (!protocol_by_num) return NULL;
2884
2885 return fr_hash_table_finddata(protocol_by_num, &(fr_dict_t) { .root = &(fr_dict_attr_t){ .attr = num } });
2886 }
2887
2888 /** Dictionary/attribute ctx struct
2889 *
2890 */
2891 typedef struct {
2892 fr_dict_t *found_dict; //!< Dictionary attribute found in.
2893 fr_dict_attr_t const *found_da; //!< Resolved attribute.
2894 fr_dict_attr_t const *find; //!< Attribute to find.
2895 } dict_attr_search_t;
2896
2897 /** Search for an attribute name in all dictionaries
2898 *
2899 * @param[in] ctx Attribute to search for.
2900 * @param[in] data Dictionary to search in.
2901 * @return
2902 * - 0 if attribute not found in dictionary.
2903 * - 1 if attribute found in dictionary.
2904 */
2905 static int _dict_attr_find_in_dicts(void *ctx, void *data)
2906 {
2907 dict_attr_search_t *search = ctx;
2908 fr_dict_t *dict;
2909
2910 if (!data) return 0; /* We get called with NULL data */
2911
2912 dict = talloc_get_type_abort(data, fr_dict_t);
2913
2914 search->found_da = fr_hash_table_finddata(dict->attributes_by_name, search->find);
2915 if (!search->found_da) return 0;
2916
2917 search->found_dict = data;
2918
2919 return 1;
2920 }
2921
2922 /** Attempt to locate the protocol dictionary containing an attribute
2923 *
2924 * @note Unlike fr_dict_by_attr_name, doesn't search through all the dictionaries,
2925 * just uses the fr_dict_attr_t hierarchy and the talloc hierarchy to locate
2926 * the dictionary (much much faster and more scalable).
2927 *
2928 * @param[in] da To get the containing dictionary for.
2929 * @return
2930 * - The dictionary containing da.
2931 * - NULL.
2932 */
2933 fr_dict_t *fr_dict_by_da(fr_dict_attr_t const *da)
2934 {
2935 fr_dict_attr_t const *da_p = da;
2936
2937 while (da_p->parent) {
2938 da_p = da_p->parent;
2939 DA_VERIFY(da_p);
2940 }
2941
2942 if (!da_p->flags.is_root) {
2943 fr_strerror_printf("%s: Attribute %s has not been inserted into a dictionary", __FUNCTION__, da->name);
2944 return NULL;
2945 }
2946
2947 /*
2948 * Parent of the root attribute must
2949 * be the dictionary.
2950 */
2951 return talloc_get_type_abort(talloc_parent(da_p), fr_dict_t);
2952 }
2953
2954 /** Attempt to locate the protocol dictionary containing an attribute
2955 *
2956 * @note This is O(n) and will only return the first instance of the dictionary.
2957 *
2958 * @param[out] found the attribute that was resolved from the name. May be NULL.
2959 * @param[in] name the name of the attribute.
2960 * @return
2961 * - the dictionary the attribute was found in.
2962 * - NULL if an attribute with the specified name wasn't found in any dictionary.
2963 */
2964 fr_dict_t *fr_dict_by_attr_name(fr_dict_attr_t const **found, char const *name)
2965 {
2966 fr_dict_attr_t find = {
2967 .name = name
2968 };
2969 dict_attr_search_t search = {
2970 .find = &find
2971 };
2972 int ret;
2973
2974 if (found) *found = NULL;
2975
2976 if (!name || !*name) return NULL;
2977
2978 ret = fr_hash_table_walk(protocol_by_name, _dict_attr_find_in_dicts, &search);
2979 if (ret == 0) return NULL;
2980
2981 if (found) *found = search.found_da;
2982
2983 return search.found_dict;
2984 }
2985
2986 /** Look up a vendor by one of its child attributes
2987 *
2988 * @param[in] da The vendor attribute.
2989 * @return
2990 * - The vendor.
2991 * - NULL if no vendor with that number was regitered for this protocol.
2992 */
2993 fr_dict_vendor_t const *fr_dict_vendor_by_da(fr_dict_attr_t const *da)
2994 {
2995 fr_dict_t *dict;
2996 fr_dict_vendor_t dv;
2997
2998 dv.pen = fr_dict_vendor_num_by_da(da);
2999 if (!dv.pen) return NULL;
3000
3001 dict = fr_dict_by_da(da);
3002
3003 return fr_hash_table_finddata(dict->vendors_by_num, &dv);
3004 }
3005
3006 /** Look up a vendor by its name
3007 *
3008 * @param[in] dict of protocol context we're operating in.
3009 * If NULL the internal dictionary will be used.
3010 * @param[in] name to search for.
3011 * @return
3012 * - The vendor.
3013 * - NULL if no vendor with that name was regitered for this protocol.
3014 */
3015 fr_dict_vendor_t const *fr_dict_vendor_by_name(fr_dict_t const *dict, char const *name)
3016 {
3017 fr_dict_vendor_t *found;
3018
3019 INTERNAL_IF_NULL(dict, NULL);
3020
3021 if (!name) return 0;
3022
3023 found = fr_hash_table_finddata(dict->vendors_by_name, &(fr_dict_vendor_t) { .name = name });
3024 if (!found) return 0;
3025
3026 return found;
3027 }
3028
3029 /** Look up a vendor by its PEN
3030 *
3031 * @param[in] dict of protocol context we're operating in.
3032 * If NULL the internal dictionary will be used.
3033 * @param[in] vendor_pen to search for.
3034 * @return
3035 * - The vendor.
3036 * - NULL if no vendor with that number was regitered for this protocol.
3037 */
3038 fr_dict_vendor_t const *fr_dict_vendor_by_num(fr_dict_t const *dict, uint32_t vendor_pen)
3039 {
3040 INTERNAL_IF_NULL(dict, NULL);
3041
3042 return fr_hash_table_finddata(dict->vendors_by_num, &(fr_dict_vendor_t) { .pen = vendor_pen });
3043 }
3044
3045 /** Return the vendor that parents this attribute
3046 *
3047 * @note Uses the dictionary hierachy to determine the parent
3048 *
3049 * @param[in] da The dictionary attribute to find parent for.
3050 * @return
3051 * - NULL if the attribute has no vendor.
3052 * - A fr_dict_attr_t representing this attribute's associated vendor.
3053 */
3054 fr_dict_attr_t const *fr_dict_vendor_attr_by_da(fr_dict_attr_t const *da)
3055 {
3056 fr_dict_attr_t const *da_p = da;
3057
3058 DA_VERIFY(da);
3059
3060 while (da_p->parent) {
3061 if (da_p->type == FR_TYPE_VENDOR) break;
3062 da_p = da_p->parent;
3063
3064 if (!da_p) return NULL;
3065 }
3066 if (da_p->type != FR_TYPE_VENDOR) return NULL;
3067
3068 return da_p;
3069 }
3070
3071 /** Return vendor attribute for the specified dictionary and pen
3072 *
3073 * @param[in] vendor_root of the vendor root attribute. Could be 26 (for example) in RADIUS.
3074 * @param[in] vendor_pen to find.
3075 * @return
3076 * - NULL if vendor does not exist.
3077 * - A fr_dict_attr_t representing the vendor in the dictionary hierarchy.
3078 */
3079 fr_dict_attr_t const *fr_dict_vendor_attr_by_num(fr_dict_attr_t const *vendor_root, uint32_t vendor_pen)
3080 {
3081 fr_dict_attr_t const *vendor;
3082
3083 switch (vendor_root->type) {
3084 case FR_TYPE_VSA: /* Vendor specific attribute */
3085 case FR_TYPE_EVS: /* Extended vendor specific attribute */
3086 break;
3087
3088 default:
3089 fr_strerror_printf("Wrong type for vendor root, expected '%s' or '%s' got '%s'",
3090 fr_int2str(fr_value_box_type_table, FR_TYPE_VSA, "<INVALID>"),
3091 fr_int2str(fr_value_box_type_table, FR_TYPE_EVS, "<INVALID>"),
3092 fr_int2str(fr_value_box_type_table, vendor_root->type, "<INVALID>"));
3093 return NULL;
3094 }
3095
3096 vendor = fr_dict_attr_child_by_num(vendor_root, vendor_pen);
3097 if (!vendor) {
3098 fr_strerror_printf("Vendor %i not defined", vendor_pen);
3099 return NULL;
3100 }
3101
3102 if (vendor->type != FR_TYPE_VENDOR) {
3103 fr_strerror_printf("Wrong type for vendor, expected '%s' got '%s'",
3104 fr_int2str(fr_value_box_type_table, vendor->type, "<INVALID>"),
3105 fr_int2str(fr_value_box_type_table, FR_TYPE_VENDOR, "<INVALID>"));
3106 return NULL;
3107 }
3108
3109 return vendor;
3110 }
3111
3112 /** Look up a dictionary attribute by a name embedded in another string
3113 *
3114 * Find the first invalid attribute name char in the string pointed
3115 * to by name.
3116 *
3117 * Copy the characters between the start of the name string and the first
3118 * none #fr_dict_attr_allowed_chars char to a buffer and perform a dictionary lookup
3119 * using that value.
3120 *
3121 * If the attribute exists, advance the pointer pointed to by name
3122 * to the first none #fr_dict_attr_allowed_chars char, and return the DA.
3123 *
3124 * If the attribute does not exist, don't advance the pointer and return
3125 * NULL.
3126 *
3127 * @param[out] err Why parsing failed. May be NULL.
3128 * @see fr_dict_attr_err_t
3129 * @param[out] out Where to store the resolve attribute.
3130 * @param[in] dict of protocol context we're operating in.
3131 * If NULL the internal dictionary will be used.
3132 * @param[in] name string start.
3133 * @return
3134 * - <= 0 on failure.
3135 * - The number of bytes of name consumed on success.
3136 */
3137 ssize_t fr_dict_attr_by_name_substr(fr_dict_attr_err_t *err, fr_dict_attr_t const **out,
3138 fr_dict_t const *dict, char const *name)
3139 {
3140 fr_dict_attr_t const *da;
3141 char const *p;
3142 size_t len;
3143 char buffer[FR_DICT_ATTR_MAX_NAME_LEN + 1];
3144
3145 *out = NULL;
3146
3147 INTERNAL_IF_NULL(dict, 0);
3148
3149 if (!*name) {
3150 fr_strerror_printf("Zero length attribute name");
3151 if (err) *err = FR_DICT_ATTR_PARSE_ERROR;
3152 return 0;
3153 }
3154
3155 if (err) *err = FR_DICT_ATTR_OK;
3156
3157 /*
3158 * Advance p until we get something that's not part of
3159 * the dictionary attribute name.
3160 */
3161 for (p = name; fr_dict_attr_allowed_chars[(uint8_t)*p]; p++);
3162
3163 len = p - name;
3164 if (len > FR_DICT_ATTR_MAX_NAME_LEN) {
3165 fr_strerror_printf("Attribute name too long");
3166 if (err) *err = FR_DICT_ATTR_PARSE_ERROR;
3167 return -(FR_DICT_ATTR_MAX_NAME_LEN);
3168 }
3169
3170 memcpy(buffer, name, len);
3171 buffer[len] = '\0';
3172
3173 da = fr_hash_table_finddata(dict->attributes_by_name, &(fr_dict_attr_t){ .name = buffer });
3174 if (!da) {
3175 if (err) *err = FR_DICT_ATTR_NOTFOUND;
3176 fr_strerror_printf("Unknown attribute '%.*s'", (int) len, name);
3177 return 0;
3178 }
3179
3180 *out = da;
3181
3182 return p - name;
3183 }
3184
3185 /** Locate a #fr_dict_attr_t by its name
3186 *
3187 * @note Unlike attribute numbers, attribute names are unique to the dictionary.
3188 *
3189 * @param[in] dict of protocol context we're operating in.
3190 * If NULL the internal dictionary will be used.
3191 * @param[in] name of the attribute to locate.
3192 * @return
3193 * - Attribute matching name.
3194 * - NULL if no matching attribute could be found.
3195 */
3196 fr_dict_attr_t const *fr_dict_attr_by_name(fr_dict_t const *dict, char const *name)
3197 {
3198 INTERNAL_IF_NULL(dict, NULL);
3199
3200 if (!name) return NULL;
3201
3202 return fr_hash_table_finddata(dict->attributes_by_name, &(fr_dict_attr_t) { .name = name });
3203 }
3204
3205 /** Locate a qualified #fr_dict_attr_t by its name and a dictionary qualifier
3206 *
3207 * @note If calling this function from the server any list or request qualifiers
3208 * should be stripped first.
3209 *
3210 * @param[out] err Why parsing failed. May be NULL.
3211 * @see fr_dict_attr_err_t
3212 * @param[out] out Dictionary found attribute.
3213 * @param[in] dict_def Default dictionary for non-qualified dictionaries.
3214 * @param[in] name Dictionary/Attribute name.
3215 * @param[in] fallback If true, fallback to the internal dictionary.
3216 * @return
3217 * - <= 0 on failure.
3218 * - The number of bytes of name consumed on success.
3219 */
3220 ssize_t fr_dict_attr_by_qualified_name_substr(fr_dict_attr_err_t *err, fr_dict_attr_t const **out,
3221 fr_dict_t const *dict_def, char const *name, bool fallback)
3222 {
3223 fr_dict_t const *dict = NULL;
3224 fr_dict_t const *dict_iter = NULL;
3225 char const *p = name;
3226 ssize_t slen;
3227 fr_dict_attr_err_t aerr = FR_DICT_ATTR_OK;
3228 bool internal = false;
3229 fr_hash_iter_t iter;
3230
3231 *out = NULL;
3232
3233 INTERNAL_IF_NULL(dict_def, -1);
3234
3235 if (err) *err = FR_DICT_ATTR_OK;
3236
3237 /*
3238 * Figure out if we should use the default dictionary
3239 * or if the string was qualified.
3240 */
3241 slen = fr_dict_by_protocol_substr(&dict, p, dict_def);
3242 if (slen < 0) {
3243 if (err) *err = FR_DICT_ATTR_PROTOCOL_NOTFOUND;
3244 return 0;
3245
3246 /*
3247 * Nothing was parsed, use the default dictionary
3248 */
3249 } else if (slen == 0) {
3250 dict = dict_def;
3251
3252 /*
3253 * Has dictionary qualifier, can't fallback
3254 */
3255 } else if (slen > 0) {
3256 p += slen;
3257
3258 /*
3259 * Next thing SHOULD be a '.'
3260 */
3261 if (*p++ != '.') {
3262 if (err) *err = FR_DICT_ATTR_PARSE_ERROR;
3263 return 0;
3264 }
3265
3266 fallback = false;
3267 }
3268
3269 again:
3270 slen = fr_dict_attr_by_name_substr(&aerr, out, dict, p);
3271
3272 switch (aerr) {
3273 case FR_DICT_ATTR_OK:
3274 break;
3275
3276 case FR_DICT_ATTR_NOTFOUND:
3277 /*
3278 * Loop over all the dictionaries
3279 */
3280 if (fallback) {
3281 /*
3282 * Haven't started yet, do so.
3283 */
3284 if (!dict_iter) {
3285 /*
3286 * Check the internal dictionary
3287 * first, unless it's alreaday
3288 * been checked.
3289 */
3290 if (!internal) {
3291 internal = true;
3292 if (dict_def != fr_dict_internal) {
3293 dict = fr_dict_internal;
3294 goto again;
3295 }
3296 }
3297
3298 /*
3299 * Start the iteration over all dictionaries.
3300 */
3301 dict_iter = fr_hash_table_iter_init(protocol_by_num, &iter);
3302 } else {
3303 redo:
3304 dict_iter = fr_hash_table_iter_next(protocol_by_num, &iter);
3305 }
3306
3307 if (!dict_iter) goto fail;
3308 if (dict_iter == dict_def) goto redo;
3309
3310 dict = dict_iter;
3311 goto again;
3312 }
3313
3314 fail:
3315 if (err) *err = aerr;
3316 return -((p - name) + slen);
3317
3318 /*
3319 * Other error codes are the same
3320 */
3321 default:
3322 if (err) *err = aerr;
3323 return -((p - name) + slen);
3324 }
3325
3326 p += slen;
3327
3328 /*
3329 * If we're returning a success code indication,
3330 * ensure we populated out
3331 */
3332 if (!fr_cond_assert(*out)) {
3333 if (err) *err = FR_DICT_ATTR_EINVAL;
3334 return 0;
3335 }
3336
3337 return p - name;
3338 }
3339
3340 /** Locate a qualified #fr_dict_attr_t by its name and a dictionary qualifier
3341 *
3342 * @param[out] out Dictionary found attribute.
3343 * @param[in] dict_def Default dictionary for non-qualified dictionaries.
3344 * @param[in] attr Dictionary/Attribute name.
3345 * @param[in] fallback If true, fallback to the internal dictionary.
3346 * @return an #fr_dict_attr_err_t value.
3347 */
3348 fr_dict_attr_err_t fr_dict_attr_by_qualified_name(fr_dict_attr_t const **out, fr_dict_t const *dict_def,
3349 char const *attr, bool fallback)
3350 {
3351 ssize_t slen;
3352 fr_dict_attr_err_t err = FR_DICT_ATTR_OK;
3353
3354 slen = fr_dict_attr_by_qualified_name_substr(&err, out, dict_def, attr, fallback);
3355 if (slen <= 0) return err;
3356
3357 if ((size_t)slen != strlen(attr)) {
3358 fr_strerror_printf("Trailing garbage after attr string \"%s\"", attr);
3359 return FR_DICT_ATTR_PARSE_ERROR;
3360 }
3361
3362 return FR_DICT_ATTR_OK;
3363 }
3364
3365 /** Lookup a attribute by its its vendor and attribute numbers and data type
3366 *
3367 * @note Only works with FR_TYPE_COMBO_IP
3368 *
3369 * @param[in] da to look for type variant of.
3370 * @param[in] type Variant of attribute to lookup.
3371 * @return
3372 * - Attribute matching parent/attr/type.
3373 * - NULL if no matching attribute could be found.
3374 */
3375 fr_dict_attr_t const *fr_dict_attr_by_type(fr_dict_attr_t const *da, fr_type_t type)
3376 {
3377 return fr_hash_table_finddata(fr_dict_by_da(da)->attributes_combo,
3378 &(fr_dict_attr_t){
3379 .parent = da->parent,
3380 .attr = da->attr,
3381 .type = type
3382 });
3383 }
3384
3385 /** Check if a child attribute exists in a parent using a pointer (da)
3386 *
3387 * @param[in] parent to check for child in.
3388 * @param[in] child to look for.
3389 * @return
3390 * - The child attribute on success.
3391 * - NULL if the child attribute does not exist.
3392 */
3393 fr_dict_attr_t const *fr_dict_attr_child_by_da(fr_dict_attr_t const *parent, fr_dict_attr_t const *child)
3394 {
3395 fr_dict_attr_t const *bin;
3396
3397 DA_VERIFY(parent);
3398
3399 if (!parent->children) return NULL;
3400
3401 /*
3402 * Only some types can have children
3403 */
3404 switch (parent->type) {
3405 default:
3406 return NULL;
3407
3408 case FR_TYPE_STRUCTURAL:
3409 break;
3410 }
3411
3412 /*
3413 * Child arrays may be trimmed back to save memory.
3414 * Check that so we don't SEGV.
3415 */
3416 if ((child->attr & 0xff) > talloc_array_length(parent->children)) return NULL;
3417
3418 bin = parent->children[child->attr & 0xff];
3419 for (;;) {
3420 if (!bin) return NULL;
3421 if (bin == child) return bin;
3422 bin = bin->next;
3423 }
3424
3425 return NULL;
3426 }
3427
3428 /** Check if a child attribute exists in a parent using an attribute number
3429 *
3430 * @param[in] parent to check for child in.
3431 * @param[in] attr number to look for.
3432 * @return
3433 * - The child attribute on success.
3434 * - NULL if the child attribute does not exist.
3435 */
3436 inline fr_dict_attr_t const *fr_dict_attr_child_by_num(fr_dict_attr_t const *parent, unsigned int attr)
3437 {
3438 fr_dict_attr_t const *bin;
3439
3440 DA_VERIFY(parent);
3441
3442 if (!parent->children) return NULL;
3443
3444 /*
3445 * Only some types can have children
3446 */
3447 switch (parent->type) {
3448 default:
3449 return NULL;
3450
3451 case FR_TYPE_STRUCTURAL:
3452 break;
3453 }
3454
3455 /*
3456 * Child arrays may be trimmed back to save memory.
3457 * Check that so we don't SEGV.
3458 */
3459 if ((attr & 0xff) > talloc_array_length(parent->children)) return NULL;
3460
3461 bin = parent->children[attr & 0xff];
3462 for (;;) {
3463 if (!bin) return NULL;
3464 if (bin->attr == attr) return bin;
3465 bin = bin->next;
3466 }
3467
3468 return NULL;
3469 }
3470
3471 /** Lookup the structure representing an enum value in a #fr_dict_attr_t
3472 *
3473 * @param[in] da to search in.
3474 * @param[in] value to search for.
3475 * @return
3476 * - Matching #fr_dict_enum_t.
3477 * - NULL if no matching #fr_dict_enum_t could be found.
3478 */
3479 fr_dict_enum_t *fr_dict_enum_by_value(fr_dict_attr_t const *da, fr_value_box_t const *value)
3480 {
3481 fr_dict_enum_t enumv, *dv;
3482 fr_dict_t *dict;
3483
3484 if (!da) return NULL;
3485
3486 dict = fr_dict_by_da(da);
3487 if (!dict) {
3488 fr_strerror_printf("Attributes \"%s\" not present in any dictionaries", da->name);
3489 return NULL;
3490 }
3491
3492 /*
3493 * Could be NULL or an unknown attribute, in which case
3494 * we want to avoid the lookup gracefully...
3495 */
3496 if (value->type != da->type) return NULL;
3497
3498 /*
3499 * First, look up aliases.
3500 */
3501 enumv.da = da;
3502 enumv.alias = "";
3503 enumv.alias_len = 0;
3504
3505 /*
3506 * Look up the attribute alias target, and use
3507 * the correct attribute number if found.
3508 */
3509 dv = fr_hash_table_finddata(dict->values_by_alias, &enumv);
3510 if (dv) enumv.da = dv->da;
3511
3512 enumv.value = value;
3513
3514 return fr_hash_table_finddata(dict->values_by_da, &enumv);
3515 }
3516
3517 /** Lookup the name of an enum value in a #fr_dict_attr_t
3518 *
3519 * @param[in] da to search in.
3520 * @param[in] value number to search for.
3521 * @return
3522 * - Name of value.
3523 * - NULL if no matching value could be found.
3524 */
3525 char const *fr_dict_enum_alias_by_value(fr_dict_attr_t const *da, fr_value_box_t const *value)
3526 {
3527 fr_dict_enum_t *dv;
3528 fr_dict_t *dict;
3529
3530 if (!da) return NULL;
3531
3532 dict = fr_dict_by_da(da);
3533 if (!dict) {
3534 fr_strerror_printf("Attributes \"%s\" not present in any dictionaries", da->name);
3535 return NULL;
3536 }
3537
3538 dv = fr_dict_enum_by_value(da, value);
3539 if (!dv) return "";
3540
3541 return dv->alias;
3542 }
3543
3544 /*
3545 * Get a value by its name, keyed off of an attribute.
3546 */
3547 fr_dict_enum_t *fr_dict_enum_by_alias(fr_dict_attr_t const *da, char const *alias, ssize_t len)
3548 {
3549 fr_dict_enum_t *found;
3550 fr_dict_enum_t find = {
3551 .da = da,
3552 .alias = alias
3553 };
3554 fr_dict_t *dict;
3555
3556 if (!alias) return NULL;
3557
3558 dict = fr_dict_by_da(da);
3559 if (!dict) {
3560 fr_strerror_printf("Attributes \"%s\" not present in any dictionaries", da->name);
3561 return NULL;
3562 }
3563
3564 if (len < 0) len = strlen(alias);
3565 find.alias_len = (size_t)len;
3566
3567 /*
3568 * Look up the attribute alias target, and use
3569 * the correct attribute number if found.
3570 */
3571 found = fr_hash_table_finddata(dict->values_by_alias, &find);
3572 if (found) find.da = found->da;
3573
3574 return fr_hash_table_finddata(dict->values_by_alias, &find);
3575 }
3576
3577 /*
3578 * String split routine. Splits an input string IN PLACE
3579 * into pieces, based on spaces.
3580 */
3581 int fr_dict_str_to_argv(char *str, char **argv, int max_argc)
3582 {
3583 int argc = 0;
3584
3585 while (*str) {
3586 if (argc >= max_argc) break;
3587
3588 /*
3589 * Chop out comments early.
3590 */
3591 if (*str == '#') {
3592 *str = '\0';
3593 break;
3594 }
3595
3596 while ((*str == ' ') ||
3597 (*str == '\t') ||
3598 (*str == '\r') ||
3599 (*str == '\n'))
3600 *(str++) = '\0';
3601
3602 if (!*str) break;
3603
3604 argv[argc] = str;
3605 argc++;
3606
3607 while (*str &&
3608 (*str != ' ') &&
3609 (*str != '\t') &&
3610 (*str != '\r') &&
3611 (*str != '\n'))
3612 str++;
3613 }
3614
3615 return argc;
3616 }
3617
3618 static int dict_read_sscanf_i(unsigned int *pvalue, char const *str)
3619 {
3620 int rcode = 0;
3621 int base = 10;
3622 static char const *tab = "0123456789";
3623
3624 if ((str[0] == '0') &&
3625 ((str[1] == 'x') || (str[1] == 'X'))) {
3626 tab = "0123456789abcdef";
3627 base = 16;
3628
3629 str += 2;
3630 }
3631
3632 while (*str) {
3633 char const *c;
3634
3635 if (*str == '.') break;
3636
3637 c = memchr(tab, tolower((int)*str), base);
3638 if (!c) return 0;
3639
3640 rcode *= base;
3641 rcode += (c - tab);
3642 str++;
3643 }
3644
3645 *pvalue = rcode;
3646 return 1;
3647 }
3648
3649 /** Parser context for dict_from_file
3650 *
3651 * Allows vendor and TLV context to persist across $INCLUDEs
3652 */
3653 typedef struct {
3654 fr_dict_t *dict; //!< Protocol dictionary we're inserting attributes into.
3655 fr_dict_t *old_dict; //!< The dictionary before the current BEGIN-PROTOCOL block.
3656
3657 fr_dict_vendor_t const *block_vendor; //!< Vendor block we're inserting attributes into.
3658 //!< Can be removed once we remove the vendor field from
3659 //!< #fr_dict_attr_t.
3660
3661 fr_dict_attr_t const *block_tlv[FR_DICT_TLV_NEST_MAX]; //!< Nested TLV block's we're
3662 //!< inserting attributes into.
3663 int block_tlv_depth; //!< Nested TLV block index we're inserting into.
3664
3665 fr_dict_attr_t const *parent; //!< Current parent attribute (root/vendor/tlv).
3666
3667 fr_dict_attr_t const *last_attr; //!< Cache of last attribute to speed up
3668 ///< value processing.
3669
3670 TALLOC_CTX *fixup_pool; //!< Temporary pool for fixups, reduces holes
3671 dict_enum_fixup_t *enum_fixup;
3672 } dict_from_file_ctx_t;
3673
3674 /** Set a new root dictionary attribute
3675 *
3676 * @note Must only be called once per dictionary.
3677 *
3678 * @param[in] dict to modify.
3679 * @param[in] name of dictionary root.
3680 * @param[in] proto_number The artificial (or IANA allocated) number for the protocol.
3681 * This is only used for
3682 * @return
3683 * - 0 on success.
3684 * - -1 on failure.
3685 */
3686 static int dict_root_set(fr_dict_t *dict, char const *name, unsigned int proto_number)
3687 {
3688 fr_dict_attr_flags_t flags = {
3689 .is_root = 1,
3690 .type_size = 1,
3691 .length = 1
3692 };
3693
3694 if (!fr_cond_assert(!dict->root)) {
3695 fr_strerror_printf("Dictionary root already set");
3696 return -1;
3697 }
3698
3699 dict->root = dict_attr_alloc_name(dict, name);
3700 if (!dict->root) return -1;
3701
3702 dict_attr_init(dict->root, NULL, proto_number, FR_TYPE_TLV, &flags);
3703 DA_VERIFY(dict->root);
3704
3705 return 0;
3706 }
3707
3708 static int _dict_free(fr_dict_t *dict)
3709 {
3710 if (dict == fr_dict_internal) fr_dict_internal = NULL;
3711
3712 if (!fr_cond_assert(!dict->in_protocol_by_name || fr_hash_table_delete(protocol_by_name, dict))) {
3713 fr_strerror_printf("Failed removing dictionary from protocol hash \"%s\"", dict->root->name);
3714 return -1;
3715 }
3716 if (!fr_cond_assert(!dict->in_protocol_by_num || fr_hash_table_delete(protocol_by_num, dict))) {
3717 fr_strerror_printf("Failed removing dictionary from protocol number_hash \"%s\"", dict->root->name);
3718 return -1;
3719 }
3720
3721 return 0;
3722 }
3723
3724 /** Allocate a new dictionary
3725 *
3726 * @param[in] ctx to allocate dictionary in.
3727 * @return
3728 * - NULL on memory allocation error.
3729 */
3730 static fr_dict_t *dict_alloc(TALLOC_CTX *ctx)
3731 {
3732 fr_dict_t *dict;
3733
3734 dict = talloc_zero(ctx, fr_dict_t);
3735 if (!dict) {
3736 error:
3737 fr_strerror_printf("Failed allocating memory for dictionary");
3738 talloc_free(dict);
3739 return NULL;
3740 }
3741
3742 talloc_set_destructor(dict, _dict_free);
3743
3744 /*
3745 * Pre-Allocate 6MB of pool memory for rapid startup
3746 * As that's the working memory required during
3747 * dictionary initialisation.
3748 */
3749 dict->pool = talloc_pool(dict, DICT_POOL_SIZE);
3750 if (!dict->pool) goto error;
3751
3752 /*
3753 * Create the table of vendor by name. There MAY NOT
3754 * be multiple vendors of the same name.
3755 */
3756 dict->vendors_by_name = fr_hash_table_create(dict, dict_vendor_name_hash, dict_vendor_name_cmp, hash_pool_free);
3757 if (!dict->vendors_by_name) goto error;
3758
3759 /*
3760 * Create the table of vendors by value. There MAY
3761 * be vendors of the same value. If there are, we
3762 * pick the latest one.
3763 */
3764 dict->vendors_by_num = fr_hash_table_create(dict, dict_vendor_pen_hash, dict_vendor_pen_cmp, NULL);
3765 if (!dict->vendors_by_num) goto error;
3766
3767 /*
3768 * Create the table of attributes by name. There MAY NOT
3769 * be multiple attributes of the same name.
3770 */
3771 dict->attributes_by_name = fr_hash_table_create(dict, dict_attr_name_hash, dict_attr_name_cmp, NULL);
3772 if (!dict->attributes_by_name) goto error;
3773
3774 /*
3775 * Horrible hacks for combo-IP.
3776 */
3777 dict->attributes_combo = fr_hash_table_create(dict, dict_attr_combo_hash, dict_attr_combo_cmp, hash_pool_free);
3778 if (!dict->attributes_combo) goto error;
3779
3780 dict->values_by_alias = fr_hash_table_create(dict, dict_enum_alias_hash, dict_enum_alias_cmp, hash_pool_free);
3781 if (!dict->values_by_alias) goto error;
3782
3783 dict->values_by_da = fr_hash_table_create(dict, dict_enum_value_hash, dict_enum_value_cmp, hash_pool_free);
3784 if (!dict->values_by_da) goto error;
3785
3786 return dict;
3787 }
3788
3789 /** Lookup a dictionary reference
3790 *
3791 * Format is @verbatim[<proto>].[<attr>]@endverbatim
3792 *
3793 * If protocol is omitted lookup is in the current dictionary.
3794 *
3795 * FIXME: Probably needs the dictionary equivalent of pass2, to fixup circular dependencies
3796 * DHCPv4->RADIUS and RADIUS->DHCPv4 are both valid.
3797 *
3798 * @param[in] dict The current dictionary we're parsing.
3799 * @param[in,out] ref The reference string. Pointer advanced to the end of the string.
3800 * @return
3801 * - NULL if the reference is invalid.
3802 * - A local or foreign attribute representing the target of the reference.
3803 */
3804 static fr_dict_attr_t const *dict_resolve_reference(fr_dict_t *dict, char const *ref)
3805 {
3806 char const *p = ref, *q, *end = p + strlen(ref);
3807 fr_dict_t *proto_dict;
3808 fr_dict_attr_t const *da;
3809 ssize_t slen;
3810
3811 /*
3812 * If the reference does not begin with .
3813 * then it's a reference into a foreign
3814 * protocol.
3815 */
3816 if (*p != '.') {
3817 char buffer[FR_DICT_PROTO_MAX_NAME_LEN + 1];
3818
3819 q = strchr(p, '.');
3820 if (!q) q = end;
3821
3822 if ((size_t)(q - p) > sizeof(buffer)) {
3823 fr_strerror_printf("Protocol name too long");
3824 return NULL;
3825 }
3826
3827 strlcpy(buffer, p, (q - p + 1));
3828
3829 dict = fr_dict_by_protocol_name(buffer);
3830 if (!dict) {
3831 fr_strerror_printf("Referenced protocol \"%s\" not found", buffer);
3832 return NULL;
3833 }
3834
3835 return NULL;
3836 /*
3837 * If the reference string begins with .
3838 * then the reference is in the current
3839 * dictionary.
3840 */
3841 } else {
3842 proto_dict = dict;
3843 }
3844
3845 /*
3846 * If there's a '.' after the dictionary, then
3847 * the reference is to a specific attribute.
3848 */
3849 if (*p == '.') {
3850 p++;
3851
3852 slen = fr_dict_attr_by_name_substr(NULL, &da, proto_dict, p);
3853 if (slen <= 0) {
3854 fr_strerror_printf("Referenced attribute \"%s\" not found", p);
3855 return NULL;
3856 }
3857 }
3858
3859 da = fr_dict_root(proto_dict);
3860 if (!da) {
3861 fr_strerror_printf("Dictionary missing attribute root");
3862 return NULL;
3863 }
3864
3865 return da;
3866 }
3867
3868 /*
3869 * Process the ATTRIBUTE command
3870 */
3871 static int dict_read_process_attribute(dict_from_file_ctx_t *ctx, char **argv, int argc,
3872 fr_dict_attr_flags_t *base_flags, fr_dict_attr_t const **previous)
3873 {
3874 bool oid = false;
3875 bool set_previous = true;
3876
3877 fr_dict_vendor_t const *vendor;
3878 unsigned int attr;
3879
3880 int type;
3881 unsigned int length;
3882 fr_dict_attr_flags_t flags;
3883 fr_dict_attr_t const *ref = NULL;
3884 fr_dict_attr_t const *parent = ctx->parent;
3885 char *p;
3886
3887 if ((argc < 3) || (argc > 4)) {
3888 fr_strerror_printf("Invalid ATTRIBUTE syntax");
3889 return -1;
3890 }
3891
3892 /*
3893 * Dictionaries need to have real names, not shitty ones.
3894 */
3895 if (strncmp(argv[0], "Attr-", 5) == 0) {
3896 fr_strerror_printf("Invalid ATTRIBUTE name");
3897 return -1;
3898 }
3899
3900 memcpy(&flags, base_flags, sizeof(flags));
3901
3902 /*
3903 * Look for OIDs before doing anything else.
3904 */
3905 if (!strchr(argv[1], '.')) {
3906 /*
3907 * Parse out the attribute number
3908 */
3909 if (!dict_read_sscanf_i(&attr, argv[1])) {
3910 fr_strerror_printf("Invalid ATTRIBUTE number");
3911 return -1;
3912 }
3913
3914 /*
3915 * Got a '.', which means "continue from the
3916 * previously defined attribute, which then must exist.
3917 */
3918 } else if (argv[1][0] == '.') {
3919 if (!previous || !*previous) {
3920 fr_strerror_printf("Unknown parent for partial OID");
3921 return -1;
3922 }
3923
3924 parent = *previous;
3925 set_previous = false;
3926 goto get_by_oid;
3927
3928
3929 /*
3930 * Got an OID string. Every attribute should exist other
3931 * than the leaf, which is the attribute we're defining.
3932 */
3933 } else {
3934 ssize_t slen;
3935
3936 get_by_oid:
3937 oid = true;
3938
3939 slen = fr_dict_attr_by_oid(ctx->dict, &parent, &attr, argv[1]);
3940 if (slen <= 0) return -1;
3941
3942 if (!fr_cond_assert(parent)) return -1; /* Should have provided us with a parent */
3943 }
3944
3945 /*
3946 * Some types can have fixed length
3947 */
3948 p = strchr(argv[2], '[');
3949 if (p) *p = '\0';
3950
3951 /*
3952 * find the type of the attribute.
3953 */
3954 type = fr_str2int(fr_value_box_type_table, argv[2], -1);
3955 if (type < 0) {
3956 fr_strerror_printf("Unknown data type '%s'", argv[2]);
3957 return -1;
3958 }
3959
3960 if (p) {
3961 char *q;
3962
3963 if (type != FR_TYPE_OCTETS) {
3964 fr_strerror_printf("Only 'octets' types can have a 'length' parameter");
3965 return -1;
3966 }
3967
3968 q = strchr(p + 1, ']');
3969 if (!q) {
3970 fr_strerror_printf("Invalid format for '%s[...]'", argv[2]);
3971 return -1;
3972 }
3973
3974 *q = '\0';
3975
3976 if (!dict_read_sscanf_i(&length, p + 1)) {
3977 fr_strerror_printf("Invalid length for '%s[...]'", argv[2]);
3978 return -1;
3979 }
3980
3981 if ((length == 0) || (length > 253)) {
3982 fr_strerror_printf("Invalid length for '%s[...]'", argv[2]);
3983 return -1;
3984 }
3985
3986 flags.length = length;
3987 }
3988
3989 /*
3990 * Parse options.
3991 */
3992 if (argc >= 4) {
3993 char *q, *v;
3994
3995 p = argv[3];
3996 do {
3997 char key[64], value[256];
3998
3999 q = strchr(p, ',');
4000 if (!q) q = p + strlen(p);
4001
4002 /*
4003 * Nothing after the trailing comma
4004 */
4005 if (p == q) break;
4006
4007 if ((size_t)(q - p) > sizeof(key)) {
4008 fr_strerror_printf("ATTRIBUTE option key too long");
4009 return -1;
4010 }
4011
4012 /*
4013 * Copy key and value
4014 */
4015 if (!(v = memchr(p, '=', q - p)) || (v == q)) {
4016 value[0] = '\0';
4017 strlcpy(key, p, (q - p) + 1);
4018 } else {
4019 strlcpy(key, p, (v - p) + 1);
4020 strlcpy(value, v + 1, q - v);
4021 }
4022
4023 /*
4024 * Boolean flag, means this is a tagged
4025 * attribute.
4026 */
4027 if (strcmp(key, "has_tag") == 0) {
4028 flags.has_tag = 1;
4029
4030 /*
4031 * Encryption method.
4032 */
4033 } else if (strcmp(key, "encrypt") == 0) {
4034 char *qq;
4035
4036 flags.encrypt = strtol(value, &qq, 0);
4037 if (*qq) {
4038 fr_strerror_printf("Invalid encrypt value \"%s\"", value);
4039 return -1;
4040 }
4041
4042 /*
4043 * Marks the attribute up as internal.
4044 * This means it can use numbers outside of the allowed
4045 * protocol range, and also means it will not be included
4046 * in replies or proxy requests.
4047 */
4048 } else if (strcmp(key, "internal") == 0) {
4049 flags.internal = 1;
4050
4051 } else if (strcmp(key, "array") == 0) {
4052 flags.array = 1;
4053
4054 } else if (strcmp(key, "concat") == 0) {
4055 flags.concat = 1;
4056
4057 } else if (strcmp(key, "virtual") == 0) {
4058 flags.virtual = 1;
4059
4060 } else if (strcmp(key, "reference") == 0) {
4061 ref = dict_resolve_reference(ctx->dict, value);
4062 if (!ref) return -1;
4063 flags.is_reference = 1;
4064
4065 /*
4066 * The only thing is the vendor name, and it's a known name:
4067 * allow it.
4068 *
4069 * This format is terrible, and is only
4070 * allowed for backwards compatability.
4071 */
4072 } else if ((argv[3] == p) && (*q == '\0')) {
4073 if (oid) {
4074 fr_strerror_printf("ATTRIBUTE cannot use a 'vendor' flag");
4075 return -1;
4076 }
4077
4078 if (ctx->block_vendor) {
4079 fr_strerror_printf("Vendor flag inside of 'BEGIN-VENDOR' is not allowed");
4080 return -1;
4081 }
4082
4083 vendor = fr_dict_vendor_by_name(ctx->dict, key);
4084 if (!vendor) goto unknown;
4085 break;
4086
4087 } else {
4088 unknown:
4089 fr_strerror_printf("Unknown option '%s'", key);
4090 return -1;
4091 }
4092 p = q;
4093 } while (*p++);
4094 }
4095
4096 #ifdef WITH_DICTIONARY_WARNINGS
4097 /*
4098 * Hack to help us discover which vendors have illegal
4099 * attributes.
4100 */
4101 if (!vendor && (attr < 256) &&
4102 !strstr(fn, "rfc") && !strstr(fn, "illegal")) {
4103 fprintf(stderr, "WARNING: Illegal Attribute %s in %s\n",
4104 argv[0], fn);
4105 }
4106 #endif
4107
4108 /*
4109 * Add in a normal attribute
4110 */
4111 if (!ref) {
4112 if (fr_dict_attr_add(ctx->dict, parent, argv[0], attr, type, &flags) < 0) return -1;
4113 /*
4114 * Add in a special reference attribute
4115 */
4116 } else {
4117 if (dict_attr_ref_add(ctx->dict, parent, argv[0], attr, type, &flags, ref) < 0) return -1;
4118 }
4119
4120 /*
4121 * If we need to set the previous attribute, we have to
4122 * look it up by number.
4123 */
4124 if (set_previous && previous) *previous = fr_dict_attr_child_by_num(parent, attr);
4125
4126 return 0;
4127 }
4128
4129 /*
4130 * Process the ATTRIBUTE command, where it only has a name.
4131 */
4132 static int dict_read_process_named_attribute(dict_from_file_ctx_t *ctx,
4133 char **argv, int argc,
4134 fr_dict_attr_flags_t const *base_flags)
4135 {
4136 int type;
4137 unsigned int attr;
4138 uint32_t hash;
4139 char *p, normalized[512];
4140
4141 if (argc != 2) {
4142 fr_strerror_printf("Invalid ATTRIBUTE syntax");
4143 return -1;
4144 }
4145
4146 /*
4147 * find the type of the attribute.
4148 */
4149 type = fr_str2int(fr_value_box_type_table, argv[1], -1);
4150 if (type < 0) {
4151 fr_strerror_printf("Unknown data type '%s'", argv[1]);
4152 return -1;
4153 }
4154
4155 strlcpy(normalized, argv[0], sizeof(normalized));
4156 for (p = normalized; *p != '\0'; p++) {
4157 if (isupper((int) *p)) {
4158 *p = tolower((int) *p);
4159 }
4160 }
4161
4162 hash = fr_hash_string(normalized);
4163 attr = hash;
4164
4165 /*
4166 * Add it in.
4167 */
4168 if (fr_dict_attr_add(ctx->dict, ctx->parent, argv[0], attr, type, base_flags) < 0) return -1;
4169
4170 return 0;
4171 }
4172
4173 /** Process a value alias
4174 *
4175 */
4176 static int dict_read_process_value(dict_from_file_ctx_t *ctx, char **argv, int argc)
4177 {
4178 fr_dict_attr_t const *da;
4179 fr_value_box_t value;
4180
4181 if (argc != 3) {
4182 fr_strerror_printf("Invalid VALUE syntax");
4183 return -1;
4184 }
4185
4186 /*
4187 * Most VALUEs are bunched together by ATTRIBUTE. We can
4188 * save a lot of lookups on dictionary initialization by
4189 * caching the last attribute.
4190 */
4191 if (ctx->last_attr && (strcasecmp(argv[0], ctx->last_attr->name) == 0)) {
4192 da = ctx->last_attr;
4193 } else {
4194 da = fr_dict_attr_by_name(ctx->dict, argv[0]);
4195 ctx->last_attr = da;
4196 }
4197
4198 /*
4199 * Remember which attribute is associated with this
4200 * value. This allows us to define enum
4201 * values before the attribute exists, and fix them
4202 * up later.
4203 */
4204 if (!da) {
4205 dict_enum_fixup_t *fixup;
4206
4207 if (!fr_cond_assert_msg(ctx->fixup_pool, "fixup pool context invalid")) return -1;
4208
4209 fixup = talloc_zero(ctx->fixup_pool, dict_enum_fixup_t);
4210 if (!fixup) {
4211 oom:
4212 talloc_free(fixup);
4213 fr_strerror_printf("Out of memory");
4214 return -1;
4215 }
4216 fixup->attribute = talloc_strdup(fixup, argv[0]);
4217 if (!fixup->attribute) goto oom;
4218 fixup->alias = talloc_strdup(fixup, argv[1]);
4219 if (!fixup->alias) goto oom;
4220 fixup->value = talloc_strdup(fixup, argv[2]);
4221 if (!fixup->value) goto oom;
4222
4223 /*
4224 * Insert to the head of the list.
4225 */
4226 fixup->next = ctx->enum_fixup;
4227 ctx->enum_fixup = fixup;
4228
4229 return 0;
4230 }
4231
4232 /*
4233 * Only a few data types can have VALUEs defined.
4234 */
4235 switch (da->type) {
4236 case FR_TYPE_ABINARY:
4237 case FR_TYPE_GROUP:
4238 case FR_TYPE_STRUCTURAL:
4239 case FR_TYPE_INVALID:
4240 case FR_TYPE_MAX:
4241 fr_strerror_printf_push("Cannot define VALUE for ATTRIBUTE \"%s\" of data type \"%s\"", da->name,
4242 fr_int2str(fr_value_box_type_table, da->type, "<INVALID>"));
4243 return -1;
4244
4245 default:
4246 break;
4247 }
4248
4249 {
4250 fr_type_t type = da->type; /* Might change - Stupid combo IP */
4251
4252 if (fr_value_box_from_str(NULL, &value, &type, NULL, argv[2], -1, '\0', false) < 0) {
4253 fr_strerror_printf_push("Invalid VALUE for ATTRIBUTE \"%s\"", da->name);
4254 return -1;
4255 }
4256 }
4257
4258 if (fr_dict_enum_add_alias(da, argv[1], &value, false, true) < 0) {
4259 fr_value_box_clear(&value);
4260 return -1;
4261 }
4262 fr_value_box_clear(&value);
4263
4264 return 0;
4265 }
4266
4267 /*
4268 * Process the FLAGS command
4269 */
4270 static int dict_read_process_flags(UNUSED fr_dict_t *dict, char **argv, int argc,
4271 fr_dict_attr_flags_t *base_flags)
4272 {
4273 bool sense = true;
4274
4275 if (argc == 1) {
4276 char *p;
4277
4278 p = argv[0];
4279 if (*p == '!') {
4280 sense = false;
4281 p++;
4282 }
4283
4284 if (strcmp(p, "internal") == 0) {
4285 base_flags->internal = sense;
4286 return 0;
4287 }
4288 }
4289
4290 fr_strerror_printf("Invalid FLAGS syntax");
4291 return -1;
4292 }
4293
4294 static int dict_read_parse_format(char const *format, unsigned int *pvalue, int *ptype, int *plength,
4295 bool *pcontinuation)
4296 {
4297 char const *p;
4298 int type, length;
4299 bool continuation = false;
4300
4301 if (strncasecmp(format, "format=", 7) != 0) {
4302 fr_strerror_printf("Invalid format for VENDOR. Expected 'format=', got '%s'",
4303 format);
4304 return -1;
4305 }
4306
4307 p = format + 7;
4308 if ((strlen(p) < 3) ||
4309 !isdigit((int)p[0]) ||
4310 (p[1] != ',') ||
4311 !isdigit((int)p[2]) ||
4312 (p[3] && (p[3] != ','))) {
4313 fr_strerror_printf("Invalid format for VENDOR. Expected text like '1,1', got '%s'",
4314 p);
4315 return -1;
4316 }
4317
4318 type = (int)(p[0] - '0');
4319 length = (int)(p[2] - '0');
4320
4321 if ((type != 1) && (type != 2) && (type != 4)) {
4322 fr_strerror_printf("Invalid type value %d for VENDOR", type);
4323 return -1;
4324 }
4325
4326 if ((length != 0) && (length != 1) && (length != 2)) {
4327 fr_strerror_printf("Ivalid length value %d for VENDOR", length);
4328 return -1;
4329 }
4330
4331 if (p[3] == ',') {
4332 if (!p[4]) {
4333 fr_strerror_printf("Invalid format for VENDOR. Expected text like '1,1', got '%s'",
4334 p);
4335 return -1;
4336 }
4337
4338 if ((p[4] != 'c') ||
4339 (p[5] != '\0')) {
4340 fr_strerror_printf("Invalid format for VENDOR. Expected text like '1,1', got '%s'",
4341 p);
4342 return -1;
4343 }
4344 continuation = true;
4345
4346 if ((*pvalue != VENDORPEC_WIMAX) ||
4347 (type != 1) || (length != 1)) {
4348 fr_strerror_printf("Only WiMAX VSAs can have continuations");
4349 return -1;
4350 }
4351 }
4352
4353 *ptype = type;
4354 *plength = length;
4355 *pcontinuation = continuation;
4356 return 0;
4357 }
4358
4359 /** Register the specified dictionary as a protocol dictionary
4360 *
4361 * Allows vendor and TLV context to persist across $INCLUDEs
4362 */
4363 static int dict_read_process_protocol(char **argv, int argc)
4364 {
4365 unsigned int value;
4366 unsigned int type_size = 1;
4367 fr_dict_t *dict;
4368
4369 if ((argc < 2) || (argc > 3)) {
4370 fr_strerror_printf("Missing arguments after PROTOCOL. Expected PROTOCOL <num> <name>");
4371 return -1;
4372 }
4373
4374 /*
4375 * Validate all entries
4376 */
4377 if (!dict_read_sscanf_i(&value, argv[1])) {
4378 fr_strerror_printf("Invalid number '%s' following PROTOCOL", argv[1]);
4379 return -1;
4380 }
4381
4382 if (value == 0) {
4383 fr_strerror_printf("Invalid value '%u' following PROTOCOL", value);
4384 return -1;
4385 }
4386
4387 /*
4388 * Look for a format statement. This may specify the
4389 * type length of the protocol's types.
4390 */
4391 if (argc == 3) {
4392 char const *p;
4393 char *q;
4394
4395 if (strncasecmp(argv[2], "format=", 7) != 0) {
4396 fr_strerror_printf("Invalid format for PROTOCOL. Expected 'format=', got '%s'", argv[2]);
4397 return -1;
4398 }
4399 p = argv[2] + 7;
4400
4401 type_size = strtoul(p, &q, 10);
4402 if (q != (p + strlen(p))) {
4403 fr_strerror_printf("Found trailing garbage '%s' after format specifier", p);
4404 return -1;
4405 }
4406 }
4407
4408 /*
4409 * Cross check name / number.
4410 */
4411 dict = fr_dict_by_protocol_name(argv[0]);
4412 if (dict) {
4413 if (dict->root->attr != value) {
4414 fr_strerror_printf("Conflicting numbers %u vs %u for PROTOCOL \"%s\"",
4415 dict->root->attr, value, dict->root->name);
4416 return -1;
4417 }
4418
4419 } else {
4420 dict = fr_dict_by_protocol_num(value);
4421
4422 if (dict && (strcasecmp(dict->root->name, argv[0]) != 0)) {
4423 fr_strerror_printf("Conflicting names \"%s\" vs \"%s\" for PROTOCOL %u",
4424 dict->root->name, argv[0], dict->root->attr);
4425 return -1;
4426 }
4427 }
4428
4429 /*
4430 * And check types no matter what.
4431 */
4432 if (dict) {
4433 if (dict->root->flags.type_size != type_size) {
4434 fr_strerror_printf("Conflicting flags for PROTOCOL \"%s\"", dict->root->name);
4435 return -1;
4436 }
4437 return 0;
4438 }
4439
4440 dict = dict_alloc(NULL);
4441
4442 /*
4443 * Set the root attribute with the protocol name
4444 */
4445 dict_root_set(dict, argv[0], value);
4446
4447 if (dict_protocol_add(dict) < 0) return -1;
4448
4449 return 0;
4450 }
4451
4452 /*
4453 * Process the VENDOR command
4454 */
4455 static int dict_read_process_vendor(fr_dict_t *dict, char **argv, int argc)
4456 {
4457 unsigned int value;
4458 int type, length;
4459 bool continuation = false;
4460 fr_dict_vendor_t const *dv;
4461 fr_dict_vendor_t *mutable;
4462
4463 if ((argc < 2) || (argc > 3)) {
4464 fr_strerror_printf("Invalid VENDOR syntax");
4465 return -1;
4466 }
4467
4468 /*
4469 * Validate all entries
4470 */
4471 if (!dict_read_sscanf_i(&value, argv[1])) {
4472 fr_strerror_printf("Invalid number in VENDOR");
4473 return -1;
4474 }
4475
4476 /*
4477 * Look for a format statement. Allow it to over-ride the hard-coded formats below.
4478 */
4479 if (argc == 3) {
4480 if (dict_read_parse_format(argv[2], &value, &type, &length, &continuation) < 0) return -1;
4481
4482 } else {
4483 type = length = 1;
4484 }
4485
4486 /* Create a new VENDOR entry for the list */
4487 if (dict_vendor_add(dict, argv[0], value) < 0) return -1;
4488
4489 dv = fr_dict_vendor_by_num(dict, value);
4490 if (!dv) {
4491 fr_strerror_printf("Failed adding format for VENDOR");
4492 return -1;
4493 }
4494
4495 memcpy(&mutable, &dv, sizeof(mutable));
4496
4497 mutable->type = type;
4498 mutable->length = length;
4499 mutable->flags = continuation;
4500
4501 return 0;
4502 }
4503
4504 static int fr_dict_finalise(dict_from_file_ctx_t *ctx)
4505 {
4506 /*
4507 * Resolve any VALUE aliases (enums) that were defined
4508 * before the attributes they reference.
4509 */
4510 if (ctx->enum_fixup) {
4511 fr_dict_attr_t const *da;
4512 dict_enum_fixup_t *this, *next;
4513
4514 for (this = ctx->enum_fixup; this != NULL; this = next) {
4515 fr_value_box_t value;
4516 fr_type_t type;
4517 int ret;
4518
4519 next = this->next;
4520 da = fr_dict_attr_by_name(ctx->dict, this->attribute);
4521 if (!da) {
4522 fr_strerror_printf("No ATTRIBUTE '%s' defined for VALUE '%s'",
4523 this->attribute, this->alias);
4524 error:
4525 return -1;
4526 }
4527 type = da->type;
4528
4529 if (fr_value_box_from_str(this, &value, &type, NULL,
4530 this->value, talloc_array_length(this->value) - 1, '\0', false) < 0) {
4531 fr_strerror_printf_push("Invalid VALUE for ATTRIBUTE \"%s\"", da->name);
4532 goto error;
4533 }
4534
4535 ret = fr_dict_enum_add_alias(da, this->alias, &value, false, false);
4536 fr_value_box_clear(&value);
4537
4538 if (ret < 0) goto error;
4539
4540 /*
4541 * Just so we don't lose track of things.
4542 */
4543 ctx->enum_fixup = next;
4544 }
4545 }
4546 TALLOC_FREE(ctx->fixup_pool);
4547
4548 /*
4549 * Walk over all of the hash tables to ensure they're
4550 * initialized. We do this because the threads may perform
4551 * lookups, and we don't want multi-threaded re-ordering
4552 * of the table entries. That would be bad.
4553 */
4554 fr_hash_table_walk(ctx->dict->vendors_by_name, hash_null_callback, NULL);
4555 fr_hash_table_walk(ctx->dict->vendors_by_num, hash_null_callback, NULL);
4556
4557 fr_hash_table_walk(ctx->dict->values_by_da, hash_null_callback, NULL);
4558 fr_hash_table_walk(ctx->dict->values_by_alias, hash_null_callback, NULL);
4559
4560 ctx->last_attr = NULL;
4561
4562 return 0;
4563 }
4564
4565 /** Parse a dictionary file
4566 *
4567 * @param[in] ctx Contains the current state of the dictionary parser.
4568 * Used to track what PROTOCOL, VENDOR or TLV block
4569 * we're in. Block context changes in $INCLUDEs should
4570 * not affect the context of the including file.
4571 * @param[in] dir_name Directory containing the dictionary we're loading.
4572 * @param[in] filename we're parsing.
4573 * @param[in] src_file The including file.
4574 * @param[in] src_line Line on which the $INCLUDE or $INCLUDE- statement was found.
4575 * @return
4576 * - 0 on success.
4577 * - -1 on failure.
4578 */
4579 static int _dict_from_file(dict_from_file_ctx_t *ctx,
4580 char const *dir_name, char const *filename,
4581 char const *src_file, int src_line)
4582 {
4583 FILE *fp;
4584 char dir[256], fn[256];
4585 char buf[256];
4586 char *p;
4587 int line = 0;
4588
4589 struct stat statbuf;
4590 char *argv[MAX_ARGV];
4591 int argc;
4592 fr_dict_attr_t const *da, *previous = NULL;
4593
4594 /*
4595 * Base flags are only set for the current file
4596 */
4597 fr_dict_attr_flags_t base_flags;
4598
4599 if (!fr_cond_assert(!ctx->dict->root || ctx->parent)) return -1;
4600
4601 if ((strlen(dir_name) + 3 + strlen(filename)) > sizeof(dir)) {
4602 fr_strerror_printf_push("%s: Filename name too long", "Error reading dictionary");
4603 return -1;
4604 }
4605
4606 /*
4607 * If it's an absolute dir, forget the parent dir,
4608 * and remember the new one.
4609 *
4610 * If it's a relative dir, tack on the current filename
4611 * to the parent dir. And use that.
4612 */
4613 if (!FR_DIR_IS_RELATIVE(filename)) {
4614 strlcpy(dir, filename, sizeof(dir));
4615 p = strrchr(dir, FR_DIR_SEP);
4616 if (p) {
4617 p[1] = '\0';
4618 } else {
4619 strlcat(dir, "/", sizeof(dir));
4620 }
4621
4622 strlcpy(fn, filename, sizeof(fn));
4623 } else {
4624 strlcpy(dir, dir_name, sizeof(dir));
4625 p = strrchr(dir, FR_DIR_SEP);
4626 if (p) {
4627 if (p[1]) strlcat(dir, "/", sizeof(dir));
4628 } else {
4629 strlcat(dir, "/", sizeof(dir));
4630 }
4631 strlcat(dir, filename, sizeof(dir));
4632 p = strrchr(dir, FR_DIR_SEP);
4633 if (p) {
4634 p[1] = '\0';
4635 } else {
4636 strlcat(dir, "/", sizeof(dir));
4637 }
4638
4639 p = strrchr(filename, FR_DIR_SEP);
4640 if (p) {
4641 snprintf(fn, sizeof(fn), "%s%s", dir, p);
4642 } else {
4643 snprintf(fn, sizeof(fn), "%s%s", dir, filename);
4644 }
4645 }
4646
4647 if ((fp = fopen(fn, "r")) == NULL) {
4648 if (!src_file) {
4649 fr_strerror_printf_push("Couldn't open dictionary %s: %s", fr_syserror(errno), fn);
4650 } else {
4651 fr_strerror_printf_push("Error reading dictionary: %s[%d]: Couldn't open dictionary '%s': %s",
4652 src_file, src_line, fn,
4653 fr_syserror(errno));
4654 }
4655 return -2;
4656 }
4657
4658 /*
4659 * If fopen works, this works.
4660 */
4661 if (stat(fn, &statbuf) < 0) {
4662 fclose(fp);
4663 return -1;
4664 }
4665
4666 if (!S_ISREG(statbuf.st_mode)) {
4667 fclose(fp);
4668 fr_strerror_printf_push("Dictionary is not a regular file: %s", fn);
4669 return -1;
4670 }
4671
4672 /*
4673 * Globally writable dictionaries means that users can control
4674 * the server configuration with little difficulty.
4675 */
4676 #ifdef S_IWOTH
4677 if ((statbuf.st_mode & S_IWOTH) != 0) {
4678 fclose(fp);
4679 fr_strerror_printf_push("Dictionary is globally writable: %s. "
4680 "Refusing to start due to insecure configuration", fn);
4681 return -1;
4682 }
4683 #endif
4684
4685 /*
4686 * Seed the random pool with data.
4687 */
4688 fr_rand_seed(&statbuf, sizeof(statbuf));
4689
4690 memset(&base_flags, 0, sizeof(base_flags));
4691
4692 while (fgets(buf, sizeof(buf), fp) != NULL) {
4693 line++;
4694
4695 switch (buf[0]) {
4696 case '#':
4697 case '\0':
4698 case '\n':
4699 case '\r':
4700 continue;
4701 }
4702
4703 /*
4704 * Comment characters should NOT be appearing anywhere but
4705 * as start of a comment;
4706 */
4707 p = strchr(buf, '#');
4708 if (p) *p = '\0';
4709
4710 argc = fr_dict_str_to_argv(buf, argv, MAX_ARGV);
4711 if (argc == 0) continue;
4712
4713 if (argc == 1) {
4714 fr_strerror_printf("Invalid entry");
4715
4716 error:
4717 fr_strerror_printf_push("Error reading %s[%d]", fn, line);
4718 fclose(fp);
4719 return -1;
4720 }
4721
4722 /*
4723 * Process VALUE lines.
4724 */
4725 if (strcasecmp(argv[0], "VALUE") == 0) {
4726 if (dict_read_process_value(ctx, argv + 1, argc - 1) == -1) goto error;
4727 continue;
4728 }
4729
4730 /*
4731 * Perhaps this is an attribute.
4732 */
4733 if (strcasecmp(argv[0], "ATTRIBUTE") == 0) {
4734 if (!base_flags.named) {
4735 if (dict_read_process_attribute(ctx,
4736 argv + 1, argc - 1,
4737 &base_flags, &previous) == -1) goto error;
4738 } else {
4739 if (dict_read_process_named_attribute(ctx,
4740 argv + 1, argc - 1,
4741 &base_flags) == -1) goto error;
4742 }
4743 continue;
4744 }
4745
4746 /*
4747 * Process VALUE lines.
4748 */
4749 if (strcasecmp(argv[0], "FLAGS") == 0) {
4750 if (dict_read_process_flags(ctx->dict, argv + 1, argc - 1, &base_flags) == -1) goto error;
4751 continue;
4752 }
4753
4754 /*
4755 * See if we need to import another dictionary.
4756 */
4757 if (strncasecmp(argv[0], "$INCLUDE", 8) == 0) {
4758 int rcode;
4759 dict_from_file_ctx_t nctx = *ctx;
4760
4761 /*
4762 * Allow "$INCLUDE" or "$INCLUDE-", but
4763 * not anything else.
4764 */
4765 if ((argv[0][8] != '\0') && ((argv[0][8] != '-') || (argv[0][9] != '\0'))) goto invalid_keyword;
4766
4767 /*
4768 * Included files operate on a copy of the context.
4769 *
4770 * This copy means that they inherit the
4771 * current context, including parents,
4772 * TLVs, etc. But if the included file
4773 * leaves a "dangling" TLV or "last
4774 * attribute", then it won't affect the
4775 * parent.
4776 */
4777
4778 rcode = _dict_from_file(&nctx, dir, argv[1], fn, line);
4779 if ((rcode == -2) && (argv[0][8] == '-')) {
4780 fr_strerror_printf(NULL); /* delete all errors */
4781 rcode = 0;
4782 }
4783
4784 if (rcode < 0) {
4785 fr_strerror_printf_push("from $INCLUDE at %s[%d]", fn, line);
4786 fclose(fp);
4787 return -1;
4788 }
4789
4790 /*
4791 * Fixups are added to the head of the
4792 * list, so copy the new head over to the
4793 * parent.
4794 */
4795 ctx->enum_fixup = nctx.enum_fixup;
4796 continue;
4797 } /* $INCLUDE */
4798
4799 /*
4800 * Process VENDOR lines.
4801 */
4802 if (strcasecmp(argv[0], "VENDOR") == 0) {
4803 if (dict_read_process_vendor(ctx->dict, argv + 1, argc - 1) == -1) goto error;
4804 continue;
4805 }
4806
4807 /*
4808 * Process PROTOCOL line. Defines a new protocol.
4809 */
4810 if (strcasecmp(argv[0], "PROTOCOL") == 0) {
4811 if (argc < 2) {
4812 fr_strerror_printf_push("Invalid PROTOCOL entry");
4813 goto error;
4814 }
4815 if (dict_read_process_protocol(argv + 1, argc - 1) == -1) goto error;
4816 continue;
4817 }
4818
4819 /*
4820 * Switches the current protocol context
4821 */
4822 if (strcasecmp(argv[0], "BEGIN-PROTOCOL") == 0) {
4823 fr_dict_t *found;
4824
4825 ctx->old_dict = ctx->dict;
4826
4827 if (argc != 2) {
4828 fr_strerror_printf_push("Invalid BEGIN-PROTOCOL entry");
4829 goto error;
4830 }
4831
4832 /*
4833 * If we're not parsing in the context of the internal
4834 * dictionary, then we don't allow BEGIN-PROTOCOL
4835 * statements.
4836 */
4837 if (ctx->dict != fr_dict_internal) {
4838 fr_strerror_printf_push("Nested BEGIN-PROTOCOL statements are not allowed");
4839 goto error;
4840 }
4841
4842 found = fr_dict_by_protocol_name(argv[1]);
4843 if (!found) {
4844 fr_strerror_printf("Unknown protocol '%s'", argv[1]);
4845 goto error;
4846 }
4847
4848 /*
4849 * Add a temporary fixup pool
4850 *
4851 * @todo - make a nested ctx?
4852 */
4853 if (!ctx->fixup_pool) ctx->fixup_pool = talloc_pool(NULL, DICT_FIXUP_POOL_SIZE);
4854
4855 ctx->dict = found;
4856 ctx->parent = ctx->dict->root;
4857 continue;
4858 }
4859
4860 /*
4861 * Switches back to the previous protocol context
4862 */
4863 if (strcasecmp(argv[0], "END-PROTOCOL") == 0) {
4864 fr_dict_t const *found;
4865
4866 if (argc != 2) {
4867 fr_strerror_printf("Invalid END-PROTOCOL entry");
4868 goto error;
4869 }
4870
4871 found = fr_dict_by_protocol_name(argv[1]);
4872 if (!found) {
4873 fr_strerror_printf("END-PROTOCOL %s does not refer to a valid protocol", argv[1]);
4874 goto error;
4875 }
4876
4877 if (found != ctx->dict) {
4878 fr_strerror_printf("END-PROTOCOL %s does not match previous BEGIN-PROTOCOL %s",
4879 argv[1], found->root->name);
4880 goto error;
4881 }
4882
4883 /*
4884 * Applies fixups to any attributes added to
4885 * the protocol dictionary.
4886 */
4887 if (fr_dict_finalise(ctx) < 0) goto error;
4888
4889 /*
4890 * Switch back to old values.
4891 *
4892 * @todo - just create a stack of contests, so we don't need "old_foo"
4893 */
4894 ctx->dict = ctx->old_dict;
4895 ctx->parent = ctx->dict->root;
4896 continue;
4897 }
4898
4899 /*
4900 * Switches TLV parent context
4901 */
4902 if (strcasecmp(argv[0], "BEGIN-TLV") == 0) {
4903 fr_dict_attr_t const *common;
4904
4905 if ((ctx->block_tlv_depth + 1) > FR_DICT_TLV_NEST_MAX) {
4906 fr_strerror_printf_push("TLVs are nested too deep");
4907 goto error;
4908 }
4909
4910 if (argc != 2) {
4911 fr_strerror_printf_push("Invalid BEGIN-TLV entry");
4912 goto error;
4913 }
4914
4915 da = fr_dict_attr_by_name(ctx->dict, argv[1]);
4916 if (!da) {
4917 fr_strerror_printf_push("Unknown attribute '%s'", argv[1]);
4918 goto error;
4919 }
4920
4921 if (da->type != FR_TYPE_TLV) {
4922 fr_strerror_printf_push("Attribute '%s' should be a 'tlv', but is a '%s'",
4923 argv[1],
4924 fr_int2str(fr_value_box_type_table, da->type, "?Unknown?"));
4925 goto error;
4926 }
4927
4928 common = fr_dict_parent_common(ctx->parent, da, true);
4929 if (!common ||
4930 (common->type == FR_TYPE_VSA) ||
4931 (common->type == FR_TYPE_EVS)) {
4932 fr_strerror_printf_push("Attribute '%s' should be a child of '%s'",
4933 argv[1], ctx->parent->name);
4934 goto error;
4935 }
4936
4937 ctx->block_tlv[ctx->block_tlv_depth++] = ctx->parent;
4938 ctx->parent = da;
4939
4940 continue;
4941 } /* BEGIN-TLV */
4942
4943 /*
4944 * Switches back to previous TLV parent
4945 */
4946 if (strcasecmp(argv[0], "END-TLV") == 0) {
4947 if (--ctx->block_tlv_depth < 0) {
4948 fr_strerror_printf_push("Too many END-TLV entries. Mismatch at END-TLV %s", argv[1]);
4949 goto error;
4950 }
4951
4952 if (argc != 2) {
4953 fr_strerror_printf_push("Invalid END-TLV entry");
4954 goto error;
4955 }
4956
4957 da = fr_dict_attr_by_name(ctx->dict, argv[1]);
4958 if (!da) {
4959 fr_strerror_printf_push("Unknown attribute '%s'", argv[1]);
4960 goto error;
4961 }
4962
4963 if (da != ctx->parent) {
4964 fr_strerror_printf_push("END-TLV %s does not match previous BEGIN-TLV %s", argv[1],
4965 ctx->parent->name);
4966 goto error;
4967 }
4968 ctx->parent = ctx->block_tlv[ctx->block_tlv_depth];
4969 continue;
4970 } /* END-VENDOR */
4971
4972 if (strcasecmp(argv[0], "BEGIN-VENDOR") == 0) {
4973 fr_dict_vendor_t const *vendor;
4974 fr_dict_attr_flags_t flags;
4975
4976 fr_dict_attr_t const *vsa_da;
4977 fr_dict_attr_t const *vendor_da;
4978 fr_dict_attr_t *new;
4979 fr_dict_attr_t *mutable;
4980
4981 if (argc < 2) {
4982 fr_strerror_printf_push("Invalid BEGIN-VENDOR entry");
4983 goto error;
4984 }
4985
4986 vendor = fr_dict_vendor_by_name(ctx->dict, argv[1]);
4987 if (!vendor) {
4988 fr_strerror_printf_push("Unknown vendor '%s'", argv[1]);
4989 goto error;
4990 }
4991
4992 /*
4993 * Check for extended attr VSAs
4994 *
4995 * BEGIN-VENDOR foo format=Foo-Encapsulation-Attr
4996 */
4997 if (argc > 2) {
4998 if (strncmp(argv[2], "format=", 7) != 0) {
4999 fr_strerror_printf_push("Invalid format %s", argv[2]);
5000 goto error;
5001 }
5002
5003 p = argv[2] + 7;
5004 da = fr_dict_attr_by_name(ctx->dict, p);
5005 if (!da) {
5006 fr_strerror_printf_push("Invalid format for BEGIN-VENDOR: Unknown "
5007 "attribute '%s'", p);
5008 goto error;
5009 }
5010
5011 if (da->type != FR_TYPE_EVS) {
5012 fr_strerror_printf_push("Invalid format for BEGIN-VENDOR. "
5013 "Attribute '%s' should be 'evs' but is '%s'", p,
5014 fr_int2str(fr_value_box_type_table, da->type, "?Unknown?"));
5015 goto error;
5016 }
5017
5018 vsa_da = da;
5019 } else {
5020 /*
5021 * Automagically create Attribute 26
5022 *
5023 * This should exist, but in case we're starting without
5024 * the RFC dictionaries we need to add it in the case
5025 * it doesn't.
5026 */
5027 vsa_da = fr_dict_attr_child_by_num(ctx->parent, FR_VENDOR_SPECIFIC);
5028 if (!vsa_da) {
5029 memset(&flags, 0, sizeof(flags));
5030
5031 if (fr_dict_attr_add(ctx->dict, ctx->parent, "Vendor-Specific",
5032 FR_VENDOR_SPECIFIC, FR_TYPE_VSA, &flags) < 0) {
5033 fr_strerror_printf_push("Failed adding Vendor-Specific for Vendor %s",
5034 vendor->name);
5035 goto error;
5036 }
5037
5038 vsa_da = fr_dict_attr_child_by_num(ctx->parent, FR_VENDOR_SPECIFIC);
5039 if (!vsa_da) {
5040 fr_strerror_printf_push("Failed finding Vendor-Specific for Vendor %s",
5041 vendor->name);
5042 goto error;
5043 }
5044 }
5045 }
5046
5047 /*
5048 * Create a VENDOR attribute on the fly, either in the context
5049 * of the EVS attribute, or the VSA (26) attribute.
5050 */
5051 vendor_da = fr_dict_attr_child_by_num(vsa_da, vendor->pen);
5052 if (!vendor_da) {
5053 memset(&flags, 0, sizeof(flags));
5054
5055 if (vsa_da->type == FR_TYPE_VSA) {
5056 fr_dict_vendor_t const *dv;
5057
5058 dv = fr_dict_vendor_by_num(ctx->dict, vendor->pen);
5059 if (dv) {
5060 flags.type_size = dv->type;
5061 flags.length = dv->length;
5062
5063 } else { /* unknown vendor, shouldn't happen */
5064 flags.type_size = 1;
5065 flags.length = 1;
5066 }
5067
5068 } else { /* EVS are always "format=1,1" */
5069 flags.type_size = 1;
5070 flags.length = 1;
5071 }
5072
5073 memcpy(&mutable, &vsa_da, sizeof(mutable));
5074 new = dict_attr_alloc(mutable, ctx->parent, argv[1],
5075 vendor->pen, FR_TYPE_VENDOR, &flags);
5076 dict_attr_child_add(mutable, new);
5077
5078 vendor_da = new;
5079 }
5080 ctx->parent = vendor_da;
5081 ctx->block_vendor = vendor;
5082 continue;
5083 } /* BEGIN-VENDOR */
5084
5085 if (strcasecmp(argv[0], "END-VENDOR") == 0) {
5086 fr_dict_vendor_t const *vendor;
5087
5088 if (argc != 2) {
5089 fr_strerror_printf_push("Invalid END-VENDOR entry");
5090 goto error;
5091 }
5092
5093 vendor = fr_dict_vendor_by_name(ctx->dict, argv[1]);
5094 if (!vendor) {
5095 fr_strerror_printf_push("Unknown vendor '%s'", argv[1]);
5096 goto error;
5097 }
5098
5099 if (vendor != ctx->block_vendor) {
5100 fr_strerror_printf_push("END-VENDOR '%s' does not match any previous BEGIN-VENDOR",
5101 argv[1]);
5102 goto error;
5103 }
5104 ctx->parent = ctx->dict->root;
5105 ctx->block_vendor = NULL;
5106 continue;
5107 } /* END-VENDOR */
5108
5109 /*
5110 * Any other string: We don't recognize it.
5111 */
5112 invalid_keyword:
5113 fr_strerror_printf_push("Invalid keyword '%s'", argv[0]);
5114 goto error;
5115 }
5116 fclose(fp);
5117 return 0;
5118 }
5119
5120 static int dict_from_file(fr_dict_t *dict,
5121 char const *dir_name, char const *filename,
5122 char const *src_file, int src_line)
5123 {
5124 int rcode;
5125 dict_from_file_ctx_t ctx;
5126
5127 memset(&ctx, 0, sizeof(ctx));
5128 ctx.dict = dict;
5129 ctx.parent = dict->root;
5130
5131 rcode = _dict_from_file(&ctx,
5132 dir_name, filename, src_file, src_line);
5133 if (rcode < 0) {
5134 // free up the various fixups
5135 return rcode;
5136 }
5137
5138 /*
5139 * Applies to any attributes added to the *internal*
5140 * dictionary.
5141 *
5142 * Fixups should have been applied already to any protocol
5143 * dictionaries.
5144 */
5145 return fr_dict_finalise(&ctx);
5146 }
5147
5148 /** (Re-)Initialize the special internal dictionary
5149 *
5150 * This dictionary has additional programatically generated attributes added to it,
5151 * and is checked in addition to the protocol specific dictionaries.
5152 *
5153 * @note The dictionary pointer returned in out must have its reference counter
5154 * decremented with #fr_dict_free when no longer used.
5155 *
5156 * @param[out] out Where to write pointer to the internal dictionary.
5157 * @param[in] dict_subdir name of the internal dictionary dir (may be NULL).
5158 * @return
5159 * - 0 on success.
5160 * - -1 on failure.
5161 */
5162 int fr_dict_internal_afrom_file(fr_dict_t **out, char const *dict_subdir)
5163 {
5164 fr_dict_t *dict;
5165 char *dict_path = NULL;
5166 char *tmp;
5167 FR_NAME_NUMBER const *p;
5168 fr_dict_attr_flags_t flags = { .internal = true };
5169 char *type_name;
5170
5171 if (unlikely(!protocol_by_name || !protocol_by_num)) {
5172 fr_strerror_printf("fr_dict_global_init() must be called before loading dictionary files");
5173 return -1;
5174 }
5175
5176 /*
5177 * Increase the reference count of the internal dictionary.
5178 */
5179 if (fr_dict_internal) {
5180 talloc_increase_ref_count(fr_dict_internal);
5181 *out = fr_dict_internal;
5182 return 0;
5183 }
5184
5185 memcpy(&tmp, &default_dict_dir, sizeof(tmp));
5186 dict_path = dict_subdir ? talloc_asprintf(NULL, "%s%c%s", default_dict_dir, FR_DIR_SEP, dict_subdir) : tmp;
5187
5188 dict = dict_alloc(dict_ctx);
5189 if (!dict) {
5190 error:
5191 if (!fr_dict_internal) talloc_free(dict);
5192 talloc_free(dict_path);
5193 return -1;
5194 }
5195
5196 /*
5197 * Set the root name of the dictionary
5198 */
5199 dict_root_set(dict, "internal", 0);
5200
5201 /*
5202 * Add cast attributes. We do it this way,
5203 * so cast attributes get added automatically for new types.
5204 *
5205 * We manually add the attributes to the dictionary, and bypass
5206 * fr_dict_attr_add(), because we know what we're doing, and
5207 * that function does too many checks.
5208 */
5209 for (p = fr_value_box_type_table; p->name; p++) {
5210 fr_dict_attr_t *n;
5211
5212 type_name = talloc_typed_asprintf(NULL, "Tmp-Cast-%s", p->name);
5213
5214 n = dict_attr_alloc(dict->pool, dict->root, type_name,
5215 FR_CAST_BASE + p->number, p->number, &flags);
5216 if (!n) {
5217 talloc_free(type_name);
5218 goto error;
5219 }
5220
5221 if (!fr_hash_table_insert(dict->attributes_by_name, n)) {
5222 fr_strerror_printf("Failed inserting \"%s\" into internal dictionary", type_name);
5223 talloc_free(type_name);
5224 goto error;
5225 }
5226
5227 talloc_free(type_name);
5228
5229 /*
5230 * Set up parenting for the attribute.
5231 */
5232 if (dict_attr_child_add(dict->root, n) < 0) goto error;
5233 }
5234
5235 if (dict_path && dict_from_file(dict, dict_path, FR_DICTIONARY_FILE, NULL, 0) < 0) goto error;
5236
5237 talloc_free(dict_path);
5238
5239 *out = dict;
5240 if (!fr_dict_internal) fr_dict_internal = dict;
5241
5242 return 0;
5243 }
5244
5245 /** (Re)-initialize a protocol dictionary
5246 *
5247 * Initialize the directory, then fix the attr number of all attributes.
5248 *
5249 * @param[out] out Where to write a pointer to the new dictionary. Will free existing
5250 * dictionary if files have changed and *out is not NULL.
5251 * @param[in] proto_name that we're loading the dictionary for.
5252 * @param[in] proto_dir Explicitly set where to hunt for the dictionary files. May be NULL.
5253 * @return
5254 * - 0 on success.
5255 * - -1 on failure.
5256 */
5257 int fr_dict_protocol_afrom_file(fr_dict_t **out, char const *proto_name, char const *proto_dir)
5258 {
5259 char *dict_dir = NULL;
5260 fr_dict_t *dict;
5261
5262 if (unlikely(!protocol_by_name || !protocol_by_num)) {
5263 fr_strerror_printf("fr_dict_global_init() must be called before loading dictionary files");
5264 return -1;
5265 }
5266
5267 if (unlikely(!fr_dict_internal)) {
5268 fr_strerror_printf("Internal dictionary must be initialised before loading protocol dictionaries");
5269 return -1;
5270 }
5271
5272 /*
5273 * Increment the reference count if the dictionary
5274 * has already been loaded and return that.
5275 */
5276 dict = fr_dict_by_protocol_name(proto_name);
5277 if (dict && dict->autoloaded) {
5278 talloc_increase_ref_count(dict);
5279 *out = dict;
5280 return 0;
5281 }
5282
5283 if (!proto_dir) {
5284 dict_dir = talloc_asprintf(NULL, "%s%c%s", default_dict_dir, FR_DIR_SEP, proto_name);
5285 } else {
5286 dict_dir = talloc_asprintf(NULL, "%s%c%s", default_dict_dir, FR_DIR_SEP, proto_dir);
5287 }
5288
5289 /*
5290 * Start in the context of the internal dictionary,
5291 * and switch to the context of a protocol dictionary
5292 * when we hit a BEGIN-PROTOCOL line.
5293 *
5294 * This allows a single file to provide definitions
5295 * for multiple protocols, which'll probably be useful
5296 * at some point.
5297 */
5298 if (dict_from_file(fr_dict_internal, dict_dir, FR_DICTIONARY_FILE, NULL, 0) < 0) {
5299 error:
5300 talloc_free(dict_dir);
5301 return -1;
5302 }
5303
5304 /*
5305 * Check the dictionary actually defined the protocol
5306 */
5307 dict = fr_dict_by_protocol_name(proto_name);
5308 if (!dict) {
5309 fr_strerror_printf("Dictionary \"%s\" missing \"BEGIN-PROTOCOL %s\" declaration", dict_dir, proto_name);
5310 goto error;
5311 }
5312
5313 talloc_free(dict_dir);
5314
5315 /*
5316 * If we're autoloading a previously defined dictionary,
5317 * then mark up the dictionary as now autoloaded.
5318 */
5319 if (!dict->autoloaded) {
5320 // talloc_increase_ref_count(dict);
5321 dict->autoloaded = true;
5322 }
5323
5324 *out = dict;
5325
5326 return 0;
5327 }
5328
5329 /** Read supplementary attribute definitions into an existing dictionary
5330 *
5331 * @param[in] dict Existing dictionary.
5332 * @param[in] dir dictionary is located in.
5333 * @param[in] filename of the dictionary.
5334 * @return
5335 * - 0 on success.
5336 * - -1 on failure.
5337 */
5338 int fr_dict_read(fr_dict_t *dict, char const *dir, char const *filename)
5339 {
5340 INTERNAL_IF_NULL(dict, -1);
5341
5342 if (!dict->attributes_by_name) {
5343 fr_strerror_printf("%s: Must call fr_dict_internal_afrom_file() before fr_dict_read()", __FUNCTION__);
5344 return -1;
5345 }
5346
5347 return dict_from_file(dict, dir, filename, NULL, 0);
5348 }
5349
5350
5351 /** Decrement the reference count on a previously loaded dictionary
5352 *
5353 * @param[in] dict to free.
5354 */
5355 void fr_dict_free(fr_dict_t **dict)
5356 {
5357 if (!*dict) return;
5358
5359 talloc_decrease_ref_count(*dict);
5360 *dict = NULL;
5361 }
5362
5363 /** Process a dict_attr_autoload element to load/verify a dictionary attribute
5364 *
5365 * @param[in] to_load attribute definition
5366 * @return
5367 * - 0 on success.
5368 * - -1 on failure.
5369 */
5370 int fr_dict_attr_autoload(fr_dict_attr_autoload_t const *to_load)
5371 {
5372 fr_dict_attr_t const *da;
5373 fr_dict_attr_autoload_t const *p = to_load;
5374
5375 for (p = to_load; p->out; p++) {
5376 if (!p->dict) {
5377 fr_strerror_printf("Invalid autoload entry, missing dictionary pointer");
5378 return -1;
5379 }
5380
5381 if (!*p->dict) {
5382 fr_strerror_printf("Can't resolve attribute \"%s\", dictionary not loaded", p->name);
5383 fr_strerror_printf_push("Check fr_dict_autoload_t struct has "
5384 "an entry to load the dictionary \"%s\" is located in, and that "
5385 "the symbol name is correct", p->name);
5386 return -1;
5387 }
5388
5389 da = fr_dict_attr_by_name(*p->dict, p->name);
5390 if (!da) {
5391 fr_strerror_printf("Attribute \"%s\" not found in \"%s\" dictionary", p->name,
5392 *p->dict ? (*p->dict)->root->name : "internal");
5393 return -1;
5394 }
5395
5396 if (da->type != p->type) {
5397 fr_strerror_printf("Attribute \"%s\" should be type %s, but defined as type %s", da->name,
5398 fr_int2str(fr_value_box_type_table, p->type, "?Unknown?"),
5399 fr_int2str(fr_value_box_type_table, da->type, "?Unknown?"));
5400 return -1;
5401 }
5402
5403 if (p->out) *(p->out) = da;
5404 }
5405
5406 return 0;
5407 }
5408
5409 /** Process a dict_autoload element to load a protocol
5410 *
5411 * @param[in] to_load dictionary definition.
5412 * @return
5413 * - 0 on success.
5414 * - -1 on failure.
5415 */
5416 int fr_dict_autoload(fr_dict_autoload_t const *to_load)
5417 {
5418 fr_dict_autoload_t const *p;
5419
5420 for (p = to_load; p->out; p++) {
5421 fr_dict_t *dict = NULL;
5422
5423 if (unlikely(!p->proto)) {
5424 fr_strerror_printf("autoload missing parameter proto");
5425 return -1;
5426 }
5427
5428 /*
5429 * Load the internal dictionary
5430 */
5431 if (strcmp(p->proto, "freeradius") == 0) {
5432 if (fr_dict_internal_afrom_file(&dict, p->proto) < 0) return -1;
5433 } else {
5434 if (fr_dict_protocol_afrom_file(&dict, p->proto, p->base_dir) < 0) return -1;
5435 }
5436
5437 *(p->out) = dict;
5438 }
5439
5440 return 0;
5441 }
5442
5443 /** Decrement the reference count on a previously loaded dictionary
5444 *
5445 * @param[in] to_free previously loaded dictionary to free.
5446 */
5447 void fr_dict_autofree(fr_dict_autoload_t const *to_free)
5448 {
5449 fr_dict_t **dict;
5450 fr_dict_autoload_t const *p;
5451
5452 for (p = to_free; p->out; p++) {
5453 dict = p->out;
5454 if (!*dict) continue;
5455
5456 fr_dict_free(dict);
5457 }
5458 }
5459
5460 static void _fr_dict_dump(fr_dict_attr_t const *da, unsigned int lvl)
5461 {
5462 unsigned int i;
5463 size_t len;
5464 fr_dict_attr_t const *p;
5465 char flags[256];
5466
5467 fr_dict_snprint_flags(flags, sizeof(flags), &da->flags);
5468
5469 printf("[%02i] 0x%016" PRIxPTR "%*s %s(%u) %s %s\n", lvl, (unsigned long)da, lvl * 2, " ",
5470 da->name, da->attr, fr_int2str(fr_value_box_type_table, da->type, "<INVALID>"), flags);
5471
5472 len = talloc_array_length(da->children);
5473 for (i = 0; i < len; i++) {
5474 for (p = da->children[i]; p; p = p->next) {
5475 _fr_dict_dump(p, lvl + 1);
5476 }
5477 }
5478 }
5479
5480 void fr_dict_dump(fr_dict_t *dict)
5481 {
5482 _fr_dict_dump(dict->root, 0);
5483 }
5484
5485 /*
5486 * External API for testing
5487 */
5488 int fr_dict_parse_str(fr_dict_t *dict, char *buf, fr_dict_attr_t const *parent, unsigned int vendor_pen)
5489 {
5490 int argc;
5491 char *argv[MAX_ARGV];
5492 int ret;
5493 fr_dict_attr_flags_t base_flags;
5494 dict_from_file_ctx_t ctx;
5495
5496 INTERNAL_IF_NULL(dict, -1);
5497
5498 argc = fr_dict_str_to_argv(buf, argv, MAX_ARGV);
5499 if (argc == 0) return 0;
5500
5501
5502 memset(&ctx, 0, sizeof(ctx));
5503 ctx.dict = dict;
5504
5505 ctx.fixup_pool = talloc_pool(NULL, DICT_FIXUP_POOL_SIZE);
5506 if (!ctx.fixup_pool) return -1;
5507
5508 if (strcasecmp(argv[0], "VALUE") == 0) {
5509 if (argc < 4) {
5510 fr_strerror_printf("VALUE needs at least 4 arguments, got %i", argc);
5511 error:
5512 TALLOC_FREE(ctx.fixup_pool);
5513 return -1;
5514 }
5515
5516 if (!fr_dict_attr_by_name(dict, argv[1])) {
5517 fr_strerror_printf("Attribute \"%s\" does not exist in dictionary \"%s\"",
5518 argv[1], dict->root->name);
5519 goto error;
5520 }
5521 ret = dict_read_process_value(&ctx, argv + 1, argc - 1);
5522 if (ret < 0) goto error;
5523
5524 } else if (strcasecmp(argv[0], "ATTRIBUTE") == 0) {
5525 if (!parent) parent = fr_dict_root(dict);
5526
5527 memset(&base_flags, 0, sizeof(base_flags));
5528
5529 if (vendor_pen) ctx.block_vendor = fr_dict_vendor_by_num(dict, vendor_pen);
5530
5531 ret = dict_read_process_attribute(&ctx,
5532 argv + 1, argc - 1, &base_flags, NULL);
5533 if (ret < 0) goto error;
5534 } else if (strcasecmp(argv[0], "VENDOR") == 0) {
5535 ret = dict_read_process_vendor(dict, argv + 1, argc - 1);
5536 if (ret < 0) goto error;
5537 } else {
5538 fr_strerror_printf("Invalid input '%s'", argv[0]);
5539 goto error;
5540 }
5541
5542 fr_dict_finalise(&ctx);
5543
5544 return 0;
5545 }
5546
5547 /** Initialise the global protocol hashes
5548 *
5549 * @note Must be called before any other dictionary functions.
5550 *
5551 * @param[in] ctx to allocate protocol hashes in.
5552 * @param[in] dict_dir the default location for the dictionaries.
5553 * @return
5554 * - 0 on success.
5555 * - -1 on failure.
5556 */
5557 int fr_dict_global_init(TALLOC_CTX *ctx, char const *dict_dir)
5558 {
5559 if (!protocol_by_name) {
5560 protocol_by_name = fr_hash_table_create(ctx, dict_protocol_name_hash, dict_protocol_name_cmp, NULL);
5561 if (!protocol_by_name) {
5562 fr_strerror_printf("Failed initializing protocol_by_name hash");
5563 return -1;
5564 }
5565 }
5566
5567 if (!protocol_by_num) {
5568 protocol_by_num = fr_hash_table_create(ctx, dict_protocol_num_hash, dict_protocol_num_cmp, NULL);
5569 if (!protocol_by_num) {
5570 fr_strerror_printf("Failed initializing protocol_by_num hash");
5571 return -1;
5572 }
5573 }
5574
5575 talloc_free(default_dict_dir); /* Free previous value */
5576 default_dict_dir = talloc_strdup(ctx, dict_dir);
5577
5578 return 0;
5579 }
5580
5581 /*
5582 * [a-zA-Z0-9_-:.]+
5583 */
5584 ssize_t fr_dict_valid_name(char const *name, ssize_t len)
5585 {
5586 char const *p = name, *end;
5587
5588 if (len < 0) len = strlen(name);
5589
5590 if (len > FR_DICT_ATTR_MAX_NAME_LEN) {
5591 fr_strerror_printf("Attribute name is too long");
5592 return -1;
5593 }
5594
5595 end = p + len;
5596
5597 do {
5598 if (!fr_dict_attr_allowed_chars[(uint8_t)*p]) {
5599 fr_strerror_printf("Invalid character '%pV' in attribute name \"%pV\"",
5600 fr_box_strvalue_len(p, 1), fr_box_strvalue_len(name, len));
5601
5602 return -(p - name);
5603 }
5604 p++;
5605 } while (p < end);
5606
5607 return len;
5608 }
5609
5610 ssize_t fr_dict_valid_oid_str(char const *name, ssize_t len)
5611 {
5612 char const *p = name, *end;
5613
5614 if (len < 0) len = strlen(name);
5615 end = p + len;
5616
5617 do {
5618 if (!fr_dict_attr_allowed_chars[(uint8_t)*p] && (*p != '.')) {
5619 fr_strerror_printf("Invalid character '%pV' in oid string \"%pV\"",
5620 fr_box_strvalue_len(p, 1), fr_box_strvalue_len(name, len));
5621
5622 return -(p - name);
5623 }
5624 p++;
5625 } while (p < end);
5626
5627 return len;
5628 }
5629
5630 void fr_dict_verify(char const *file, int line, fr_dict_attr_t const *da)
5631 {
5632 int i;
5633 fr_dict_attr_t const *da_p;
5634
5635 if (!da) {
5636 FR_FAULT_LOG("CONSISTENCY CHECK FAILED %s[%u]: fr_dict_attr_t pointer was NULL", file, line);
5637
5638 if (!fr_cond_assert(0)) fr_exit_now(1);
5639 }
5640
5641 (void) talloc_get_type_abort_const(da, fr_dict_attr_t);
5642
5643 if ((!da->flags.is_root) && (da->depth == 0)) {
5644 FR_FAULT_LOG("CONSISTENCY CHECK FAILED %s[%u]: fr_dict_attr_t %s vendor: %i, attr %i: "
5645 "Is not root, but depth is 0",
5646 file, line, da->name, fr_dict_vendor_num_by_da(da), da->attr);
5647
5648 if (!fr_cond_assert(0)) fr_exit_now(1);
5649 }
5650
5651 if (da->depth > FR_DICT_MAX_TLV_STACK) {
5652 FR_FAULT_LOG("CONSISTENCY CHECK FAILED %s[%u]: fr_dict_attr_t %s vendor: %i, attr %i: "
5653 "Indicated depth (%u) greater than TLV stack depth (%u)",
5654 file, line, da->name, fr_dict_vendor_num_by_da(da), da->attr,
5655 da->depth, FR_DICT_MAX_TLV_STACK);
5656
5657 if (!fr_cond_assert(0)) fr_exit_now(1);
5658 }
5659
5660 for (da_p = da; da_p; da_p = da_p->next) {
5661 (void) talloc_get_type_abort_const(da_p, fr_dict_attr_t);
5662 }
5663
5664 for (i = da->depth, da_p = da; (i >= 0) && da; i--, da_p = da_p->parent) {
5665 if (i != (int)da_p->depth) {
5666 FR_FAULT_LOG("CONSISTENCY CHECK FAILED %s[%u]: fr_dict_attr_t %s vendor: %i, attr %i: "
5667 "Depth out of sequence, expected %i, got %u",
5668 file, line, da->name, fr_dict_vendor_num_by_da(da), da->attr, i, da_p->depth);
5669
5670 if (!fr_cond_assert(0)) fr_exit_now(1);
5671 }
5672
5673 }
5674
5675 if ((i + 1) < 0) {
5676 FR_FAULT_LOG("CONSISTENCY CHECK FAILED %s[%u]: fr_dict_attr_t top of hierarchy was not at depth 0",
5677 file, line);
5678
5679 if (!fr_cond_assert(0)) fr_exit_now(1);
5680 }
5681 }