/src/freeradius-server/src/lib/util/dict_unknown.c
Line | Count | Source |
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 | | /** Deal with 'unknown' attributes, creating ephemeral dictionary attributes for them |
18 | | * |
19 | | * @file src/lib/util/dict_unknown.c |
20 | | * |
21 | | * @copyright 2019 The FreeRADIUS server project |
22 | | */ |
23 | | RCSID("$Id: 98351278da1b0e109a207f44c98197c0845c19a8 $") |
24 | | |
25 | | #include <freeradius-devel/util/dict_priv.h> |
26 | | |
27 | | static int dict_attr_unknown_init(fr_dict_attr_t const *parent, UNUSED fr_dict_attr_t const *da, fr_type_t type, fr_dict_attr_flags_t *flags) |
28 | 6.04M | { |
29 | 6.04M | flags->is_unknown = true; |
30 | | |
31 | 6.04M | if (parent->flags.internal) { |
32 | 0 | fr_strerror_printf("Cannot create 'raw' attribute of data type '%s' which is 'internal'", |
33 | 0 | fr_type_to_str(type)); |
34 | 0 | return -1; |
35 | 0 | } |
36 | | |
37 | 6.04M | if ((parent->type == FR_TYPE_UNION) && (type != FR_TYPE_OCTETS)) { |
38 | 0 | fr_strerror_printf("Cannot create 'raw' attribute of data type '%s' which has parent data type 'union'", |
39 | 0 | fr_type_to_str(type)); |
40 | 0 | return -1; |
41 | 0 | } |
42 | | |
43 | 6.04M | if (parent->depth >= FR_DICT_MAX_TLV_STACK) { |
44 | 39 | fr_strerror_const("Attribute depth is too large"); |
45 | 39 | return -1; |
46 | 39 | } |
47 | | |
48 | | /* |
49 | | * If we are leveraging an existing attribute, then do some additional checks. |
50 | | */ |
51 | 6.04M | if (da) { |
52 | 2.45M | if (da->flags.internal) { |
53 | 0 | fr_strerror_printf("Cannot create unknown attribute from internal attribute %s", da->name); |
54 | 0 | return -1; |
55 | 0 | } |
56 | | |
57 | | /* |
58 | | * @todo - do we actually care about this? |
59 | | * |
60 | | * If we fix the unknown allocations to always use the raw number as the name, then it |
61 | | * should be fine to change the data types. |
62 | | */ |
63 | 2.45M | if (type != FR_TYPE_OCTETS) { |
64 | 37.5k | if (da->type != type) { |
65 | 0 | fr_strerror_printf("Cannot allocate unknown attribute (%s) which changes data type from '%s' to '%s'", |
66 | 0 | da->name, |
67 | 0 | fr_type_to_str(da->type), |
68 | 0 | fr_type_to_str(type)); |
69 | 0 | return -1; |
70 | 0 | } |
71 | 37.5k | } |
72 | 2.45M | } |
73 | | |
74 | | /* |
75 | | * Ensure that raw members of a structure have the correct length. |
76 | | */ |
77 | 6.04M | if (parent->type == FR_TYPE_STRUCT) { |
78 | 22.9k | if (!da) { |
79 | 4 | fr_strerror_printf("Cannot create 'raw' attribute of data type '%s' which has parent data type 'struct'", |
80 | 4 | fr_type_to_str(type)); |
81 | 4 | return -1; |
82 | 4 | } |
83 | | |
84 | 22.9k | if (fr_type_is_leaf(da->type)) { |
85 | 19.0k | if (fr_type_is_structural(type)) goto cannot_change_type; |
86 | | |
87 | 19.0k | fr_assert(da->flags.is_known_width); |
88 | | |
89 | 19.0k | flags->is_known_width = true; |
90 | 19.0k | flags->length = da->flags.length; |
91 | | |
92 | 19.0k | } else if (da->type != type) { |
93 | 3.90k | cannot_change_type: |
94 | | /* |
95 | | * @todo - why not? So long as the size is the same... |
96 | | */ |
97 | 3.90k | fr_strerror_printf("Cannot create 'raw' attribute in 'struct' which changes data type from '%s' to '%s'", |
98 | 3.90k | fr_type_to_str(da->type), fr_type_to_str(type)); |
99 | 3.90k | return -1; |
100 | 3.90k | } |
101 | 22.9k | } |
102 | | |
103 | 6.04M | return 0; |
104 | 6.04M | } |
105 | | |
106 | | /** Converts an unknown to a known by adding it to the internal dictionaries. |
107 | | * |
108 | | * Does not free old #fr_dict_attr_t, that is left up to the caller. |
109 | | * |
110 | | * @param[in] dict of protocol context we're operating in. |
111 | | * If NULL the internal dictionary will be used. |
112 | | * @param[in] unknown attribute to add. |
113 | | * @return |
114 | | * - Existing #fr_dict_attr_t if unknown was found in a dictionary. |
115 | | * - A new entry representing unknown. |
116 | | */ |
117 | | fr_dict_attr_t const *fr_dict_attr_unknown_add(fr_dict_t *dict, fr_dict_attr_t const *unknown) |
118 | 0 | { |
119 | 0 | fr_dict_attr_t const *da; |
120 | 0 | fr_dict_attr_t const *parent; |
121 | 0 | fr_dict_attr_flags_t flags; |
122 | |
|
123 | 0 | if (unlikely(dict->read_only)) { |
124 | 0 | fr_strerror_printf("%s dictionary has been marked as read only", fr_dict_root(dict)->name); |
125 | 0 | return NULL; |
126 | 0 | } |
127 | | |
128 | | #ifdef STATIC_ANALYZER |
129 | | if (!unknown->name || !unknown->parent) return NULL; |
130 | | #endif |
131 | | |
132 | 0 | da = fr_dict_attr_by_name(NULL, unknown->parent, unknown->name); |
133 | 0 | if (da) { |
134 | 0 | if (da->attr == unknown->attr) return da; |
135 | | |
136 | 0 | fr_strerror_printf("Unknown attribute '%s' conflicts with existing attribute in namespace '%s'", |
137 | 0 | da->name, unknown->parent->name); |
138 | 0 | return da; |
139 | 0 | } |
140 | | |
141 | | /* |
142 | | * Define the complete unknown hierarchy |
143 | | */ |
144 | 0 | if (unknown->parent && unknown->parent->flags.is_unknown) { |
145 | 0 | parent = fr_dict_attr_unknown_add(dict, unknown->parent); |
146 | 0 | if (!parent) { |
147 | 0 | fr_strerror_printf_push("Failed adding parent \"%s\"", unknown->parent->name); |
148 | 0 | return NULL; |
149 | 0 | } |
150 | 0 | } else { |
151 | 0 | parent = unknown->parent; |
152 | 0 | } |
153 | | |
154 | 0 | memcpy(&flags, &unknown->flags, sizeof(flags)); |
155 | 0 | flags.is_unknown = 0; |
156 | | |
157 | | /* |
158 | | * If this is a vendor, we skip most of the sanity |
159 | | * checks and add it to the vendor hash, and add it |
160 | | * as a child attribute to the Vendor-Specific |
161 | | * container. |
162 | | */ |
163 | 0 | if (unknown->type == FR_TYPE_VENDOR) { |
164 | 0 | fr_dict_attr_t *n; |
165 | |
|
166 | 0 | if (dict_vendor_add(dict, unknown->name, unknown->attr) < 0) return NULL; |
167 | | |
168 | 0 | n = dict_attr_alloc(dict->pool, parent, unknown->name, unknown->attr, unknown->type, |
169 | 0 | &(dict_attr_args_t){ .flags = &flags }); |
170 | 0 | if (unlikely(!n)) return NULL; |
171 | | |
172 | | /* |
173 | | * Setup parenting for the attribute |
174 | | */ |
175 | 0 | if (dict_attr_child_add(UNCONST(fr_dict_attr_t *, unknown->parent), n) < 0) return NULL; |
176 | | |
177 | 0 | return n; |
178 | 0 | } |
179 | | |
180 | | /* |
181 | | * Look up the attribute by number. If it doesn't exist, |
182 | | * add it both by name and by number. If it does exist, |
183 | | * add it only by name. |
184 | | */ |
185 | 0 | da = fr_dict_attr_child_by_num(parent, unknown->attr); |
186 | 0 | if (da) { |
187 | 0 | fr_dict_attr_t *n; |
188 | |
|
189 | 0 | n = dict_attr_alloc(dict->pool, parent, unknown->name, unknown->attr, unknown->type, |
190 | 0 | &(dict_attr_args_t){ .flags = &flags }); |
191 | 0 | if (!n) return NULL; |
192 | | |
193 | | /* |
194 | | * Add the unknown by NAME. e.g. if the admin does "Attr-26", we want |
195 | | * to return "Attr-26", and NOT "Vendor-Specific". The rest of the server |
196 | | * is responsible for converting "Attr-26 = 0x..." to an actual attribute, |
197 | | * if it so desires. |
198 | | */ |
199 | 0 | if (dict_attr_add_to_namespace(parent, n) < 0) { |
200 | 0 | talloc_free(n); |
201 | 0 | return NULL; |
202 | 0 | } |
203 | | |
204 | 0 | return n; |
205 | 0 | } |
206 | | |
207 | | /* |
208 | | * Add the attribute by both name and number. |
209 | | * |
210 | | * Fixme - Copy extensions? |
211 | | */ |
212 | 0 | if (fr_dict_attr_add(dict, parent, unknown->name, unknown->attr, unknown->type, &flags) < 0) return NULL; |
213 | | |
214 | | /* |
215 | | * For paranoia, return it by name. |
216 | | */ |
217 | 0 | return fr_dict_attr_by_name(NULL, parent, unknown->name); |
218 | 0 | } |
219 | | |
220 | | /** Free dynamically allocated (unknown attributes) |
221 | | * |
222 | | * If the da was dynamically allocated it will be freed, else the function |
223 | | * will return without doing anything. |
224 | | * |
225 | | * @param[in] da to free. |
226 | | */ |
227 | | void fr_dict_attr_unknown_free(fr_dict_attr_t const **da) |
228 | 157k | { |
229 | 157k | if (!da || !*da) return; |
230 | | |
231 | | /* Don't free real DAs */ |
232 | 157k | if (!(*da)->flags.is_unknown) { |
233 | 33.2k | return; |
234 | 33.2k | } |
235 | | |
236 | 123k | talloc_const_free(*da); |
237 | | |
238 | 123k | *da = NULL; |
239 | 123k | } |
240 | | |
241 | | /** Allocate an unknown DA. |
242 | | * |
243 | | */ |
244 | | fr_dict_attr_t *fr_dict_attr_unknown_alloc(TALLOC_CTX *ctx, fr_dict_attr_t const *da, fr_type_t type) |
245 | 2.45M | { |
246 | 2.45M | fr_dict_attr_t *n; |
247 | 2.45M | fr_dict_attr_t const *parent; |
248 | 2.45M | fr_dict_attr_flags_t flags = {}; |
249 | | |
250 | 2.45M | fr_assert(!da->flags.is_root); /* cannot copy root attributes */ |
251 | 2.45M | fr_assert(da->parent); |
252 | | |
253 | 2.45M | switch (type) { |
254 | 36.1M | case FR_TYPE_LEAF: |
255 | 36.1M | case FR_TYPE_GROUP: |
256 | 2.43M | case FR_TYPE_TLV: |
257 | 2.43M | case FR_TYPE_VSA: |
258 | 2.45M | case FR_TYPE_VENDOR: |
259 | 2.45M | break; |
260 | | |
261 | 0 | default: |
262 | 0 | fr_strerror_printf("Invalid data type '%s' for unknown attribute", fr_type_to_str(type)); |
263 | 0 | return NULL; |
264 | 2.45M | } |
265 | | |
266 | 2.45M | switch (da->type) { |
267 | 35.4M | case FR_TYPE_LEAF: |
268 | 35.4M | case FR_TYPE_STRUCTURAL: |
269 | 2.45M | break; |
270 | | |
271 | 0 | default: |
272 | 0 | fr_strerror_printf("Cannot create unknown attribute from data type '%s'", fr_type_to_str(da->type)); |
273 | 0 | return NULL; |
274 | 2.45M | } |
275 | | |
276 | 2.45M | if (dict_attr_unknown_init(da->parent, da, type, &flags)) return NULL; |
277 | | |
278 | | /* |
279 | | * Set the unknown flags. Note that we don't copy any other flags, as they are all likely to be wrong. |
280 | | */ |
281 | 2.44M | flags.is_raw = 1; |
282 | | |
283 | | /* |
284 | | * Allocate an attribute. |
285 | | */ |
286 | 2.44M | n = dict_attr_alloc_null(ctx, da->dict->proto); |
287 | 2.44M | if (!n) return NULL; |
288 | | |
289 | | /* |
290 | | * We want to have parent / child relationships, AND to |
291 | | * copy all unknown parents, AND to free the unknown |
292 | | * parents when this 'da' is freed. We therefore talloc |
293 | | * the parent from the 'da'. |
294 | | */ |
295 | 2.44M | if (da->parent->flags.is_unknown) { |
296 | 30.4k | parent = fr_dict_attr_unknown_copy(n, da->parent); |
297 | 30.4k | if (!parent) { |
298 | 0 | error: |
299 | 0 | talloc_free(n); |
300 | 0 | return NULL; |
301 | 0 | } |
302 | | |
303 | 2.41M | } else { |
304 | 2.41M | parent = da->parent; |
305 | 2.41M | } |
306 | | |
307 | | /* |
308 | | * Initialize the rest of the fields. |
309 | | */ |
310 | 2.44M | if (dict_attr_init(&n, parent, da->name, da->attr, type, &(dict_attr_args_t){ .flags = &flags }) < 0) { |
311 | 0 | goto error; |
312 | 0 | } |
313 | | |
314 | | /* |
315 | | * Copy protocol-specific extents, and hope to heck that the protocol encoder knows what it's doing. |
316 | | */ |
317 | 2.44M | if (fr_dict_attr_ext(da, FR_DICT_ATTR_EXT_PROTOCOL_SPECIFIC) && |
318 | 298k | !dict_attr_ext_copy(&n, da, FR_DICT_ATTR_EXT_PROTOCOL_SPECIFIC)) { |
319 | 0 | goto error; |
320 | 0 | } |
321 | | |
322 | | /* |
323 | | * Do NOT copy extents. The name and da_stack extents are already defined. We do NOT copy |
324 | | * existing children, references, keys, enumv, etc. If the unknown attribute is a group, it's |
325 | | * ref is already set to the root, or to a copy of the input DA. If the unknown attribute is a |
326 | | * TLV, then it cannot have known children. If an unknown attribute is a leaf, then it cannot |
327 | | * have known enums. |
328 | | */ |
329 | | |
330 | 2.44M | DA_VERIFY(n); |
331 | | |
332 | 2.44M | return n; |
333 | 2.44M | } |
334 | | |
335 | | /** Copy a known or unknown attribute to produce an unknown attribute with the specified name |
336 | | * |
337 | | * Will copy the complete hierarchy down to the first known attribute. |
338 | | */ |
339 | | fr_dict_attr_t *fr_dict_attr_unknown_afrom_da(TALLOC_CTX *ctx, fr_dict_attr_t const *da) |
340 | 2.33M | { |
341 | 2.33M | fr_type_t type = da->type; |
342 | | |
343 | | /* |
344 | | * VENDOR, etc. are logical containers, and can have |
345 | | * unknown children, so they're left alone. All other |
346 | | * base types are mangled to OCTETs. |
347 | | * |
348 | | * Note that we can't allocate an unknown STRUCT. If the |
349 | | * structure is malformed, then it's just a sequence of |
350 | | * OCTETS. Similarly, if a GROUP is malformed, then we |
351 | | * have no idea what's inside of it, and we make it OCTETS. |
352 | | */ |
353 | 2.33M | switch (type) { |
354 | 15.8k | case FR_TYPE_VENDOR: |
355 | 15.8k | fr_assert(da->flags.type_size != 0); |
356 | 15.8k | break; |
357 | | |
358 | 21.6k | case FR_TYPE_TLV: |
359 | 21.6k | case FR_TYPE_VSA: |
360 | 21.6k | break; |
361 | | |
362 | 2.30M | default: |
363 | 2.30M | type = FR_TYPE_OCTETS; |
364 | 2.30M | break; |
365 | 2.33M | } |
366 | | |
367 | 2.33M | return fr_dict_attr_unknown_alloc(ctx, da, type); |
368 | 2.33M | } |
369 | | |
370 | | /** Initialise a fr_dict_attr_t from a number and a data type |
371 | | * |
372 | | * @param[in] ctx to allocate the attribute in. |
373 | | * @param[in] parent of the unknown attribute (may also be unknown). |
374 | | * @param[in] num of the unknown attribute. |
375 | | * @param[in] type data type |
376 | | * @param[in] raw is it raw, i.e. _bad_ value, versus unknown? |
377 | | * @return |
378 | | * - An fr_dict_attr_t on success. |
379 | | * - NULL on failure. |
380 | | */ |
381 | | fr_dict_attr_t *fr_dict_attr_unknown_typed_afrom_num_raw(TALLOC_CTX *ctx, fr_dict_attr_t const *parent, unsigned int num, fr_type_t type, bool raw) |
382 | 3.59M | { |
383 | 3.59M | fr_dict_attr_flags_t flags = { |
384 | 3.59M | .is_raw = raw, |
385 | 3.59M | }; |
386 | 3.59M | fr_dict_attr_t const *da = NULL; |
387 | | |
388 | 3.59M | if (parent->flags.internal) { |
389 | 33 | fr_strerror_printf("Cannot create 'raw' attribute from internal parent '%s' of data type '%s'", |
390 | 33 | parent->name, fr_type_to_str(parent->type)); |
391 | 33 | return NULL; |
392 | 33 | } |
393 | | |
394 | 3.59M | if (((parent->type == FR_TYPE_TLV) || (parent->type == FR_TYPE_VENDOR))) { |
395 | 3.58M | if ((uint64_t) num >= ((uint64_t) 1 << (8 * parent->flags.type_size))) { |
396 | 89 | fr_strerror_printf("Invalid attribute number '%u' - it must be no more than %u bits in size", |
397 | 89 | num, 8 * parent->flags.type_size); |
398 | 89 | return NULL; |
399 | 89 | } |
400 | 3.58M | } |
401 | | |
402 | 3.59M | switch (type) { |
403 | 0 | default: |
404 | 0 | fr_strerror_printf("Cannot allocate unknown attribute '%u' - invalid data type '%s'", |
405 | 0 | num, fr_type_to_str(type)); |
406 | 0 | return NULL; |
407 | | |
408 | 6.84k | case FR_TYPE_VENDOR: |
409 | 6.84k | if (parent->type != FR_TYPE_VSA) goto fail; |
410 | | |
411 | 6.84k | if (parent->flags.is_unknown) goto fail; |
412 | 6.84k | break; |
413 | | |
414 | 53.6M | case FR_TYPE_LEAF: |
415 | 53.6M | case FR_TYPE_TLV: |
416 | 3.58M | case FR_TYPE_VSA: |
417 | 3.58M | if (!fr_type_is_structural_except_vsa(parent->type)) { |
418 | 0 | fail: |
419 | 0 | fr_strerror_printf("Cannot allocate unknown attribute '%u' data type '%s' with parent %s data type '%s'", |
420 | 0 | num, fr_type_to_str(type), |
421 | 0 | parent->name, fr_type_to_str(parent->type)); |
422 | 0 | return NULL; |
423 | 0 | } |
424 | | |
425 | | /* |
426 | | * We can convert anything to 'octets'. |
427 | | */ |
428 | 3.58M | if (type == FR_TYPE_OCTETS) break; |
429 | | |
430 | | /* |
431 | | * But we shouldn't be able to create a raw attribute which is a _different_ type than an |
432 | | * existing one. |
433 | | */ |
434 | 22.6k | da = fr_dict_attr_child_by_num(parent, num); |
435 | 22.6k | break; |
436 | 3.59M | } |
437 | | |
438 | 3.59M | if (dict_attr_unknown_init(parent, da, type, &flags)) return NULL; |
439 | | |
440 | 3.59M | return dict_attr_alloc(ctx, parent, NULL, num, type, |
441 | 3.59M | &(dict_attr_args_t){ .flags = &flags }); |
442 | 3.59M | } |
443 | | |
444 | | /** Create a fr_dict_attr_t from an ASCII attribute and value |
445 | | * |
446 | | * Where the attribute name is in the form: |
447 | | * - %d |
448 | | * - %d.%d.%d... |
449 | | * |
450 | | * @note If vendor != 0, an unknown vendor (may) also be created, parented by |
451 | | * the correct VSA attribute. This is accessible via vp->parent, |
452 | | * and will be use the unknown da as its talloc parent. |
453 | | * |
454 | | * @param[in] ctx to alloc new attribute in. |
455 | | * @param[out] out Where to write the head of the chain unknown |
456 | | * dictionary attributes. |
457 | | * @param[in] parent Attribute to use as the root for resolving OIDs in. |
458 | | * Usually the root of a protocol dictionary. |
459 | | * @param[in] in OID string to parse |
460 | | * @param[in] type data type of the unknown attribute |
461 | | * @return |
462 | | * - The number of bytes parsed on success. |
463 | | * - <= 0 on failure. Negative offset indicates parse error position. |
464 | | */ |
465 | | fr_slen_t fr_dict_attr_unknown_afrom_oid_substr(TALLOC_CTX *ctx, |
466 | | fr_dict_attr_t const **out, |
467 | | fr_dict_attr_t const *parent, |
468 | | fr_sbuff_t *in, fr_type_t type) |
469 | 0 | { |
470 | 0 | fr_sbuff_t our_in = FR_SBUFF(in); |
471 | 0 | fr_dict_attr_t const *our_parent = parent; |
472 | 0 | fr_dict_attr_t *n = NULL; |
473 | 0 | fr_dict_attr_flags_t flags = { .is_raw = true, }; |
474 | |
|
475 | 0 | *out = NULL; |
476 | | |
477 | | /* |
478 | | * Allocate the final attribute first, so that any |
479 | | * unknown parents can be freed when this da is freed. |
480 | | * |
481 | | * See fr_dict_attr_unknown_afrom_da() for more details. |
482 | | * |
483 | | * Note also that we copy the input name, even if it is |
484 | | * not normalized. |
485 | | * |
486 | | * While the name of this attribute is "Attr-#.#.#", one |
487 | | * or more of the leading components may, in fact, be |
488 | | * known. |
489 | | */ |
490 | 0 | n = dict_attr_alloc_null(ctx, parent->dict->proto); |
491 | | |
492 | | /* |
493 | | * Loop until there's no more component separators. |
494 | | */ |
495 | 0 | for (;;) { |
496 | 0 | uint32_t num; |
497 | 0 | fr_sbuff_parse_error_t sberr; |
498 | |
|
499 | 0 | fr_sbuff_out(&sberr, &num, &our_in); |
500 | 0 | switch (sberr) { |
501 | 0 | case FR_SBUFF_PARSE_OK: |
502 | 0 | switch (our_parent->type) { |
503 | | /* |
504 | | * If the parent is a VSA, this component |
505 | | * must specify a vendor. |
506 | | */ |
507 | 0 | case FR_TYPE_VSA: |
508 | 0 | { |
509 | 0 | fr_dict_attr_t *ni; |
510 | |
|
511 | 0 | if (fr_sbuff_next_if_char(&our_in, '.')) { |
512 | 0 | ni = fr_dict_attr_unknown_vendor_afrom_num(n, our_parent, num); |
513 | 0 | if (!ni) { |
514 | 0 | error: |
515 | 0 | talloc_free(n); |
516 | 0 | FR_SBUFF_ERROR_RETURN(&our_in); |
517 | 0 | } |
518 | 0 | our_parent = ni; |
519 | 0 | continue; |
520 | 0 | } |
521 | 0 | if (dict_attr_init(&n, our_parent, NULL, num, FR_TYPE_VENDOR, |
522 | 0 | &(dict_attr_args_t){ .flags = &flags }) < 0) goto error; |
523 | 0 | } |
524 | 0 | break; |
525 | | |
526 | | /* |
527 | | * If it's structural, this component must |
528 | | * specify a TLV. |
529 | | */ |
530 | 0 | case FR_TYPE_STRUCTURAL_EXCEPT_VSA: |
531 | 0 | { |
532 | 0 | fr_dict_attr_t *ni; |
533 | |
|
534 | 0 | if (fr_sbuff_next_if_char(&our_in, '.')) { |
535 | 0 | ni = fr_dict_attr_unknown_typed_afrom_num(n, our_parent, num, FR_TYPE_TLV); |
536 | 0 | if (!ni) goto error; |
537 | 0 | our_parent = ni; |
538 | 0 | continue; |
539 | 0 | } |
540 | 0 | } |
541 | 0 | FALL_THROUGH; |
542 | |
|
543 | 0 | default: |
544 | | /* |
545 | | * Leaf type with more components |
546 | | * is an error. |
547 | | */ |
548 | 0 | if (fr_sbuff_is_char(&our_in, '.')) { |
549 | 0 | fr_strerror_printf("Interior OID component cannot proceed a %s type", |
550 | 0 | fr_type_to_str(our_parent->type)); |
551 | 0 | goto error; |
552 | 0 | } |
553 | | |
554 | 0 | if (dict_attr_unknown_init(our_parent, NULL, type, &flags)) goto error; |
555 | | |
556 | 0 | if (dict_attr_init(&n, our_parent, NULL, num, type, |
557 | 0 | &(dict_attr_args_t){ .flags = &flags }) < 0) goto error; |
558 | 0 | break; |
559 | 0 | } |
560 | 0 | break; |
561 | | |
562 | 0 | default: |
563 | 0 | { |
564 | 0 | fr_sbuff_marker_t c_start; |
565 | |
|
566 | 0 | fr_sbuff_marker(&c_start, &our_in); |
567 | 0 | fr_sbuff_adv_past_allowed(&our_in, FR_DICT_ATTR_MAX_NAME_LEN, fr_dict_attr_allowed_chars, NULL); |
568 | 0 | fr_strerror_printf("Unknown attribute \"%.*s\" for parent \"%s\"", |
569 | 0 | (int)fr_sbuff_behind(&c_start), fr_sbuff_current(&c_start), our_parent->name); |
570 | 0 | goto error; |
571 | 0 | } |
572 | 0 | } |
573 | 0 | break; |
574 | 0 | } |
575 | | |
576 | 0 | DA_VERIFY(n); |
577 | |
|
578 | 0 | *out = n; |
579 | |
|
580 | 0 | FR_SBUFF_SET_RETURN(in, &our_in); |
581 | 0 | } |
582 | | |
583 | | /** Fixup the parent of an unknown attribute using an equivalent known attribute |
584 | | * |
585 | | * This can be useful where an unknown attribute's ancestors are added to |
586 | | * a dictionary but not the unknown attribute itself. |
587 | | * |
588 | | * @param[in] da to fixup. |
589 | | * @param[in] parent to assign. If NULL, we will attempt to resolve |
590 | | * the parent in the dictionary the current unknown |
591 | | * attribute extends. |
592 | | * @return |
593 | | * - 0 on success. |
594 | | * - -1 on failure. |
595 | | */ |
596 | | int fr_dict_attr_unknown_parent_to_known(fr_dict_attr_t *da, fr_dict_attr_t const *parent) |
597 | 0 | { |
598 | 0 | fr_dict_attr_t const *da_u, *da_k; |
599 | |
|
600 | 0 | if (parent) { |
601 | | /* |
602 | | * Walk back up the hierarchy until we get to a known |
603 | | * ancestor on the unknown side. |
604 | | */ |
605 | 0 | for (da_u = da->parent, da_k = parent; |
606 | 0 | da_k && da_u && da_u->flags.is_unknown; |
607 | 0 | da_u = da_u->parent, da_k = da_k->parent) { |
608 | 0 | if (unlikely(da_u->attr != da_k->attr)) { |
609 | 0 | fr_strerror_printf("Unknown parent number %u does not match " |
610 | 0 | "known parent number %u (%s)", |
611 | 0 | da_u->attr, da_k->attr, da_k->name); |
612 | 0 | return -1; |
613 | 0 | } |
614 | | |
615 | 0 | if (unlikely(da_u->depth != da_k->depth)) { |
616 | 0 | fr_strerror_printf("Unknown parent depth %u does not match " |
617 | 0 | "known parent depth %u (%s)", |
618 | 0 | da_u->depth, da_k->depth, da_k->name); |
619 | 0 | return -1; |
620 | 0 | } |
621 | 0 | } |
622 | 0 | if ((da_k == NULL) != (da_u == NULL)) { |
623 | 0 | fr_strerror_printf("Truncated or over-extended hierarchy " |
624 | 0 | "for unknown attribute %u", da->attr); |
625 | 0 | return -1; |
626 | 0 | } |
627 | 0 | } else { |
628 | 0 | parent = fr_dict_attr_unknown_resolve(fr_dict_by_da(da), da->parent); |
629 | 0 | if (!parent) { |
630 | 0 | fr_strerror_printf("Failed resolving unknown attribute %u " |
631 | 0 | "in dictionary", da->attr); |
632 | 0 | return -1; |
633 | 0 | } |
634 | 0 | } |
635 | | |
636 | 0 | da->parent = fr_dict_attr_unconst(parent); |
637 | |
|
638 | 0 | return 0; |
639 | 0 | } |
640 | | |
641 | | /** Check to see if we can convert a nested TLV structure to known attributes |
642 | | * |
643 | | * @param[in] dict to search in. |
644 | | * @param[in] da Nested tlv structure to convert. |
645 | | * @return |
646 | | * - NULL if we can't. |
647 | | * - Known attribute if we can. |
648 | | */ |
649 | | fr_dict_attr_t const *fr_dict_attr_unknown_resolve(fr_dict_t const *dict, fr_dict_attr_t const *da) |
650 | 0 | { |
651 | 0 | INTERNAL_IF_NULL(dict, NULL); |
652 | | |
653 | 0 | if (!da->flags.is_unknown) return da; /* It's known */ |
654 | | |
655 | 0 | if (da->parent) { |
656 | 0 | fr_dict_attr_t const *parent; |
657 | |
|
658 | 0 | parent = fr_dict_attr_unknown_resolve(dict, da->parent); |
659 | 0 | if (!parent) return NULL; |
660 | | |
661 | 0 | return fr_dict_attr_child_by_num(parent, da->attr); |
662 | 0 | } |
663 | | |
664 | 0 | if (dict->root == da) return dict->root; |
665 | 0 | return NULL; |
666 | 0 | } |