Coverage Report

Created: 2026-08-14 07:17

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