/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 | | }; |