The FreeRADIUS server $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
Loading...
Searching...
No Matches
base.c
Go to the documentation of this file.
1/*
2 * This library is free software; you can redistribute it and/or
3 * modify it under the terms of the GNU Lesser General Public
4 * License as published by the Free Software Foundation; either
5 * version 2.1 of the License, or (at your option) any later version.
6 *
7 * This library 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 GNU
10 * Lesser General Public License for more details.
11 *
12 * You should have received a copy of the GNU Lesser General Public
13 * License along with this library; if not, write to the Free Software
14 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
15 */
16
17/**
18 * $Id: 5383371534c07200273a92d3612632f1f6e04e75 $
19 *
20 * @file protocols/der/decode.c
21 * @brief Functions to decode DER encoded data.
22 *
23 * @author Ethan Thompson (ethan.thompson@inkbridge.io)
24 *
25 * @copyright (C) 2025 Network RADIUS SAS (legal@networkradius.com)
26 */
27RCSID("$Id: 5383371534c07200273a92d3612632f1f6e04e75 $")
28
29#include <freeradius-devel/util/net.h>
30#include <freeradius-devel/util/proto.h>
31#include <freeradius-devel/util/table.h>
32#include <freeradius-devel/util/dict_ext_priv.h>
33
34#include "attrs.h"
35#include "der.h"
36
38
41
48
54
56 { L("bitstring"), FR_DER_TAG_BITSTRING },
57 { L("bmpstring"), FR_DER_TAG_BMP_STRING },
58 { L("boolean"), FR_DER_TAG_BOOLEAN },
59 { L("choice"), FR_DER_TAG_CHOICE },
60 { L("enumerated"), FR_DER_TAG_ENUMERATED },
61 { L("generalizedtime"), FR_DER_TAG_GENERALIZED_TIME },
62 { L("generalstring"), FR_DER_TAG_GENERAL_STRING },
63 { L("ia5string"), FR_DER_TAG_IA5_STRING },
64 { L("integer"), FR_DER_TAG_INTEGER },
65 { L("null"), FR_DER_TAG_NULL },
66 { L("octetstring"), FR_DER_TAG_OCTETSTRING },
67 { L("oid"), FR_DER_TAG_OID },
68 { L("printablestring"), FR_DER_TAG_PRINTABLE_STRING },
69 { L("sequence"), FR_DER_TAG_SEQUENCE },
70 { L("set"), FR_DER_TAG_SET },
71 { L("t61string"), FR_DER_TAG_T61_STRING },
72 { L("universalstring"), FR_DER_TAG_UNIVERSAL_STRING },
73 { L("utctime"), FR_DER_TAG_UTC_TIME },
74 { L("utf8string"), FR_DER_TAG_UTF8_STRING },
75 { L("visiblestring"), FR_DER_TAG_VISIBLE_STRING },
76};
78
79
81{
82 return fr_table_str_by_value(tag_name_to_number, tag, "???");
83}
84
85#define ALL_STRINGS ((1 << FR_DER_TAG_BMP_STRING) | (1 << FR_DER_TAG_GENERAL_STRING) | \
86 (1 << FR_DER_TAG_IA5_STRING) | (1 << FR_DER_TAG_PRINTABLE_STRING) | \
87 (1 << FR_DER_TAG_T61_STRING) | (1 << FR_DER_TAG_UTF8_STRING) | \
88 (1 << FR_DER_TAG_VISIBLE_STRING))
89
102
104{
105 return (der_tags_compatible[tag1] & (1 << (uint64_t) tag2)) != 0;
106}
107
108/*
109 * Create a mapping between FR_TYPE_* and valid FR_DER_TAG_*'s
110 */
111static const bool *fr_type_to_der_tags[FR_DER_TAG_MAX] = {
112 [FR_TYPE_IPV4_ADDR] = (bool [FR_DER_TAG_MAX]) {
113 [FR_DER_TAG_BITSTRING] = true,
114 },
115
117 [FR_DER_TAG_BITSTRING] = true,
118 },
119
120 [FR_TYPE_IPV6_ADDR] = (bool [FR_DER_TAG_MAX]) {
121 [FR_DER_TAG_BITSTRING] = true,
122 },
123
125 [FR_DER_TAG_BITSTRING] = true,
126 },
127
129 [FR_DER_TAG_OCTETSTRING] = true,
130 },
131
132 [FR_TYPE_BOOL] = (bool [FR_DER_TAG_MAX]) {
133 [FR_DER_TAG_BOOLEAN] = true,
134 [FR_DER_TAG_INTEGER] = true,
135 [FR_DER_TAG_NULL] = true,
136 },
137 [FR_TYPE_INT64] = (bool [FR_DER_TAG_MAX]) {
138 [FR_DER_TAG_INTEGER] = true,
139 [FR_DER_TAG_ENUMERATED] = true,
140 },
141 [FR_TYPE_OCTETS] = (bool [FR_DER_TAG_MAX]) {
142 [FR_DER_TAG_BITSTRING] = true,
143 [FR_DER_TAG_OCTETSTRING] = true,
144 },
145 [FR_TYPE_STRING] = (bool [FR_DER_TAG_MAX]) {
146 [FR_DER_TAG_UTF8_STRING] = true,
148 [FR_DER_TAG_T61_STRING] = true,
149 [FR_DER_TAG_IA5_STRING] = true,
153 },
154 [FR_TYPE_DATE] = (bool [FR_DER_TAG_MAX]) {
155 [FR_DER_TAG_UTC_TIME] = true,
157 },
158 [FR_TYPE_ATTR] = (bool [FR_DER_TAG_MAX]) {
159 [FR_DER_TAG_OID] = true,
160 },
161 [FR_TYPE_TLV] = (bool [FR_DER_TAG_MAX]) {
162 [FR_DER_TAG_SEQUENCE] = true,
163 [FR_DER_TAG_SET] = true,
164 },
165 [FR_TYPE_STRUCT] = (bool [FR_DER_TAG_MAX]) {
166 [FR_DER_TAG_BITSTRING] = true,
167 },
168 [FR_TYPE_GROUP] = (bool [FR_DER_TAG_MAX]) {
169 [FR_DER_TAG_SEQUENCE] = true,
170 },
171};
172
173/*
174 * Return true if the given type can be encoded as the given tag.
175 * @param[in] type The fr_type to check.
176 * @param[in] tag The der tag to check.
177 * @return true if the type can be encoded as the given tag.
178 */
180{
181 if (!fr_type_to_der_tags[type]) return false;
182
183 return fr_type_to_der_tags[type][tag];
184}
185
186
188{
189 fr_der_attr_flags_t const *flags;
190
191 if (da->dict != dict_der) return NULL;
192
193 flags = fr_der_attr_flags(da);
194 if (!flags || !flags->has_shortname) return NULL;
195
196 return flags->shortname;
197}
198
200{
201 if (instance_count > 0) {
203 return 0;
204 }
205
207
209 fail:
211 return -1;
212 }
213
216 goto fail;
217 }
218
219 return 0;
220}
221
223{
224 if (--instance_count != 0) return;
225
227}
228
229/*
230 * Allow setting class of APPLICATION and PRIVATE.
231 */
232static int dict_flag_class(fr_dict_attr_t **da_p, char const *value, UNUSED fr_dict_flag_parser_rule_t const *rules)
233{
234 static const fr_table_num_sorted_t table[] = {
235 { L("application"), FR_DER_CLASS_APPLICATION },
236 { L("private"), FR_DER_CLASS_PRIVATE },
237 };
238 static size_t table_len = NUM_ELEMENTS(table);
239
240 fr_der_attr_flags_t *flags;
241 fr_der_tag_class_t tag_class;
242
244 if (flags->der_type != FR_DER_TAG_SEQUENCE) {
245 fr_strerror_printf("Cannot use 'class' for attribute %s DER type %s - the parent must be 'sequence'",
246 (*da_p)->parent->name, fr_der_tag_to_str(flags->der_type));
247 return -1;
248 }
249
250 if ((*da_p)->attr >= FR_DER_TAG_VALUE_MAX) {
251 fr_strerror_printf("Cannot use 'class' for attribute %s - the attribute number must be 0..30",
252 (*da_p)->parent->name);
253 return -1;
254 }
255
257 if (flags->class) {
258 fr_strerror_printf("Attribute %s already has a 'class' defined", (*da_p)->name);
259 return -1;
260 }
261
263 if (tag_class == FR_DER_CLASS_INVALID) {
264 fr_strerror_printf("Unknown or invalid name in 'class=%s'", value);
265 return -1;
266 }
267
268 flags->class = tag_class;
269
270 return 0;
271}
272
274{
276
277 if (!fr_type_is_leaf((*da_p)->type)) {
278 fr_strerror_printf("Cannot set 'default=...' for attribute %s DER type %s",
279 (*da_p)->name, fr_der_tag_to_str(flags->der_type));
280 return -1;
281 }
282
283 if (flags->has_shortname) {
284 fr_strerror_const("Cannot set 'default=...' when there is already a 'shortname=...'");
285 return -1;
286 }
287
288 /*
289 * The default values are parented from the dict root. That way we don't need to copy the values
290 * when we clone the attribute, we can just copy the pointer.
291 */
292 flags->default_value = fr_value_box_alloc(fr_dict_unconst((*da_p)->dict), (*da_p)->type, NULL);
293 if (!flags->default_value) return -1;
294
295 if (fr_value_box_from_str(flags->default_value, flags->default_value, (*da_p)->type, NULL,
296 value, strlen(value), NULL) < 0) {
297 fr_strerror_printf("Failed parsing 'value=...' - %s", fr_strerror());
298 return -1;
299 }
300
301 flags->has_default_value = true;
302
303 return 0;
304}
305
306static int dict_flag_der_type(fr_dict_attr_t **da_p, char const *value, UNUSED fr_dict_flag_parser_rule_t const *rules)
307{
309 fr_der_tag_t der_type;
310
312 if (der_type == FR_DER_TAG_INVALID) {
313 fr_strerror_printf("Unknown type in 'der_type=%s'", value);
314 return -1;
315 }
316
317 /*
318 * The DER type and FreeRADIUS type must be compatible.
319 *
320 * Except for some der_type=integer, such as a
321 * certificate serialNumber. Those are too large for us
322 * to represent in 64 bits, so we just treat them as
323 * 'octets'.
324 */
325 if (!fr_type_to_der_tag_valid((*da_p)->type, der_type) &&
326 (der_type != FR_DER_TAG_INTEGER) && ((*da_p)->type != FR_TYPE_OCTETS)) {
327 fr_strerror_printf("Attribute type %s is not compatible with 'der_type=%s'",
328 fr_type_to_str((*da_p)->type), value);
329 return -1;
330 }
331
332 flags->der_type = der_type;
333
334 return 0;
335}
336
338{
339 flags->is_oid_and_value = true;
340 flags->is_sequence_of = true;
341 flags->sequence_of = FR_DER_TAG_SEQUENCE;
342
343 /*
344 * The dict autoload things aren't set until after we load all of the dictionary entries. So we
345 * just manually set it here for laziness.
346 */
347 if (!attr_oid_tree) {
348 attr_oid_tree = fr_dict_attr_by_name(NULL, fr_dict_root((*da_p)->dict), "OID-Tree");
349 if (!attr_oid_tree) return -1;
350 }
351
352 if (fr_dict_attr_set_group(da_p, attr_oid_tree) < 0) return -1;
353
354 (*da_p)->flags.allow_flat = true;
355 return 0;
356}
357
359{
362
363 if (flags->is_set_of) {
364 fr_strerror_const("Cannot be both 'sequence_of=...' and 'set_of=...'");
365 return -1;
366 }
367
368 if (flags->der_type != FR_DER_TAG_SEQUENCE) {
369 fr_strerror_printf("Cannot use 'sequence_of=...' for DER type '%s'", fr_der_tag_to_str(flags->der_type));
370 return -1;
371 }
372
373 if (strcmp(value, "oid_and_value") == 0) {
374 return dict_flag_set_oid_and_value(da_p, flags);
375 }
376
378 if (type == FR_DER_TAG_INVALID) {
379 fr_strerror_printf("Unknown type in 'sequence_of=%s'", value);
380 return -1;
381 }
382
383 flags->sequence_of = type;
384 flags->is_sequence_of = true;
385
386 return 0;
387}
388
389static int dict_flag_set_of(fr_dict_attr_t **da_p, char const *value, UNUSED fr_dict_flag_parser_rule_t const *rules)
390{
393
394 if (flags->is_sequence_of) {
395 fr_strerror_const("Cannot be both 'sequence_of=...' and 'set_of=...'");
396 return -1;
397 }
398
399 if (flags->der_type != FR_DER_TAG_SET) {
400 fr_strerror_printf("Cannot use 'set_of=...' for DER type '%s'", fr_der_tag_to_str(flags->der_type));
401 return -1;
402 }
403
404 if (strcmp(value, "oid_and_value") == 0) {
405 return dict_flag_set_oid_and_value(da_p, flags);
406 }
407
409 if (type == FR_DER_TAG_INVALID) {
410 fr_strerror_printf("Unknown type in 'set_of=%s'", value);
411 return -1;
412 }
413
414 /*
415 * The "choice" can only be used for sequence.
416 */
417 if (type == FR_DER_TAG_CHOICE) {
418 fr_strerror_printf("Invalid type in 'set_of=%s' - 'choice' can only be used for sequences", value);
419 return -1;
420 }
421
422 flags->set_of = type;
423 flags->is_set_of = true;
424
425 return 0;
426}
427
429{
431
432 flags->is_extensions = true;
433
434 return 0;
435}
436
437static int dict_flag_leaf(fr_dict_attr_t **da_p, UNUSED char const *value, UNUSED fr_dict_flag_parser_rule_t const *rules)
438{
440
441 /*
442 * The "leaf" property means that when we're encoding a nested set of attributes, we encode the
443 * OIDs until we hit one which has the "leaf" property set. We then encode the OID of this
444 * attribute, along with its value.
445 */
446 if (fr_der_flag_der_type((*da_p)->parent) != FR_DER_TAG_SEQUENCE) {
447 fr_strerror_printf("Cannot set 'leaf' for parent %s of DER type %s",
448 (*da_p)->parent->name, fr_der_tag_to_str(fr_der_flag_der_type((*da_p)->parent)));
449 return -1;
450 }
451
452 flags->leaf = true;
453
454 return 0;
455}
456
457static int dict_flag_shortname(fr_dict_attr_t **da_p, char const *value, UNUSED fr_dict_flag_parser_rule_t const *rules)
458{
460
461 if (!fr_type_is_leaf((*da_p)->type)) {
462 fr_strerror_printf("Cannot set 'shortname=...' for attribute %s DER type %s",
463 (*da_p)->name, fr_der_tag_to_str(flags->der_type));
464 return -1;
465 }
466
467 if (flags->has_default_value) {
468 fr_strerror_const("Cannot set 'shortname=...' when there is already a 'default=...'");
469 return -1;
470 }
471
472 /*
473 * The shortnames are parented from the dict root. That way we don't need to copy the values
474 * when we clone the attribute, we can just copy the pointer.
475 */
476 flags->shortname = talloc_strdup(fr_dict_unconst((*da_p)->dict), value);
477 if (!flags->shortname) return -1;
478
479 flags->has_shortname = true;
480
481 return 0;
482}
483
484/*
485 * size=MIN..MAX
486 */
487static int dict_flag_size(fr_dict_attr_t **da_p, char const *value, UNUSED fr_dict_flag_parser_rule_t const *rules)
488{
490 unsigned long num;
491 char const *p = value;
492 char *end = NULL;
493
494 if (fr_type_is_leaf((*da_p)->type) && !fr_type_is_variable_size((*da_p)->type)) {
495 fr_strerror_printf("Cannot use 'size=...' for type '%s'", fr_type_to_str((*da_p)->type));
496 return -1;
497 }
498
499 /*
500 * size=..max
501 */
502 if ((p[0] == '.') && (p[1] == '.')) goto check_max;
503
504 num = strtoul(p, &end, 10);
505 if (num == ULONG_MAX) {
506 invalid:
507 fr_strerror_printf("Invalid value in 'size=%s'", value);
508 return -1;
509 }
510
511 if (num > UINT8_MAX) {
512 fr_strerror_printf("Invalid value in 'size=%s' - 'min' value is too large", value);
513 return -1;
514 }
515
516 /*
517 * size=4
518 *
519 * Fixed size, but not size=0.
520 */
521 if (!*end) {
522 if (!num) goto invalid;
523
524 /*
525 * printablestring size=2
526 *
527 * instead of string[2] der_type=printablestring
528 */
529 if (((*da_p)->type == FR_TYPE_OCTETS) || ((*da_p)->type == FR_TYPE_STRING)) {
530 (*da_p)->flags.is_known_width = !fr_type_is_structural((*da_p)->type);
531 (*da_p)->flags.length = num;
532 return 0;
533 }
534
535 /*
536 * Sets and sequences can have a fixed number of elements.
537 */
538 flags->min = flags->max = num;
539 return 0;
540 }
541
542 if ((end[0] != '.') || (end[1] != '.')) {
543 fr_strerror_printf("Invalid value in 'size=%s' - unexpected data after 'min'", value);
544 return -1;
545 }
546
547 flags->min = num;
548
549 /*
550 * size=1..
551 *
552 * Sets the minimum, but not the maximum.
553 */
554 p = end + 2;
555 if (!*p) return 0;
556
557check_max:
558 num = strtoul(p, &end, 10);
559 if (num == ULONG_MAX) goto invalid;
560
561 if (*end) {
562 fr_strerror_printf("Invalid value in 'size=%s' - unexpected data after 'max'", value);
563 return -1;
564 }
565
566 flags->max = num;
567
568 return 0;
569}
570
571static int dict_flag_max(fr_dict_attr_t **da_p, char const *value, UNUSED fr_dict_flag_parser_rule_t const *rules)
572{
574 unsigned long num;
575 char *end = NULL;
576
577 num = strtoul(value, &end, 10);
578 if (*end || !num || (num == ULONG_MAX)) {
579 fr_strerror_printf("Invalid value in 'max=%s'", value);
580 return -1;
581 }
582
583 flags->max = num;
584
585 return 0;
586}
587
588static int dict_flag_option(fr_dict_attr_t **da_p, char const *value, UNUSED fr_dict_flag_parser_rule_t const *rules)
589{
590 fr_der_attr_flags_t *flags;
591 unsigned long num;
592 char *end = NULL;
593
594 /*
595 * Only SET and SEQUENCE can have tagged types.
596 */
598 if (!(*da_p)->parent->flags.is_root &&
599 (flags->der_type != FR_DER_TAG_SEQUENCE) && (flags->der_type != FR_DER_TAG_SET)) {
600 fr_strerror_printf("Cannot use 'option' for attribute %s DER type %s - the parent must be 'sequence' or 'set'",
601 (*da_p)->parent->name, fr_der_tag_to_str(flags->der_type));
602 return -1;
603 }
604
605 /*
606 * In the interest of laziness, allow a bare 'option', so
607 * that we don't have to give an attribute number, and
608 * then also duplicate that number in 'option='.
609 */
610 if (!value) {
611 if (!(*da_p)->state.attr_set || (*da_p)->attr > 0x1f) {
612 fr_strerror_printf("Missing value for 'option='");
613 return -1;
614 }
615
616 num = (*da_p)->attr;
617 goto check;
618 }
619
620 /*
621 * ATTRIBUTE can't have 'option='.
622 */
623 if ((*da_p)->state.attr_set) {
624 fr_strerror_printf("Cannot use 'option=%s' for attribute %s, just use 'option'", value, (*da_p)->name);
625 return -1;
626 }
627
628 /*
629 * We limit the allowed options (tag numbers) to ones
630 * which fit into the 5 bits of the first byte. We don't
631 * support continued tags.
632 */
633 num = strtoul(value, &end, 10);
634 if ((num == ULONG_MAX) || *end) {
635 fr_strerror_printf("Invalid value in 'option=%s'", value);
636 return -1;
637 }
638
639check:
640 if (num >= FR_DER_TAG_VALUE_MAX) {
641 fr_strerror_printf("Option value '%lu' is larger than 30", num);
642 return -1;
643 }
644
647 flags->option = num;
648 flags->is_option = true;
649
650 return 0;
651}
652
654{
655 fr_der_attr_flags_t *flags;
656
657 /*
658 * Only SET and SEQUENCE can have optional elements.
659 */
661 if (!(*da_p)->parent->flags.is_root &&
662 (flags->der_type != FR_DER_TAG_SEQUENCE) && (flags->der_type != FR_DER_TAG_SET)) {
663 fr_strerror_printf("Cannot use 'optional' for attribute %s DER type %s - the parent must be 'sequence' or 'set'",
664 (*da_p)->parent->name, fr_der_tag_to_str(flags->der_type));
665 return -1;
666 }
667
669 flags->optional = true;
670
671 return 0;
672}
673
675 { L("class"), { .func = dict_flag_class } },
676 { L("default"), { .func = dict_flag_default_value,.needs_value = true } },
677 { L("der_type"), { .func = dict_flag_der_type, .needs_value = true } },
678 { L("is_extensions"), { .func = dict_flag_is_extensions } },
679 { L("leaf"), { .func = dict_flag_leaf } },
680 { L("max"), { .func = dict_flag_max, .needs_value = true } },
681 { L("option"), { .func = dict_flag_option} },
682 { L("optional"), { .func = dict_flag_optional} },
683 { L("sequence_of"), { .func = dict_flag_sequence_of, .needs_value = true } },
684 { L("set_of"), { .func = dict_flag_set_of, .needs_value = true } },
685 { L("shortname"), { .func = dict_flag_shortname,.needs_value = true } },
686 { L("size"), { .func = dict_flag_size, .needs_value=true } },
687};
688
689static bool type_parse(fr_type_t *type_p,fr_dict_attr_t **da_p, char const *name)
690{
691 static const fr_table_num_sorted_t type_table[] = {
692 { L("bitstring"), FR_TYPE_OCTETS },
693// { L("bmpstring"), FR_TYPE_OCTETS },
694 { L("boolean"), FR_TYPE_BOOL },
695 { L("choice"), FR_TYPE_TLV },
696 { L("enumerated"), FR_TYPE_INT64 },
697 { L("generalizedtime"), FR_TYPE_DATE },
698 { L("generalstring"), FR_TYPE_STRING },
699 { L("ia5string"), FR_TYPE_STRING },
700 { L("integer"), FR_TYPE_INT64 },
701 { L("null"), FR_TYPE_BOOL },
702 { L("octetstring"), FR_TYPE_OCTETS },
703 { L("oid"), FR_TYPE_ATTR },
704 { L("printablestring"), FR_TYPE_STRING },
705 { L("sequence"), FR_TYPE_TLV },
706 { L("set"), FR_TYPE_TLV },
707 { L("t61string"), FR_TYPE_STRING },
708 { L("universalstring"), FR_TYPE_STRING },
709 { L("utctime"), FR_TYPE_DATE },
710 { L("utf8string"), FR_TYPE_STRING },
711 { L("visiblestring"), FR_TYPE_STRING },
712 { L("x509_extensions"), FR_TYPE_GROUP }
713 };
714 static size_t type_table_len = NUM_ELEMENTS(type_table);
715
716 static const fr_table_num_sorted_t der_tag_table[] = {
717 { L("bitstring"), FR_DER_TAG_BITSTRING },
718// { L("bmpstring"), FR_DER_TAG_BMP_STRING },
719 { L("boolean"), FR_DER_TAG_BOOLEAN },
720 { L("choice"), FR_DER_TAG_SEQUENCE },
721 { L("enumerated"), FR_DER_TAG_ENUMERATED },
722 { L("generalizedtime"), FR_DER_TAG_GENERALIZED_TIME },
723 { L("generalstring"), FR_DER_TAG_GENERAL_STRING },
724 { L("ia5string"), FR_DER_TAG_IA5_STRING },
725 { L("integer"), FR_DER_TAG_INTEGER },
726 { L("null"), FR_DER_TAG_NULL },
727 { L("octetstring"), FR_DER_TAG_OCTETSTRING },
728 { L("oid"), FR_DER_TAG_OID },
729 { L("printablestring"), FR_DER_TAG_PRINTABLE_STRING },
730 { L("sequence"), FR_DER_TAG_SEQUENCE },
731 { L("set"), FR_DER_TAG_SET },
732 { L("t61string"), FR_DER_TAG_T61_STRING },
733 { L("universalstring"), FR_DER_TAG_UNIVERSAL_STRING },
734 { L("utctime"), FR_DER_TAG_UTC_TIME },
735 { L("utf8string"), FR_DER_TAG_UTF8_STRING },
736 { L("visiblestring"), FR_DER_TAG_VISIBLE_STRING },
737 { L("x509_extensions"), FR_DER_TAG_SEQUENCE }
738 };
739 static size_t der_tag_table_len = NUM_ELEMENTS(der_tag_table);
740
742 fr_der_tag_t der_type;
743 fr_type_t fr_type;
744
745 /*
746 * To avoid confusion, we want to use the DER names where
747 * possible.
748 *
749 * We only use the FreeRADIUS names where we don't have a
750 * choice. :(
751 */
752 switch (*type_p) {
753 case FR_TYPE_TLV:
754 fr_strerror_const("Cannot use 'tlv' in DER. Please use 'sequence'");
755 return false;
756
757 default:
759 fr_strerror_printf("Cannot use type '%s' in the DER dictionaries",
760 fr_type_to_str(*type_p));
761 return false;
762
763 /*
764 * We allow all integer types. They may be
765 * internal, or they may be inside of a struct.
766 */
767 case FR_TYPE_NULL:
768 case FR_TYPE_INTEGER:
775 case FR_TYPE_STRUCT:
776 case FR_TYPE_GROUP:
777 case FR_TYPE_ATTR:
778 break;
779 }
780
781 /*
782 * Convert the DER data type to the underlying FreeRADIUS
783 * data type.
784 *
785 * If we don't know anything about the data type then
786 * it's either bad, or a data type which we don't care
787 * about. We set the der_type, and then return to the
788 * caller. It will check *type_p, which is likely
789 * FR_TYPE_NULL, and will print an error.
790 *
791 * "return true" here means "I dunno, you deal with it".
792 */
793 fr_type = fr_table_value_by_str(type_table, name, FR_TYPE_MAX);
794 if (fr_type == FR_TYPE_MAX) {
795 flags->der_type = fr_type_to_der_tag_default(*type_p);
796 if (!flags->der_type) goto invalid_type;
797 return true;
798 }
799
800 /*
801 * Now that we've converted the DER type to the
802 * underlying FreeRADIUS type, we get the corresponding
803 * DER type. This MUST exist, as the two tables MUST
804 * have the same names.
805 *
806 * @todo - arguably they should be in one table....
807 */
808 der_type = fr_table_value_by_str(der_tag_table, name, FR_DER_TAG_INVALID);
809 fr_assert(der_type != FR_DER_TAG_INVALID);
810
811 /*
812 * The der type is set only if there are extra flags seen
813 * and parsed by attr_valid().
814 */
816
817 /*
818 * Only now do we update the output data type. From here
819 * on in, any validation failure will return 'false', and
820 * not 'true'.
821 */
822 *type_p = fr_type;
823 flags->der_type = der_type;
824
825 if (der_type == FR_DER_TAG_OID) {
827
828 fr_assert(fr_type == FR_TYPE_ATTR);
829
831
832 ext = dict_attr_ext_alloc(da_p, FR_DICT_ATTR_EXT_REF); /* can change da_p */
833 if (unlikely(!ext)) return -1;
834
835 if (!attr_oid_tree) {
836 attr_oid_tree = fr_dict_attr_by_name(NULL, fr_dict_root((*da_p)->dict), "OID-Tree");
837 fr_assert(attr_oid_tree != NULL);
838 }
839
841 ext->ref = attr_oid_tree;
842 }
843
844 /*
845 * If it is a collection of x509 extensions, we will set
846 * a few other flags as per RFC 5280.
847 */
848 if (strcmp(name, "x509_extensions") == 0) {
849 flags->is_extensions = true;
850
852 flags->option = 3;
853 flags->is_option = true;
854
855 if (dict_flag_set_oid_and_value(da_p, flags) < 0) return false;
856 }
857
858 /*
859 * If this is a choice, then the children MUST have a limited option.
860 */
861 flags->is_choice = (strcmp(name, "choice") == 0);
862
863 return true;
864}
865
892
897
899{
902
903 if (flags->is_choice && unlikely(!fr_type_is_tlv(da->type))) {
904 fr_strerror_printf("Attribute %s of type %s is not allowed represent a collection of choices.",
905 da->name, fr_type_to_str(da->type));
906 return false;
907 }
908
909 /*
910 * The DER encoder / decoder assume that all pairs are FR_TYPE_INT64.
911 *
912 * The "on the wire" DER data has variable-sized encoding for integers,
913 * and drops leading zeros.
914 *
915 * For consistency, we disallow data types which the
916 * encoder/decoder don't handle. Except for data types
917 * in structs, because the struct encoder/decoder takes
918 * care of those.
919 */
920 if (fr_type_is_integer_except_bool(da->type) &&
921 !da->flags.internal &&
922 (da->type != FR_TYPE_INT64) &&
923 (da->type != FR_TYPE_DATE) && (da->type != FR_TYPE_TIME_DELTA) &&
924 (da->parent->type != FR_TYPE_STRUCT)) {
925 fr_strerror_printf("All integers in DER must be 'int64', and not '%s'",
926 fr_type_to_str(da->type));
927 return false;
928 }
929
930 if (flags->is_extensions) {
931 if (da->type != FR_TYPE_GROUP) {
932 fr_strerror_printf("Extensions must be type 'group', and not '%s'",
933 fr_type_to_str(da->type));
934 return false;
935 }
936
937#if 0
938 /*
939 * Group refs are added as unresolved refs, see dict_flag_ref(), and are resolved later
940 * in dict_fixup_group_apply().
941 *
942 * @todo - have a function called from dict_attr_finalize() ?
943 */
944 if (!fr_dict_attr_ref(da)) {
945 fr_strerror_const("Attribute is 'x509_extensions', but is missing 'ref=OID-Tree'");
946 return false;
947 }
948#endif
949
950 /*
951 * Avoid run-time checks.
952 */
953 if (!flags->max) flags->max = UINT64_MAX;
954 }
955
956 /*
957 * Either complain on invalid 'max', or set it to the maximum.
958 */
959 if ((flags->der_type != FR_DER_TAG_SET) && (flags->der_type != FR_DER_TAG_SEQUENCE)) {
960 if (!flags->max) {
961 flags->max = DER_MAX_STR;
962
963 } else if (flags->max > DER_MAX_STR) {
964 fr_strerror_printf("Invalid value of 'max' for DER type '%s'",
966 return false;
967 }
968 }
969
970 /*
971 * Set the restriction types, which make the run-time decoding a lot easier.
972 */
973 if (flags->is_set_of) {
974 flags->restrictions = (1 << flags->set_of);
975 }
976
977 if (flags->is_sequence_of) {
978 /*
979 * If the sequence isn't a choice, it has to be a sequence of one thing.
980 *
981 * If the sequence is group, then it has to be a sequence of sequences.
982 *
983 * If the sequence is a TLV, then the children will update the restrictions.
984 */
985 if (flags->sequence_of != FR_DER_TAG_CHOICE) {
986 flags->restrictions = (1 << flags->sequence_of);
987
988 } else if (da->type == FR_TYPE_GROUP) {
989#ifndef NDEBUG
990 fr_dict_attr_t const *ref;
991
992 ref = fr_dict_attr_ref(da);
993 if (ref) {
995 }
996#endif
997
998 /*
999 * A group of choices is really a sequence of sequences. i.e. x509extensions
1000 * contain only a sequence, as does sequence_of=oid_and_value.
1001 */
1002 flags->restrictions = (1 << FR_DER_TAG_SEQUENCE);
1003
1004 } else {
1005 /*
1006 * The children will update our restriction types.
1007 */
1008 fr_assert(da->type == FR_TYPE_TLV);
1009 }
1010 }
1011
1012 /*
1013 * If the parent is a choice, then the child MUST have a limited set of options / tags.
1014 */
1016
1017 /*
1018 * The attribute was defined with the full OID, and no 'option' flag. Add it manually.
1019 */
1020 if ((parent->is_choice && !flags->is_option) ||
1021 (flags->class == FR_DER_CLASS_PRIVATE) || (flags->class == FR_DER_CLASS_APPLICATION)) {
1022 fr_assert(da->attr < FR_DER_TAG_VALUE_MAX);
1023
1024 if (!flags->class) flags->class = FR_DER_CLASS_CONTEXT;
1025 flags->option = da->attr;
1026 flags->is_option = true;
1027 }
1028
1029 /*
1030 * Can't have duplicates.
1031 */
1032 if (flags->is_option) {
1033 if ((parent->restrictions & (1 << flags->option)) != 0) {
1034 fr_strerror_printf("Parent %s already has a child with option %u - duplicates are not allowed",
1035 da->parent->name, flags->option);
1036 return false;
1037 }
1038
1039 parent->restrictions |= (1 << flags->option);
1040
1041 } else if (parent->is_sequence_of && (parent->sequence_of == FR_DER_TAG_CHOICE)) {
1043
1044 flags->class = FR_DER_CLASS_CONTEXT;
1045// flags->option = flags->der_type;
1046
1047 if ((parent->restrictions & (1 << flags->der_type)) != 0) {
1048 fr_strerror_printf("Parent %s already has a child with tag %s - duplicates are not allowed",
1049 da->parent->name, fr_der_tag_to_str(flags->der_type));
1050 return false;
1051 }
1052
1053 parent->restrictions |= (1 << flags->der_type);
1054
1055 } else if (parent->is_sequence_of) {
1056 if (flags->der_type != parent->sequence_of) {
1057 fr_strerror_printf("Parent %s is a sequence_of=%s - a child cannot be %s",
1058 da->parent->name, fr_der_tag_to_str(parent->set_of),
1059 fr_der_tag_to_str(flags->der_type));
1060 return false;
1061 }
1062
1063 /*
1064 * A sequence can sometimes have mixed tags && options.
1065 */
1066 fr_assert(!flags->is_option);
1067
1068 } else if (parent->is_set_of) {
1069 if (flags->der_type != parent->set_of) {
1070 fr_strerror_printf("Parent %s is a set_of=%s - a child cannot be %s",
1071 da->parent->name, fr_der_tag_to_str(parent->set_of),
1072 fr_der_tag_to_str(flags->der_type));
1073 return false;
1074 }
1075 }
1076
1077 if ((da->type == FR_TYPE_GROUP) && !da->flags.allow_flat) {
1078 if ((da->parent == attr_oid_tree) || da->parent->flags.allow_flat) {
1079 da->flags.allow_flat = true;
1080 } else {
1081 fr_dict_attr_t const *oid;
1082
1083 for (oid = da->parent; !oid->flags.is_root; oid = oid->parent) {
1084 if (oid == attr_oid_tree) {
1085 da->flags.allow_flat = true;
1086 break;
1087 }
1088 }
1089 }
1090 }
1091
1092 return true;
1093}
1094
1097 .name = "der",
1098 .default_type_size = 4,
1099 .default_type_length = 4,
1100 .attr = {
1101 .flags = {
1102 .table = der_flags,
1103 .table_len = NUM_ELEMENTS(der_flags),
1104 .len = sizeof(fr_der_attr_flags_t),
1105 },
1106 .type_parse = type_parse,
1107 .valid = attr_valid
1108 },
1109
1110 .init = fr_der_global_init,
1111 .free = fr_der_global_free,
1112
1113 // .decode = fr_der_decode_foreign,
1114 // .encode = fr_der_encode_foreign,
1115};
#define RCSID(id)
Definition build.h:487
#define L(_str)
Helper for initialising arrays of string literals.
Definition build.h:209
#define unlikely(_x)
Definition build.h:383
#define UNUSED
Definition build.h:317
#define NUM_ELEMENTS(_t)
Definition build.h:339
static int invalid_type(fr_type_t type)
Definition calc.c:698
fr_der_tag_t
Enumeration describing the data types in a DER encoded structure.
Definition der.h:34
@ FR_DER_TAG_IA5_STRING
String of IA5 (7bit) chars.
Definition der.h:48
@ FR_DER_TAG_SEQUENCE
A sequence of DER encoded data (a structure).
Definition der.h:44
@ FR_DER_TAG_SET
A set of DER encoded data (a structure).
Definition der.h:45
@ FR_DER_TAG_BMP_STRING
String of BMP chars.
Definition der.h:54
@ FR_DER_TAG_INTEGER
Arbitrary width signed integer.
Definition der.h:37
@ FR_DER_TAG_BOOLEAN
Boolean true/false.
Definition der.h:36
@ FR_DER_TAG_CHOICE
A choice of types. Techically not a DER tag, but used to represent a choice.
Definition der.h:56
@ FR_DER_TAG_UTF8_STRING
String of UTF8 chars.
Definition der.h:43
@ FR_DER_TAG_UTC_TIME
A time in UTC "YYMMDDhhmmssZ" format.
Definition der.h:49
@ FR_DER_TAG_GENERALIZED_TIME
A time in "YYYYMMDDHHMMSS[.fff]Z" format.
Definition der.h:50
@ FR_DER_TAG_INVALID
Invalid tag.
Definition der.h:35
@ FR_DER_TAG_NULL
An empty value.
Definition der.h:40
@ FR_DER_TAG_OCTETSTRING
String of octets (length field specifies bytes).
Definition der.h:39
@ FR_DER_TAG_VISIBLE_STRING
String of visible chars.
Definition der.h:51
@ FR_DER_TAG_BITSTRING
String of bits (length field specifies bits).
Definition der.h:38
@ FR_DER_TAG_T61_STRING
String of T61 (8bit) chars.
Definition der.h:47
@ FR_DER_TAG_ENUMERATED
An enumerated value.
Definition der.h:42
@ FR_DER_TAG_UNIVERSAL_STRING
String of universal chars.
Definition der.h:53
@ FR_DER_TAG_PRINTABLE_STRING
String of printable chars.
Definition der.h:46
@ FR_DER_TAG_GENERAL_STRING
String of general chars.
Definition der.h:52
@ FR_DER_TAG_OID
Reference to an OID based attribute.
Definition der.h:41
@ FR_DER_TAG_MAX
Definition der.h:58
bool optional
optional, we MUST already have set 'option'
Definition der.h:107
bool is_extensions
a list of X.509 extensions
Definition der.h:111
#define fr_der_flag_der_type(_da)
Definition der.h:130
fr_der_tag_t der_type
the DER type, which is different from the FreeRADIUS type
Definition der.h:95
bool is_option
has an option defined
Definition der.h:106
bool is_sequence_of
sequence_of has been defined
Definition der.h:108
bool is_set_of
set_of has been defined
Definition der.h:109
uint32_t restrictions
for choice of options and tags - no dups allowed
Definition der.h:103
bool has_shortname
has a short name
Definition der.h:113
bool leaf
encode this OID along with its value
Definition der.h:114
#define DER_MAX_STR
Definition der.h:76
uint8_t min
mininum count
Definition der.h:104
bool is_oid_and_value
is OID+value
Definition der.h:110
static fr_der_attr_flags_t const * fr_der_attr_flags(fr_dict_attr_t const *da)
Definition der.h:122
bool is_choice
DER name "choice".
Definition der.h:115
#define FR_DER_TAG_VALUE_MAX
tags >=max can't exist
Definition der.h:61
uint8_t option
an "attribute number" encoded in the tag field.
Definition der.h:105
bool has_default_value
a default value exists
Definition der.h:112
fr_der_tag_class_t class
tag Class
Definition der.h:94
uint64_t max
maximum count of items in a sequence, set, or string.
Definition der.h:102
fr_der_tag_class_t
Definition der.h:68
@ FR_DER_CLASS_APPLICATION
Definition der.h:70
@ FR_DER_CLASS_CONTEXT
Definition der.h:71
@ FR_DER_CLASS_INVALID
Definition der.h:73
@ FR_DER_CLASS_PRIVATE
Definition der.h:72
int fr_dict_attr_set_group(fr_dict_attr_t **da_p, fr_dict_attr_t const *ref)
Definition dict_util.c:5300
fr_dict_t * fr_dict_unconst(fr_dict_t const *dict)
Coerce to non-const.
Definition dict_util.c:4916
#define fr_dict_autofree(_to_free)
Definition dict.h:917
fr_dict_attr_t const * fr_dict_attr_by_name(fr_dict_attr_err_t *err, fr_dict_attr_t const *parent, char const *attr))
Locate a fr_dict_attr_t by its name.
Definition dict_util.c:3535
fr_dict_attr_t const * fr_dict_root(fr_dict_t const *dict)
Return the root attribute of a dictionary.
Definition dict_util.c:2672
fr_dict_attr_t const ** out
Where to write a pointer to the resolved fr_dict_attr_t.
Definition dict.h:294
fr_dict_t const ** out
Where to write a pointer to the loaded/resolved fr_dict_t.
Definition dict.h:307
int fr_dict_attr_autoload(fr_dict_attr_autoload_t const *to_load)
Process a dict_attr_autoload element to load/verify a dictionary attribute.
Definition dict_util.c:4403
@ FR_DICT_ATTR_EXT_PROTOCOL_SPECIFIC
Protocol specific extensions.
Definition dict.h:192
@ FR_DICT_ATTR_EXT_REF
Attribute references another attribute and/or dictionary.
Definition dict.h:186
#define fr_dict_autoload(_to_load)
Definition dict.h:914
#define DICT_AUTOLOAD_TERMINATOR
Definition dict.h:313
char const * name
name of this protocol
Definition dict.h:458
Specifies an attribute which must be present for the module to function.
Definition dict.h:293
Specifies a dictionary which must be loaded/loadable for the module to function.
Definition dict.h:306
Protocol specific custom flag definitnion.
Definition dict.h:427
Protocol-specific callbacks in libfreeradius-PROTOCOL.
Definition dict.h:457
fr_dict_attr_ref_type_t type
The state of the reference.
Definition dict_ext.h:78
static void * fr_dict_attr_ext(fr_dict_attr_t const *da, fr_dict_attr_ext_t ext)
Definition dict_ext.h:121
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:148
@ FR_DICT_ATTR_REF_ROOT
only for FR_TYPE_ATTR, point to the default root for enums
Definition dict_ext.h:65
Attribute extension - Holds a reference to an attribute in another dictionary.
Definition dict_ext.h:77
static void * dict_attr_ext_alloc(fr_dict_attr_t **da_p, fr_dict_attr_ext_t ext)
Allocate an attribute extension.
Test enumeration values.
Definition dict_test.h:92
static uint32_t instance_count
Definition base.c:44
HIDDEN fr_dict_t const * dict_der
Definition base.c:39
fr_type_t
@ FR_TYPE_TIME_DELTA
A period of time measured in nanoseconds.
@ FR_TYPE_IPV4_ADDR
32 Bit IPv4 Address.
@ FR_TYPE_INT8
8 Bit signed integer.
@ FR_TYPE_TLV
Contains nested attributes.
@ FR_TYPE_IPV6_PREFIX
IPv6 Prefix.
@ FR_TYPE_STRING
String of printable characters.
@ FR_TYPE_MAX
Number of defined data types.
@ FR_TYPE_NULL
Invalid (uninitialised) attribute type.
@ FR_TYPE_UINT16
16 Bit unsigned integer.
@ FR_TYPE_INT64
64 Bit signed integer.
@ FR_TYPE_INT16
16 Bit signed integer.
@ FR_TYPE_DATE
Unix time stamp, always has value >2^31.
@ FR_TYPE_UINT8
8 Bit unsigned integer.
@ FR_TYPE_UINT32
32 Bit unsigned integer.
@ FR_TYPE_STRUCT
like TLV, but without T or L, and fixed-width children
@ FR_TYPE_INT32
32 Bit signed integer.
@ FR_TYPE_UINT64
64 Bit unsigned integer.
@ FR_TYPE_IPV6_ADDR
128 Bit IPv6 Address.
@ FR_TYPE_IPV4_PREFIX
IPv4 Prefix.
@ FR_TYPE_BOOL
A truth value.
@ FR_TYPE_COMBO_IP_ADDR
IPv4 or IPv6 address depending on length.
@ FR_TYPE_OCTETS
Raw octets.
@ FR_TYPE_GROUP
A grouping of other attributes.
unsigned int uint32_t
#define UINT8_MAX
static int dict_flag_optional(fr_dict_attr_t **da_p, UNUSED char const *value, UNUSED fr_dict_flag_parser_rule_t const *rules)
Definition base.c:653
bool fr_der_tags_compatible(fr_der_tag_t tag1, fr_der_tag_t tag2)
Definition base.c:103
#define ALL_STRINGS
Definition base.c:85
void fr_der_global_free(void)
Definition base.c:222
static const uint64_t der_tags_compatible[FR_DER_TAG_MAX]
Definition base.c:90
static const fr_dict_flag_parser_t der_flags[]
Definition base.c:674
static int dict_flag_der_type(fr_dict_attr_t **da_p, char const *value, UNUSED fr_dict_flag_parser_rule_t const *rules)
Definition base.c:306
static int dict_flag_leaf(fr_dict_attr_t **da_p, UNUSED char const *value, UNUSED fr_dict_flag_parser_rule_t const *rules)
Definition base.c:437
static int dict_flag_max(fr_dict_attr_t **da_p, char const *value, UNUSED fr_dict_flag_parser_rule_t const *rules)
Definition base.c:571
static const fr_der_tag_t fr_type_to_der_tag_defaults[FR_TYPE_MAX+1]
Definition base.c:866
static int dict_flag_sequence_of(fr_dict_attr_t **da_p, char const *value, UNUSED fr_dict_flag_parser_rule_t const *rules)
Definition base.c:358
static int dict_flag_default_value(fr_dict_attr_t **da_p, char const *value, UNUSED fr_dict_flag_parser_rule_t const *rules)
Definition base.c:273
int fr_der_global_init(void)
Definition base.c:199
static bool attr_valid(fr_dict_attr_t *da)
Definition base.c:898
static int dict_flag_class(fr_dict_attr_t **da_p, char const *value, UNUSED fr_dict_flag_parser_rule_t const *rules)
Definition base.c:232
fr_dict_protocol_t libfreeradius_der_dict_protocol
Definition base.c:1096
bool fr_type_to_der_tag_valid(fr_type_t type, fr_der_tag_t tag)
Definition base.c:179
static bool type_parse(fr_type_t *type_p, fr_dict_attr_t **da_p, char const *name)
Definition base.c:689
static int dict_flag_set_of(fr_dict_attr_t **da_p, char const *value, UNUSED fr_dict_flag_parser_rule_t const *rules)
Definition base.c:389
fr_dict_attr_autoload_t libfreeradius_der_dict_attr[]
Definition base.c:50
fr_dict_attr_t const * attr_oid_tree
Definition base.c:40
static int dict_flag_is_extensions(fr_dict_attr_t **da_p, UNUSED char const *value, UNUSED fr_dict_flag_parser_rule_t const *rules)
Definition base.c:428
fr_der_tag_t fr_type_to_der_tag_default(fr_type_t type)
Definition base.c:893
char const * fr_der_tag_to_str(fr_der_tag_t tag)
Definition base.c:80
char const * fr_der_dict_attr_to_shortname(fr_dict_attr_t const *da)
Definition base.c:187
static int dict_flag_set_oid_and_value(fr_dict_attr_t **da_p, fr_der_attr_flags_t *flags)
Definition base.c:337
static int dict_flag_option(fr_dict_attr_t **da_p, char const *value, UNUSED fr_dict_flag_parser_rule_t const *rules)
Definition base.c:588
fr_dict_autoload_t libfreeradius_der_dict[]
Definition base.c:43
static size_t tag_name_to_number_len
Definition base.c:77
static const bool * fr_type_to_der_tags[FR_DER_TAG_MAX]
Definition base.c:111
static fr_table_num_sorted_t const tag_name_to_number[]
Definition base.c:55
static int dict_flag_shortname(fr_dict_attr_t **da_p, char const *value, UNUSED fr_dict_flag_parser_rule_t const *rules)
Definition base.c:457
static int dict_flag_size(fr_dict_attr_t **da_p, char const *value, UNUSED fr_dict_flag_parser_rule_t const *rules)
Definition base.c:487
VQP attributes.
#define fr_assert(_expr)
Definition rad_assert.h:38
static char const * name
fr_aka_sim_id_type_t type
#define fr_table_value_by_str(_table, _name, _def)
Convert a string to a value using a sorted or ordered table.
Definition table.h:653
#define fr_table_str_by_value(_table, _number, _def)
Convert an integer to a string.
Definition table.h:772
An element in a lexicographically sorted array of name to num mappings.
Definition table.h:49
static fr_slen_t parent
Definition pair.h:859
char const * fr_strerror(void)
Get the last library error.
Definition strerror.c:553
#define fr_strerror_printf(_fmt,...)
Log to thread local error buffer.
Definition strerror.h:64
#define fr_strerror_const(_msg)
Definition strerror.h:223
#define FR_TYPE_VARIABLE_SIZE
Definition types.h:312
#define fr_type_is_variable_size(_x)
Definition types.h:389
#define fr_type_is_structural(_x)
Definition types.h:393
@ FR_TYPE_ATTR
A contains an attribute reference.
Definition types.h:84
#define fr_type_is_integer_except_bool(_x)
Definition types.h:381
#define FR_TYPE_INTEGER
Definition types.h:305
#define fr_type_is_tlv(_x)
Definition types.h:373
#define fr_type_is_leaf(_x)
Definition types.h:394
static char const * fr_type_to_str(fr_type_t type)
Return a static string containing the type name.
Definition types.h:455
ssize_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:6076
#define fr_value_box_alloc(_ctx, _type, _enumv)
Allocate a value box of a specific type.
Definition value.h:644