The FreeRADIUS server $Id: f3670dba8951ca10eb4948feb3dc3db9423a334f $
Loading...
Searching...
No Matches
dict_fixup.c
Go to the documentation of this file.
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/** Code to apply fctx and finalisation steps to a dictionary
18 *
19 * @file src/lib/util/dict_fixup.c
20 *
21 * @copyright 2020 The FreeRADIUS server project
22 * @copyright 2020,2024 Arran Cudbard-Bell <a.cudbardb@freeradius.org>
23 */
24RCSID("$Id: 605ec097907ea21940af03c5ba2076d15e300ac7 $")
25
26#include <freeradius-devel/util/dict.h>
27#include <freeradius-devel/util/file.h>
28#include <freeradius-devel/util/value.h>
29
30#include "dict_fixup_priv.h"
31
32/** Common fields for every fixup structure
33 *
34 */
35typedef struct {
36 fr_dlist_t entry; //!< Entry in linked list of fctx.
38
39/** Add an enumeration value to an attribute that wasn't defined at the time the value was parsed
40 *
41 */
42typedef struct {
43 dict_fixup_common_t common; //!< Common fields.
44
45 char *filename; //!< where the line being fixed up.
46 int line; //!< ditto.
47
48 char *alias; //!< we need to create.
49 fr_dict_attr_t *alias_parent; //!< Where to add the alias.
50
51 char *ref; //!< what the alias references.
52 fr_dict_attr_t *ref_parent; //!< Parent attribute to resolve the 'attribute' string in.
54
55/** Add an enumeration value to an attribute that wasn't defined at the time the value was parsed
56 *
57 */
58typedef struct {
59 dict_fixup_common_t common; //!< Common fields.
60
61 char *filename; //!< where the line being fixed up.
62 int line; //!< ditto.
63
64 char *attribute; //!< we couldn't find (and will need to resolve later).
65 char *name; //!< Raw enum name.
66 char *value; //!< Raw enum value. We can't do anything with this until
67 //!< we know the attribute type, which we only find out later.
68
69 fr_dict_attr_t const *parent; //!< Parent attribute to resolve the 'attribute' string in.
71
72/** Resolve a group reference
73 *
74 */
75typedef struct {
76 dict_fixup_common_t common; //!< Common fields.
77
78 fr_dict_attr_t *da; //!< FR_TYPE_GROUP to fix
79 char *ref; //!< the reference name
81
82/** Clone operation from one tree node to another
83 *
84 */
85typedef struct {
86 dict_fixup_common_t common; //!< Common fields.
87
88 fr_dict_attr_t *da; //!< to populate with cloned information.
89 char *ref; //!< the target attribute to clone
91
92/** Run fixup callbacks for a VSA
93 *
94 */
95typedef struct {
96 dict_fixup_common_t common; //!< Common fields.
97
98 fr_dict_attr_t *da; //!< FR_TYPE_VSA to fix
100
101/** Dictionary attribute namespaces need their hash tables finalised
102 *
103 */
104typedef struct {
105 dict_fixup_common_t common; //!< Common fields.
106
107 fr_hash_table_t *hash; //!< We need to finalise.
109
110/** Initialise common fields in fixup struct, and add it to a fixup list
111 *
112 * @param[in] fixup_list to add fixup to.
113 * @param[in] common common header to populate.
114 * @return
115 * - 0 on success.
116 * - -1 on out of memory.
117 */
118static inline CC_HINT(always_inline) int dict_fixup_common(fr_dlist_head_t *fixup_list, dict_fixup_common_t *common)
119{
120 fr_dlist_insert_tail(fixup_list, common);
121
122 return 0;
123}
124
125/** Resolve a reference string to a dictionary attribute
126 *
127 * @param[out] da_p Where the attribute will be stored
128 * @param[in] rel Relative attribute to resolve from.
129 * @param[in] in Reference string.
130 * @return
131 * - <0 on error
132 * - 0 on parse OK, but *da_p is NULL;
133 * - 1 for parse OK, and *da_p is !NULL
134 */
136{
137 fr_dict_t *dict = fr_dict_unconst(rel->dict);
138 fr_dict_attr_t const *da = rel;
139 fr_slen_t slen;
140
141 *da_p = NULL;
142
143 /*
144 * Are we resolving a foreign reference?
145 */
146 if (fr_sbuff_next_if_char(in, '@')) {
147 char proto_name[FR_DICT_ATTR_MAX_NAME_LEN + 1];
148 fr_sbuff_t proto_name_sbuff = FR_SBUFF_OUT(proto_name, sizeof(proto_name));
149 size_t len;
150
151 /*
152 * @.foo is "foo from the current root".
153 *
154 * This is a bit clearer than "foo".
155 */
156 if (fr_sbuff_next_if_char(in, '.')) {
157 if (fr_sbuff_is_char(in, '.')) goto above_root;
158
159 da = rel->dict->root;
160 goto more;
161 }
162
163 slen = dict_by_protocol_substr(NULL, &dict, in, NULL);
164 /* Need to load it... */
165 if (slen <= 0) {
166 /* Quiet coverity */
167 fr_sbuff_terminate(&proto_name_sbuff);
168
169 /* Fixme, probably want to limit allowed chars */
170 if ((fr_sbuff_out_bstrncpy_until(&len, &proto_name_sbuff, in, SIZE_MAX,
171 &FR_SBUFF_TERMS(L(""), L(".")), NULL) < 0) || (len == 0)) {
172 invalid_name:
173 fr_strerror_const("Invalid protocol name");
174 return -1;
175 }
176
177 /*
178 * The filenames are lowercase. The names in the dictionaries are case-insensitive. So
179 * we mash the name to all lowercase.
180 */
181 fr_tolower(proto_name);
182
183 /*
184 * Catch this early, so people don't do stupid things
185 * like put slashes in the references and then claim
186 * it's a security issue.
187 */
188 if (fr_dict_valid_oid_str(proto_name, -1) < 0) goto invalid_name;
189
190 /*
191 * Load the new dictionary, and mark it as loaded from our dictionary.
192 */
193 if (fr_dict_protocol_afrom_file(&dict, proto_name, NULL, (rel->dict)->root->name) < 0) {
194 fr_strerror_printf_push("Perhaps there is a '.' missing before the attribute name in %.*s ?",
196 return -1;
197 }
198
199 if (fr_hash_table_insert((rel->dict)->autoref, dict) != 0) {
200 fr_strerror_const("Failed inserting into internal autoref table");
201 return -1;
202 }
203 }
204
205 /*
206 * Didn't stop at an attribute ref... we're done
207 */
208 if (fr_sbuff_eof(in)) {
209 *da_p = dict->root;
210 return 1;
211 }
212
213 da = dict->root;
214 }
215
216 /*
217 * ref=.foo is a ref from the current parent.
218 *
219 * ref=@foo is a ref from the root of the tree.
220 */
221
222 if (!fr_sbuff_next_if_char(in, '.')) {
223 fr_strerror_printf("Invalid reference '%s' - it should start with '@' (from the root), or '.' (from the parent)",
225 return -1;
226 }
227
228 /*
229 * First '.' makes it relative, subsequent ones traverse up the tree.
230 *
231 * No '.' means use the root.
232 */
233 while (fr_sbuff_next_if_char(in, '.')) {
234 if (!da->parent) {
235 above_root:
236 fr_strerror_const("Reference attempted to navigate above dictionary root");
237 return -1;
238 }
239 da = da->parent;
240 }
241
242 /*
243 * Look up the attribute. Note that this call will
244 * update *da_p with a partial reference if it exists.
245 */
246more:
247 slen = fr_dict_attr_by_oid_substr(NULL, da_p, da, in, NULL);
248 if (slen < 0) return -1;
249
250 if (slen == 0) {
251 *da_p = NULL;
252 return 0;
253 }
254
255 return 1;
256}
257
258/** Add an enumeration value to an attribute which has not yet been defined
259 *
260 * @param[in] fctx Holds current dictionary parsing information.
261 * @param[in] filename this fixup relates to.
262 * @param[in] line this fixup relates to.
263 * @param[in] attr The OID string pointing to the attribute
264 * to add the enumeration value to.
265 * @param[in] attr_len The length of the attr string.
266 * @param[in] name The name of the enumv.
267 * @param[in] name_len Length of the name string.
268 * @param[in] value Value string. This is kept as a string until we know
269 * what type we want to transform it into.
270 * @param[in] value_len Length of the value string.
271 * @param[in] parent of this attribute.
272 * @return
273 * - 0 on success.
274 * - -1 on out of memory.
275 */
276int dict_fixup_enumv_enqueue(dict_fixup_ctx_t *fctx, char const *filename, int line,
277 char const *attr, size_t attr_len,
278 char const *name, size_t name_len,
279 char const *value, size_t value_len,
280 fr_dict_attr_t const *parent)
281{
282 dict_fixup_enumv_t *fixup;
283
284 fixup = talloc(fctx->pool, dict_fixup_enumv_t);
285 if (!fixup) {
286 oom:
287 fr_strerror_const("Out of memory");
288 return -1;
289 }
290 *fixup = (dict_fixup_enumv_t) {
291 .attribute = talloc_bstrndup(fixup, attr, attr_len),
292 .name = talloc_bstrndup(fixup, name, name_len),
293 .value = talloc_bstrndup(fixup, value, value_len),
294 .parent = parent
295 };
296 if (!fixup->attribute || !fixup->name || !fixup->value) goto oom;
297
298 fixup->filename = talloc_strdup(fixup, filename);
299 if (!fixup->filename) goto oom;
300 fixup->line = line;
301
302 return dict_fixup_common(&fctx->enumv, &fixup->common);
303}
304
305/** Add a previously defined enumeration value to an existing attribute
306 *
307 * @param[in] fctx Holds current dictionary parsing information.
308 * @param[in] fixup Hash table to fill.
309 * @return
310 * - 0 on success.
311 * - -1 on failure.
312 */
313static inline CC_HINT(always_inline) int dict_fixup_enumv_apply(UNUSED dict_fixup_ctx_t *fctx, dict_fixup_enumv_t *fixup)
314{
315 fr_dict_attr_t *da;
318 int ret;
319 fr_dict_attr_t const *da_const;
320
321 da_const = fr_dict_attr_by_oid(NULL, fixup->parent, fixup->attribute);
322 if (!da_const) {
323 fr_strerror_printf_push("Failed resolving ATTRIBUTE referenced by VALUE '%s' at %s[%d]",
324 fixup->name, fr_cwd_strip(fixup->filename), fixup->line);
325 return -1;
326 }
327 da = fr_dict_attr_unconst(da_const);
328 type = da->type;
329
330 if (fr_value_box_from_str(fixup, &value, type, NULL,
331 fixup->value, talloc_strlen(fixup->value),
332 NULL) < 0) {
333 fr_strerror_printf_push("Invalid VALUE '%pV' for attribute '%s' at %s[%d]",
335 da->name,
336 fr_cwd_strip(fixup->filename), fixup->line);
337 return -1;
338 }
339
340 ret = fr_dict_enum_add_name(da, fixup->name, &value, false, false);
342 da->flags.has_fixup = false;
343
344 return ret;
345}
346
347/** Resolve a group reference
348 *
349 * This is required as the reference may point to another dictionary which
350 * hasn't been loaded yet.
351 *
352 * @param[in] fctx Holds current dictionary parsing information.
353 * @param[in] da The group dictionary attribute.
354 * @param[in] ref OID string representing what the group references.
355 * @return
356 * - 0 on success.
357 * - -1 on out of memory.
358 */
360{
361 dict_fixup_group_t *fixup;
362
363 fixup = talloc(fctx->pool, dict_fixup_group_t);
364 if (!fixup) {
365 fr_strerror_const("Out of memory");
366 return -1;
367 }
368 *fixup = (dict_fixup_group_t) {
369 .da = da,
370 .ref = talloc_strdup(fixup, ref),
371 };
372
373 da->flags.has_fixup = true;
374
375 return dict_fixup_common(&fctx->group, &fixup->common);
376}
377
378/** Resolve a group reference
379 *
380 * @param[in] fctx Holds current dictionary parsing information.
381 * @param[in] fixup Hash table to fill.
382 * @return
383 * - 0 on success.
384 * - -1 on failure.
385 */
386static inline CC_HINT(always_inline) int dict_fixup_group_apply(UNUSED dict_fixup_ctx_t *fctx, dict_fixup_group_t *fixup)
387{
388 fr_dict_attr_t const *da;
389
390 (void) fr_dict_protocol_reference(&da, fixup->da->parent, &FR_SBUFF_IN_STR(fixup->ref));
391 if (!da) {
392 fr_strerror_printf_push("Failed resolving reference for attribute %s at %s[%d]",
393 fixup->da->name, fr_cwd_strip(fr_dict_attr_filename(fixup->da)), fixup->da->line);
394 return -1;
395 }
396
397 if (da->type != FR_TYPE_TLV) {
398 fr_strerror_printf("References MUST be to attributes of type 'tlv' at %s[%d]",
399 fr_cwd_strip(fr_dict_attr_filename(fixup->da)), fixup->da->line);
400 return -1;
401 }
402
403 if (fr_dict_attr_ref(da)) {
404 fr_strerror_printf("References MUST NOT refer to an ATTRIBUTE which also has 'ref=...' at %s[%d]",
405 fr_cwd_strip(fr_dict_attr_filename(fixup->da)), fixup->da->line);
406 return -1;
407 }
408
409 fixup->da->flags.has_fixup = false;
410
411 return dict_attr_ref_resolve(fixup->da, da);
412}
413
414/** Clone one area of a tree into another
415 *
416 * These must be processed later to ensure that we've finished building an
417 * attribute by the time it has been cloned.
418 *
419 * @param[in] fctx Holds current dictionary parsing information.
420 * @param[in] da The group dictionary attribute.
421 * @param[in] ref OID string representing what the group references..
422 * @return
423 * - 0 on success.
424 * - -1 on out of memory.
425 */
427{
428 dict_fixup_clone_t *fixup;
429
430 fr_assert(!fr_type_is_leaf(da->type));
431
432 /*
433 * Delay type checks until we've loaded all of the
434 * dictionaries. This means that errors are produced
435 * later, but that shouldn't matter for the default
436 * dictionaries. They're supposed to work.
437 */
438 fixup = talloc(fctx->pool, dict_fixup_clone_t);
439 if (!fixup) {
440 fr_strerror_const("Out of memory");
441 return -1;
442 }
443 *fixup = (dict_fixup_clone_t) {
444 .da = da,
445 .ref = talloc_strdup(fixup, ref)
446 };
447
448 return dict_fixup_common(&fctx->clone, &fixup->common);
449}
450
451/** Clone a dictionary attribute from a ref
452 *
453 * @param[in] dst_p will either be inserted directly, with fields from the clone, or will be
454 * cloned, and then inserted. In this case the original dst da will be freed
455 * and the new cloned attribute will be written back to dst_p.
456 * @param[in] src to clone.
457 * @return
458 * - 0 on success.
459 * - -1 on failure.
460 */
462{
463 fr_dict_attr_t *dst = *dst_p;
464 fr_dict_t *dict = fr_dict_unconst(dst->dict);
465
466 /*
467 * @todo - allow this for structural attributes, so long as they don't have a child TLV.
468 */
469 if (src->dict->proto != dst->dict->proto) {
470 fr_strerror_printf("Incompatible protocols. Referenced '%s', referencing '%s'. Defined at %s[%d]",
471 src->dict->proto->name, dst->dict->proto->name, fr_dict_attr_filename(dst), dst->line);
472 return -1;
473 }
474
475 /*
476 * The referenced DA is higher than the one we're creating. Ensure it's not a parent.
477 *
478 * @todo - Do we want to require that aliases only go deeper in the tree? Otherwise aliases can
479 * make the tree a lot more complicated.
480 */
481 if (src->depth < dst->depth) {
482 fr_dict_attr_t const *parent;
483
484 for (parent = dst->parent; !parent->flags.is_root; parent = parent->parent) {
485 if (parent == src) {
486 fr_strerror_printf("References MUST NOT be to a parent attribute %s at %s[%d]",
487 parent->name, fr_cwd_strip(fr_dict_attr_filename(dst)), dst->line);
488 return -1;
489 }
490 }
491 }
492
493 if (fr_dict_attr_ref(src)) {
494 fr_strerror_printf("References MUST NOT refer to an ATTRIBUTE which itself has a 'ref=...' at %s[%d]",
495 fr_cwd_strip(fr_dict_attr_filename(dst)), dst->line);
496 return -1;
497 }
498
499 /*
500 * Leaf attributes can be cloned. TLV and STRUCT can be cloned. But all other data types cannot
501 * be cloned.
502 *
503 * And while we're at it, copy the flags over.
504 */
505 switch (src->type) {
506 default:
507 fr_strerror_printf("References MUST NOT refer to an attribute of data type '%s' at %s[%d]",
508 fr_type_to_str(src->type), fr_cwd_strip(fr_dict_attr_filename(dst)), dst->line);
509 return -1;
510
511 case FR_TYPE_TLV:
512 dst->flags.type_size = src->flags.type_size;
513 dst->flags.length = src->flags.length;
515
516 case FR_TYPE_STRUCT:
517 if (!dict_attr_children(src)) {
518 fr_strerror_printf_push("Reference %s has no children defined at %s[%d]",
519 src->name, fr_cwd_strip(fr_dict_attr_filename(dst)), dst->line);
520 return -1;
521 }
522 break;
523 }
524
525 dst->flags.array = src->flags.array;
526 dst->flags.is_known_width = src->flags.is_known_width;
527 dst->flags.internal = src->flags.internal;
528 dst->flags.name_only = src->flags.name_only;
529
530 /*
531 * Clone the children from the source to the dst.
532 *
533 * Note that the destination may already have children!
534 */
535 if (dict_attr_acopy_children(dict, dst, src) < 0) {
536 fr_strerror_printf_push("Failed populating attribute '%s' with children of %s", dst->name, src->name);
537 return -1;
538 }
539
541}
542
543/** Clone one are of a tree into another
544 *
545 * @param[in] fctx Holds current dictionary parsing information.
546 * @param[in] fixup Containing source/destination of the clone.
547 * @return
548 * - 0 on success.
549 * - -1 on failure.
550 */
551static inline CC_HINT(always_inline) int dict_fixup_clone_apply(UNUSED dict_fixup_ctx_t *fctx, dict_fixup_clone_t *fixup)
552{
553 fr_dict_attr_t const *src;
554
555 (void) fr_dict_protocol_reference(&src, fixup->da->parent, &FR_SBUFF_IN_STR(fixup->ref));
556 if (!src) {
557 fr_strerror_printf_push("Failed resolving reference for attribute %s at %s[%d]",
558 fixup->da->name, fr_cwd_strip(fr_dict_attr_filename(fixup->da)), fixup->da->line);
559 return -1;
560 }
561
562 fixup->da->flags.has_fixup = false;
563 return dict_fixup_clone(&fixup->da, src);
564}
565
566/** Clone enumeration values from one attribute to another
567 *
568 * These must be processed later to ensure that we've finished building an
569 * attribute by the time it has been cloned.
570 *
571 * @param[in] fctx Holds current dictionary parsing information.
572 * @param[in] da The group dictionary attribute.
573 * @param[in] ref OID string representing what the group references..
574 * @return
575 * - 0 on success.
576 * - -1 on out of memory.
577 */
579{
580 dict_fixup_clone_t *fixup;
581
582 fr_assert(fr_type_is_leaf(da->type));
583
584 /*
585 * Delay type checks until we've loaded all of the
586 * dictionaries. This means that errors are produced
587 * later, but that shouldn't matter for the default
588 * dictionaries. They're supposed to work.
589 */
590 fixup = talloc(fctx->pool, dict_fixup_clone_t);
591 if (!fixup) {
592 fr_strerror_const("Out of memory");
593 return -1;
594 }
595 *fixup = (dict_fixup_clone_t) {
596 .da = da,
597 .ref = talloc_strdup(fixup, ref)
598 };
599
600 return dict_fixup_common(&fctx->clone_enum, &fixup->common);
601}
602
603/** Clone one are of a tree into another
604 *
605 * @param[in] fctx Holds current dictionary parsing information.
606 * @param[in] fixup Containing source/destination of the clone.
607 * @return
608 * - 0 on success.
609 * - -1 on failure.
610 */
611static inline CC_HINT(always_inline) int dict_fixup_clone_enum_apply(UNUSED dict_fixup_ctx_t *fctx, dict_fixup_clone_t *fixup)
612{
613 fr_dict_attr_t const *src;
614
615 /*
616 * This extension must already exist.
617 */
619
620 /*
621 * Find the referenced attribute, and validate it.
622 */
623 (void) fr_dict_protocol_reference(&src, fixup->da->parent, &FR_SBUFF_IN_STR(fixup->ref));
624 if (!src) {
625 fr_strerror_printf_push("Failed resolving reference for attribute %s at %s[%d]",
626 fixup->da->name, fr_cwd_strip(fr_dict_attr_filename(fixup->da)), fixup->da->line);
627 return -1;
628 }
629
631 fr_strerror_printf_push("Reference %s has no VALUEs defined at %s[%d]",
632 fixup->ref, fr_cwd_strip(fr_dict_attr_filename(fixup->da)), fixup->da->line);
633 return -1;
634 }
635
636 /*
637 * Allow enums to be copied from any protocol, so long as the attribute is not a key, and not of
638 * type 'attribute'.
639 */
640 if (fr_dict_attr_is_key_field(src) || fr_dict_attr_is_key_field(fixup->da) || (src->type == FR_TYPE_ATTR)) {
641 fr_strerror_printf("Cannot clone VALUEs from 'key=...' or type 'attribute' at %s[%d]",
642 fr_dict_attr_filename(fixup->da), fixup->da->line);
643 return -1;
644 }
645
646 if (fr_dict_attr_ref(src)) {
647 fr_strerror_printf("References MUST NOT refer to an ATTRIBUTE which itself has a 'ref=...' at %s[%d]",
648 fr_cwd_strip(fr_dict_attr_filename(fixup->da)), fixup->da->line);
649 return -1;
650 }
651
652 if (!dict_attr_ext_copy(&fixup->da, src, FR_DICT_ATTR_EXT_ENUMV)) {
653 fr_strerror_printf("Reference copied no VALUEs from type '%s' at %s[%d]",
654 fr_type_to_str(fixup->da->type),
655 fr_cwd_strip(fr_dict_attr_filename(fixup->da)), fixup->da->line);
656 return -1;
657 }
658
659 fixup->da->flags.has_fixup = false;
660 return 0;
661}
662
663/** Push a fixup for a VSA.
664 *
665 * This is required so that we can define VENDORs for all VSAs, even
666 * if the dictionary doesn't contain VENDOR children for that VSA.
667 * This fixup means that we can define VENDORs elsewhere, and then
668 * use them in all VSA definitions. It means that we don't have to
669 * do these lookups at run-time.
670 *
671 * @param[in] fctx Holds current dictionary parsing information.
672 * @param[in] da The group dictionary attribute.
673 * @return
674 * - 0 on success.
675 * - -1 on out of memory.
676 */
678{
679 dict_fixup_vsa_t *fixup;
680
681 fixup = talloc(fctx->pool, dict_fixup_vsa_t);
682 if (!fixup) {
683 fr_strerror_const("Out of memory");
684 return -1;
685 }
686 *fixup = (dict_fixup_vsa_t) {
687 .da = da,
688 };
689
690 return dict_fixup_common(&fctx->vsa, &fixup->common);
691}
692
693/** Run VSA fixups
694 *
695 * @param[in] fctx Holds current dictionary parsing information.
696 * @param[in] fixup entry for fixup
697 * @return
698 * - 0 on success.
699 * - -1 on failure.
700 */
701static inline CC_HINT(always_inline) int dict_fixup_vsa_apply(UNUSED dict_fixup_ctx_t *fctx, dict_fixup_vsa_t *fixup)
702{
705 fr_hash_iter_t iter;
706
707 if (!dict->vendors_by_num) return 0;
708
709 for (dv = fr_hash_table_iter_init(dict->vendors_by_num, &iter);
710 dv;
711 dv = fr_hash_table_iter_next(dict->vendors_by_num, &iter)) {
712 if (dict_attr_child_by_num(fixup->da, dv->pen)) continue;
713
714 if (fr_dict_attr_add(dict, fixup->da, dv->name, dv->pen, FR_TYPE_VENDOR, NULL) < 0) return -1;
715 }
716
717 fixup->da->flags.has_fixup = false;
718 return 0;
719}
720
721
722/** Resolve a group reference
723 *
724 * This is required as the reference may point to another dictionary which
725 * hasn't been loaded yet.
726 *
727 * @param[in] fctx Holds current dictionary parsing information.
728 * @param[in] filename this fixup relates to.
729 * @param[in] line this fixup relates to.
730 * @param[in] alias_parent where to add the alias.
731 * @param[in] alias alias to add.
732 * @param[in] ref_parent attribute that should contain the reference.
733 * @param[in] ref OID string representing what the group references.
734 * @return
735 * - 0 on success.
736 * - -1 on out of memory.
737 */
738int dict_fixup_alias_enqueue(dict_fixup_ctx_t *fctx, char const *filename, int line,
739 fr_dict_attr_t *alias_parent, char const *alias,
740 fr_dict_attr_t *ref_parent, char const *ref)
741{
742 dict_fixup_alias_t *fixup;
743
744 fixup = talloc(fctx->pool, dict_fixup_alias_t);
745 if (!fixup) {
746 oom:
747 fr_strerror_const("Out of memory");
748 return -1;
749 }
750 *fixup = (dict_fixup_alias_t) {
751 .alias = talloc_strdup(fixup, alias),
752 .alias_parent = alias_parent,
753 .ref = talloc_strdup(fixup, ref),
754 .ref_parent = ref_parent
755 };
756
757 fixup->filename = talloc_strdup(fixup, filename);
758 if (!fixup->filename) goto oom;
759 fixup->line = line;
760
761 alias_parent->flags.has_fixup = true;
762
763 return dict_fixup_common(&fctx->alias, &fixup->common);
764}
765
766static inline CC_HINT(always_inline) int dict_fixup_alias_apply(UNUSED dict_fixup_ctx_t *fctx, dict_fixup_alias_t *fixup)
767{
768 fr_dict_attr_t const *da;
769
770 /*
771 * The <ref> can be a name.
772 */
773 da = fr_dict_attr_by_oid(NULL, fixup->ref_parent, fixup->ref);
774 if (!da) {
775 fr_strerror_printf("Attribute '%s' aliased by '%s' doesn't exist in namespace '%s', at %s[%d]",
776 fixup->ref, fixup->alias, fixup->ref_parent->name, fixup->filename, fixup->line);
777 return -1;
778 }
779
780 fixup->alias_parent->flags.has_fixup = false;
781 return dict_attr_alias_add(fixup->alias_parent, fixup->alias, da, true);
782}
783
784/** Initialise a fixup ctx
785 *
786 * @param[in] fctx to initialise.
787 * @return
788 * - 0 on success.
789 * - -1 on failure.
790 */
792{
793 if (fctx->pool) return 0;
794
795 fr_dlist_talloc_init(&fctx->enumv, dict_fixup_enumv_t, common.entry);
796 fr_dlist_talloc_init(&fctx->group, dict_fixup_group_t, common.entry);
797 fr_dlist_talloc_init(&fctx->clone, dict_fixup_clone_t, common.entry);
799 fr_dlist_talloc_init(&fctx->vsa, dict_fixup_vsa_t, common.entry);
800 fr_dlist_talloc_init(&fctx->alias, dict_fixup_alias_t, common.entry);
801
802 fctx->pool = talloc_pool(NULL, DICT_FIXUP_POOL_SIZE);
803 if (!fctx->pool) return -1;
804
805 return 0;
806}
807
808/** Apply all outstanding fixes to a set of dictionaries
809 *
810 */
812{
813
814#define APPLY_FIXUP(_fctx, _list, _func, _type) \
815do { \
816 _type *_fixup; \
817 while ((_fixup = fr_dlist_head(&(_fctx)->_list))) { \
818 if (_func(_fctx, _fixup) < 0) return -1; \
819 fr_dlist_remove(&(_fctx)->_list, _fixup); \
820 talloc_free(_fixup); \
821 } \
822} while (0)
823
824 /*
825 * Apply all the fctx in order
826 *
827
828 * - Enumerations first as they have no dependencies
829 * - Group references next, as group attributes may be cloned.
830 * - Clones last as all other references and additions should
831 * be applied before cloning.
832 * - Clone enum clones the enumeration values from a dedicated
833 * enum, or another attribute with enumerations.
834 * - VSAs
835 * - Aliases last as all attributes need to be defined.
836 */
843
844 TALLOC_FREE(fctx->pool);
845
846 return 0;
847}
848
849/** Fixup all hash tables in the dictionary so they're suitable for threaded access
850 *
851 */
852static int _dict_attr_fixup_hash_tables(fr_dict_attr_t const *da, UNUSED void *uctx)
853{
854 {
856
858 if (ext) {
861 }
862 }
863
864 {
866
869 }
870
871 return 0;
872}
873
874/** Walk a dictionary finalising the hash tables in all attributes with a distinct namespace
875 *
876 * @param[in] dict to finalise namespaces for.
877 */
879{
881
882 (void)_dict_attr_fixup_hash_tables(root, NULL);
883
885
886 /*
887 * Walk over all of the hash tables to ensure they're
888 * initialized. We do this because the threads may perform
889 * lookups, and we don't want multi-threaded re-ordering
890 * of the table entries. That would be bad.
891 */
892 fr_hash_table_fill(dict->vendors_by_name);
893 fr_hash_table_fill(dict->vendors_by_num);
894}
int const char int line
Definition acutest.h:702
#define RCSID(id)
Definition build.h:560
#define L(_str)
Helper for initialising arrays of string literals.
Definition build.h:228
#define FALL_THROUGH
clang 10 doesn't recognised the FALL-THROUGH comment anymore
Definition build.h:391
#define UNUSED
Definition build.h:384
fr_dict_t * dict
Definition common.c:31
int dict_attr_acopy_children(fr_dict_t *dict, fr_dict_attr_t *dst, fr_dict_attr_t const *src)
Copy the children of an existing attribute.
Definition dict_util.c:1243
int fr_dict_attr_add_initialised(fr_dict_attr_t *da)
A variant of fr_dict_attr_t that allows a pre-allocated, populated fr_dict_attr_t to be added.
Definition dict_util.c:1897
ssize_t fr_dict_valid_oid_str(char const *name, ssize_t len)
Definition dict_util.c:5118
int fr_dict_enum_add_name(fr_dict_attr_t *da, char const *name, fr_value_box_t const *value, bool coerce, bool replace)
Add a value name.
Definition dict_util.c:2272
char const * name
Vendor name.
Definition dict.h:298
fr_dict_t * fr_dict_unconst(fr_dict_t const *dict)
Coerce to non-const.
Definition dict_util.c:5040
fr_dict_t const * fr_dict_by_da(fr_dict_attr_t const *da)
Attempt to locate the protocol dictionary containing an attribute.
Definition dict_util.c:2938
fr_dict_attr_t * fr_dict_attr_unconst(fr_dict_attr_t const *da)
Coerce to non-const.
Definition dict_util.c:5052
fr_slen_t fr_dict_attr_by_oid_substr(fr_dict_attr_err_t *err, fr_dict_attr_t const **out, fr_dict_attr_t const *parent, fr_sbuff_t *in, fr_sbuff_term_t const *tt))
Resolve an attribute using an OID string.
Definition dict_util.c:2644
fr_dict_attr_t const * fr_dict_root(fr_dict_t const *dict)
Return the root attribute of a dictionary.
Definition dict_util.c:2720
int fr_dict_walk(fr_dict_attr_t const *da, fr_dict_walk_t callback, void *uctx))
Definition dict_util.c:5230
uint32_t pen
Private enterprise number.
Definition dict.h:294
int fr_dict_protocol_afrom_file(fr_dict_t **out, char const *proto_name, char const *proto_dir, char const *dependent))
(Re)-initialize a protocol dictionary
char const * fr_dict_attr_filename(fr_dict_attr_t const *da)
Resolve an attribute's location file id to the filename.
Definition dict_util.c:795
@ FR_DICT_ATTR_EXT_ENUMV
Enumeration values.
Definition dict.h:188
int fr_dict_attr_add(fr_dict_t *dict, fr_dict_attr_t const *parent, char const *name, unsigned int attr, fr_type_t type, fr_dict_attr_flags_t const *flags))
Add an attribute to the dictionary.
Definition dict_util.c:2023
fr_dict_attr_t const * fr_dict_attr_by_oid(fr_dict_attr_err_t *err, fr_dict_attr_t const *parent, char const *oid))
Resolve an attribute using an OID string.
Definition dict_util.c:2693
#define fr_dict_attr_is_key_field(_da)
Definition dict.h:170
static fr_slen_t in
Definition dict.h:904
#define FR_DICT_ATTR_MAX_NAME_LEN
Maximum length of a attribute name.
Definition dict.h:525
Private enterprise.
Definition dict.h:293
fr_hash_table_t * name_by_value
Lookup a name by value.
Definition dict_ext.h:99
static void * fr_dict_attr_ext(fr_dict_attr_t const *da, fr_dict_attr_ext_t ext)
Definition dict_ext.h:122
fr_hash_table_t * value_by_name
Lookup an enumeration value by name.
Definition dict_ext.h:98
static fr_dict_attr_t const * fr_dict_attr_ref(fr_dict_attr_t const *da)
Return the reference associated with a group type attribute.
Definition dict_ext.h:149
Attribute extension - Holds enumeration values.
Definition dict_ext.h:96
static fr_hash_table_t * dict_attr_namespace(fr_dict_attr_t const *da)
Return the namespace hash table associated with the attribute.
static int dict_attr_ref_resolve(fr_dict_attr_t const *da, fr_dict_attr_t const *ref)
static void * dict_attr_ext_copy(fr_dict_attr_t **da_out_p, fr_dict_attr_t const *da_in, fr_dict_attr_ext_t ext)
Copy a single attribute extension from one attribute to another.
static fr_dict_attr_t const ** dict_attr_children(fr_dict_attr_t const *da)
fr_dict_attr_t * da
FR_TYPE_GROUP to fix.
Definition dict_fixup.c:78
dict_fixup_common_t common
Common fields.
Definition dict_fixup.c:59
int dict_fixup_apply(dict_fixup_ctx_t *fctx)
Apply all outstanding fixes to a set of dictionaries.
Definition dict_fixup.c:811
char * ref
the target attribute to clone
Definition dict_fixup.c:89
char * attribute
we couldn't find (and will need to resolve later).
Definition dict_fixup.c:64
static int dict_fixup_clone_apply(UNUSED dict_fixup_ctx_t *fctx, dict_fixup_clone_t *fixup)
Clone one are of a tree into another.
Definition dict_fixup.c:551
char * value
Raw enum value.
Definition dict_fixup.c:66
char * alias
we need to create.
Definition dict_fixup.c:48
char * ref
what the alias references.
Definition dict_fixup.c:51
static int dict_fixup_common(fr_dlist_head_t *fixup_list, dict_fixup_common_t *common)
Initialise common fields in fixup struct, and add it to a fixup list.
Definition dict_fixup.c:118
char * filename
where the line being fixed up.
Definition dict_fixup.c:61
int dict_fixup_enumv_enqueue(dict_fixup_ctx_t *fctx, char const *filename, int line, char const *attr, size_t attr_len, char const *name, size_t name_len, char const *value, size_t value_len, fr_dict_attr_t const *parent)
Add an enumeration value to an attribute which has not yet been defined.
Definition dict_fixup.c:276
int dict_fixup_alias_enqueue(dict_fixup_ctx_t *fctx, char const *filename, int line, fr_dict_attr_t *alias_parent, char const *alias, fr_dict_attr_t *ref_parent, char const *ref)
Resolve a group reference.
Definition dict_fixup.c:738
int dict_fixup_clone_enqueue(dict_fixup_ctx_t *fctx, fr_dict_attr_t *da, char const *ref)
Clone one area of a tree into another.
Definition dict_fixup.c:426
dict_fixup_common_t common
Common fields.
Definition dict_fixup.c:96
dict_fixup_common_t common
Common fields.
Definition dict_fixup.c:76
static int dict_fixup_clone_enum_apply(UNUSED dict_fixup_ctx_t *fctx, dict_fixup_clone_t *fixup)
Clone one are of a tree into another.
Definition dict_fixup.c:611
int dict_fixup_clone_enum_enqueue(dict_fixup_ctx_t *fctx, fr_dict_attr_t *da, char const *ref)
Clone enumeration values from one attribute to another.
Definition dict_fixup.c:578
static int dict_fixup_vsa_apply(UNUSED dict_fixup_ctx_t *fctx, dict_fixup_vsa_t *fixup)
Run VSA fixups.
Definition dict_fixup.c:701
fr_dict_attr_t * alias_parent
Where to add the alias.
Definition dict_fixup.c:49
fr_dlist_t entry
Entry in linked list of fctx.
Definition dict_fixup.c:36
fr_dict_attr_t const * parent
Parent attribute to resolve the 'attribute' string in.
Definition dict_fixup.c:69
static int _dict_attr_fixup_hash_tables(fr_dict_attr_t const *da, UNUSED void *uctx)
Fixup all hash tables in the dictionary so they're suitable for threaded access.
Definition dict_fixup.c:852
dict_fixup_common_t common
Common fields.
Definition dict_fixup.c:105
char * filename
where the line being fixed up.
Definition dict_fixup.c:45
fr_hash_table_t * hash
We need to finalise.
Definition dict_fixup.c:107
int dict_fixup_vsa_enqueue(dict_fixup_ctx_t *fctx, fr_dict_attr_t *da)
Push a fixup for a VSA.
Definition dict_fixup.c:677
int dict_fixup_clone(fr_dict_attr_t **dst_p, fr_dict_attr_t const *src)
Clone a dictionary attribute from a ref.
Definition dict_fixup.c:461
static int dict_fixup_group_apply(UNUSED dict_fixup_ctx_t *fctx, dict_fixup_group_t *fixup)
Resolve a group reference.
Definition dict_fixup.c:386
dict_fixup_common_t common
Common fields.
Definition dict_fixup.c:43
int dict_fixup_group_enqueue(dict_fixup_ctx_t *fctx, fr_dict_attr_t *da, char const *ref)
Resolve a group reference.
Definition dict_fixup.c:359
char * ref
the reference name
Definition dict_fixup.c:79
int fr_dict_protocol_reference(fr_dict_attr_t const **da_p, fr_dict_attr_t const *rel, fr_sbuff_t *in)
Resolve a reference string to a dictionary attribute.
Definition dict_fixup.c:135
static int dict_fixup_enumv_apply(UNUSED dict_fixup_ctx_t *fctx, dict_fixup_enumv_t *fixup)
Add a previously defined enumeration value to an existing attribute.
Definition dict_fixup.c:313
#define APPLY_FIXUP(_fctx, _list, _func, _type)
fr_dict_attr_t * ref_parent
Parent attribute to resolve the 'attribute' string in.
Definition dict_fixup.c:52
int dict_fixup_init(dict_fixup_ctx_t *fctx)
Initialise a fixup ctx.
Definition dict_fixup.c:791
char * name
Raw enum name.
Definition dict_fixup.c:65
void dict_hash_tables_finalise(fr_dict_t *dict)
Walk a dictionary finalising the hash tables in all attributes with a distinct namespace.
Definition dict_fixup.c:878
fr_dict_attr_t * da
FR_TYPE_VSA to fix.
Definition dict_fixup.c:98
static int dict_fixup_alias_apply(UNUSED dict_fixup_ctx_t *fctx, dict_fixup_alias_t *fixup)
Definition dict_fixup.c:766
fr_dict_attr_t * da
to populate with cloned information.
Definition dict_fixup.c:88
dict_fixup_common_t common
Common fields.
Definition dict_fixup.c:86
Add an enumeration value to an attribute that wasn't defined at the time the value was parsed.
Definition dict_fixup.c:42
Clone operation from one tree node to another.
Definition dict_fixup.c:85
Common fields for every fixup structure.
Definition dict_fixup.c:35
Add an enumeration value to an attribute that wasn't defined at the time the value was parsed.
Definition dict_fixup.c:58
Resolve a group reference.
Definition dict_fixup.c:75
Dictionary attribute namespaces need their hash tables finalised.
Definition dict_fixup.c:104
Run fixup callbacks for a VSA.
Definition dict_fixup.c:95
Functions to finalise and fixup dictionaries.
fr_dlist_head_t clone_enum
Clone enum operation to apply.
fr_dlist_head_t alias
Aliases that can't be resolved immediately.
fr_dlist_head_t group
Group references to resolve.
fr_dlist_head_t clone
Clone operation to apply.
fr_dlist_head_t vsa
VSAs to add vendors for.
fr_dlist_head_t enumv
Raw enumeration values to add.
TALLOC_CTX * pool
Temporary pool for fixups, reduces holes.
fr_slen_t dict_by_protocol_substr(fr_dict_attr_err_t *err, fr_dict_t **out, fr_sbuff_t *name, fr_dict_t const *dict_def)
Definition dict_util.c:2735
#define DICT_FIXUP_POOL_SIZE
Definition dict_priv.h:38
fr_dict_attr_t * dict_attr_child_by_num(fr_dict_attr_t const *parent, unsigned int attr)
Internal version of fr_dict_attr_child_by_num.
Definition dict_util.c:3623
int dict_attr_alias_add(fr_dict_attr_t const *parent, char const *alias, fr_dict_attr_t const *ref, bool from_public)
Add an alias to an existing attribute.
Definition dict_util.c:1456
Test enumeration values.
Definition dict_test.h:92
static int fr_dlist_insert_tail(fr_dlist_head_t *list_head, void *ptr)
Insert an item into the tail of a list.
Definition dlist.h:360
#define fr_dlist_talloc_init(_head, _type, _field)
Initialise the head structure of a doubly linked list.
Definition dlist.h:257
Head of a doubly linked list.
Definition dlist.h:51
Entry in a doubly linked list.
Definition dlist.h:41
void * fr_hash_table_iter_next(fr_hash_table_t *ht, fr_hash_iter_t *iter)
Iterate over entries in a hash table.
Definition hash.c:668
void * fr_hash_table_iter_init(fr_hash_table_t *ht, fr_hash_iter_t *iter)
Initialise an iterator.
Definition hash.c:723
void fr_hash_table_fill(fr_hash_table_t *ht)
Ensure all buckets are filled.
Definition hash.c:764
int fr_hash_table_insert(fr_hash_table_t *ht, void const *data)
Insert data into a hash table.
Definition hash.c:501
Stores the state of the current iteration operation.
Definition hash.h:41
char const * fr_cwd_strip(char const *filename)
Intended to be used in logging functions to make output more readable.
Definition file.c:383
fr_type_t
@ FR_TYPE_TLV
Contains nested attributes.
@ FR_TYPE_STRUCT
like TLV, but without T or L, and fixed-width children
@ FR_TYPE_VENDOR
Attribute that represents a vendor in the attribute tree.
fr_sbuff_err_t fr_sbuff_out_bstrncpy_until(size_t *len, fr_sbuff_t *out, fr_sbuff_t *in, size_t max, fr_sbuff_term_t const *tt, fr_sbuff_unescape_rules_t const *u_rules)
ssize_t fr_slen_t
char * fr_tolower(char *str)
Definition misc.c:225
#define fr_assert(_expr)
Definition rad_assert.h:37
static unsigned int hash(char const *username, unsigned int tablesize)
Definition rlm_passwd.c:132
static char const * name
bool fr_sbuff_next_if_char(fr_sbuff_t *sbuff, char c)
Return true if the current char matches, and if it does, advance.
Definition sbuff.c:2247
#define fr_sbuff_start(_sbuff_or_marker)
#define FR_SBUFF_TERMS(...)
Initialise a terminal structure with a list of sorted strings.
Definition sbuff.h:190
#define fr_sbuff_is_char(_sbuff_or_marker, _c)
#define fr_sbuff_eof(_x)
#define FR_SBUFF_IN_STR(_start)
#define FR_SBUFF_OUT(_start, _len_or_end)
#define fr_sbuff_used(_sbuff_or_marker)
fr_aka_sim_id_type_t type
char * talloc_bstrndup(TALLOC_CTX *ctx, char const *in, size_t inlen)
Binary safe strndup function.
Definition talloc.c:618
#define talloc_strdup(_ctx, _str)
Definition talloc.h:149
static size_t talloc_strlen(char const *s)
Returns the length of a talloc array containing a string.
Definition talloc.h:143
static fr_slen_t parent
Definition pair.h:860
#define fr_strerror_printf(_fmt,...)
Log to thread local error buffer.
Definition strerror.h:64
#define fr_strerror_printf_push(_fmt,...)
Add a message to an existing stack of messages at the tail.
Definition strerror.h:84
#define fr_strerror_const(_msg)
Definition strerror.h:223
@ FR_TYPE_ATTR
A contains an attribute reference.
Definition types.h:83
#define fr_type_is_leaf(_x)
Definition types.h:393
static char const * fr_type_to_str(fr_type_t type)
Return a static string containing the type name.
Definition types.h:454
fr_slen_t fr_value_box_from_str(TALLOC_CTX *ctx, fr_value_box_t *dst, fr_type_t dst_type, fr_dict_attr_t const *dst_enumv, char const *in, size_t inlen, fr_sbuff_unescape_rules_t const *erules)
Definition value.c:6141
void fr_value_box_clear(fr_value_box_t *data)
Clear/free any existing value and metadata.
Definition value.c:4404
#define fr_box_strvalue_buffer(_val)
Definition value.h:337
#define FR_VALUE_BOX_INITIALISER_NULL(_vb)
A static initialiser for stack/globally allocated boxes.
Definition value.h:536
static fr_sbuff_err_t char size_t * len
Definition value.h:1062