/src/freeradius-server/src/lib/util/cbor.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 | | /** CBPR encoding and decoding |
18 | | * |
19 | | * @file src/lib/util/cbor.c |
20 | | * |
21 | | * @copyright 2024 Network RADIUS SAS (legal@networkradius.com) |
22 | | */ |
23 | | RCSID("$Id: 89c32c9eed0918d7eb9971075997f0a025f953b9 $") |
24 | | |
25 | | #include <freeradius-devel/util/cbor.h> |
26 | | |
27 | 0 | #define CBOR_INTEGER (0) |
28 | 0 | #define CBOR_NEGATIVE (1) |
29 | 0 | #define CBOR_OCTETS (2) |
30 | 0 | #define CBOR_STRING (3) |
31 | 0 | #define CBOR_ARRAY (4) |
32 | 0 | #define CBOR_MAP (5) |
33 | 0 | #define CBOR_TAG (6) |
34 | 0 | #define CBOR_FLOAT (7) |
35 | | |
36 | 0 | #define CBOR_1_BYTE ((uint8_t) 24) |
37 | 0 | #define CBOR_2_BYTE ((uint8_t) 25) |
38 | 0 | #define CBOR_4_BYTE ((uint8_t) 26) |
39 | 0 | #define CBOR_8_BYTE ((uint8_t) 27) |
40 | | |
41 | | static const char *cbor_type_to_str[8] = { |
42 | | "integer", "negative", "octets", "string", |
43 | | "array", "map", "tag", "float" |
44 | | }; |
45 | | |
46 | | /* |
47 | | * Some of our data types need tags. |
48 | | * |
49 | | * We don't have a tag to data type array. When decoding, we should usually have the enclosing pair |
50 | | * number, which includes our data type. If the tag type doesn't match the value here, then something is |
51 | | * wrong. |
52 | | */ |
53 | | static const uint64_t cbor_type_to_tag[FR_TYPE_MAX + 1] = { |
54 | | [FR_TYPE_DATE] = 1, |
55 | | [FR_TYPE_ETHERNET] = 48, |
56 | | [FR_TYPE_IPV4_ADDR] = 52, |
57 | | [FR_TYPE_IPV4_PREFIX] = 52, |
58 | | [FR_TYPE_IPV6_ADDR] = 54, |
59 | | [FR_TYPE_IPV6_PREFIX] = 54, |
60 | | [FR_TYPE_TIME_DELTA] = 1002, |
61 | | }; |
62 | | |
63 | | static fr_type_t cbor_guess_type(fr_dbuff_t *dbuff, bool pair); |
64 | | |
65 | | static ssize_t cbor_encode_integer(fr_dbuff_t *dbuff, uint8_t type, uint64_t data) |
66 | 0 | { |
67 | 0 | fr_dbuff_t work_dbuff = FR_DBUFF(dbuff); |
68 | 0 | uint8_t value[8]; |
69 | |
|
70 | 0 | fr_assert(type < 8); |
71 | 0 | type <<= 5; |
72 | |
|
73 | 0 | if (data < 24) { |
74 | 0 | data |= type; |
75 | |
|
76 | 0 | FR_DBUFF_IN_BYTES_RETURN(&work_dbuff, (uint8_t) (data & 0xff)); |
77 | 0 | goto done; |
78 | 0 | } |
79 | | |
80 | 0 | if (data < (((uint64_t) 1) << 8)) { |
81 | 0 | value[0] = data; |
82 | |
|
83 | 0 | FR_DBUFF_IN_BYTES_RETURN(&work_dbuff, (uint8_t) (type | CBOR_1_BYTE)); |
84 | 0 | FR_DBUFF_IN_RETURN(&work_dbuff, value[0]); |
85 | 0 | goto done; |
86 | 0 | } |
87 | | |
88 | 0 | if (data < (((uint64_t) 1) << 16)) { |
89 | 0 | fr_nbo_from_uint16(value, data); |
90 | |
|
91 | 0 | FR_DBUFF_IN_BYTES_RETURN(&work_dbuff, (uint8_t) (type | CBOR_2_BYTE)); |
92 | 0 | FR_DBUFF_IN_MEMCPY_RETURN(&work_dbuff, value, 2); |
93 | 0 | goto done; |
94 | 0 | } |
95 | | |
96 | 0 | if (data < (((uint64_t) 1) << 32)) { |
97 | 0 | fr_nbo_from_uint32(value, data); |
98 | |
|
99 | 0 | FR_DBUFF_IN_BYTES_RETURN(&work_dbuff, (uint8_t) (type | CBOR_4_BYTE)); |
100 | 0 | FR_DBUFF_IN_MEMCPY_RETURN(&work_dbuff, value, 4); |
101 | 0 | goto done; |
102 | 0 | } |
103 | | |
104 | 0 | fr_nbo_from_uint64(value, data); |
105 | | |
106 | | /* |
107 | | * Has to be 8 bytes. |
108 | | */ |
109 | 0 | FR_DBUFF_IN_BYTES_RETURN(&work_dbuff, type | CBOR_8_BYTE); |
110 | 0 | FR_DBUFF_IN_MEMCPY_RETURN(&work_dbuff, value, 8); |
111 | | |
112 | 0 | done: |
113 | 0 | return fr_dbuff_set(dbuff, &work_dbuff); |
114 | 0 | } |
115 | | |
116 | 0 | #define cbor_encode_array(_dbuff, _size) cbor_encode_integer(_dbuff, CBOR_ARRAY, _size) |
117 | | |
118 | 0 | #define cbor_encode_tag(_dbuff, _tag) cbor_encode_integer(_dbuff, CBOR_TAG, _tag) |
119 | | |
120 | | /* |
121 | | * Make many things easier |
122 | | */ |
123 | 0 | #define return_slen return FR_DBUFF_ERROR_OFFSET(slen, fr_dbuff_used(&work_dbuff)) |
124 | | |
125 | | /* |
126 | | * Octets is length + data |
127 | | */ |
128 | | static ssize_t cbor_encode_octets(fr_dbuff_t *dbuff, uint8_t const *data, size_t data_len) |
129 | 0 | { |
130 | 0 | fr_dbuff_t work_dbuff = FR_DBUFF(dbuff); |
131 | 0 | ssize_t slen; |
132 | |
|
133 | 0 | slen = cbor_encode_integer(&work_dbuff, CBOR_OCTETS, data_len); |
134 | 0 | if (slen <= 0) return_slen; |
135 | | |
136 | 0 | if (data_len > 0) FR_DBUFF_IN_MEMCPY_RETURN(&work_dbuff, data, data_len); |
137 | | |
138 | 0 | return fr_dbuff_set(dbuff, &work_dbuff); |
139 | 0 | } |
140 | | |
141 | | static ssize_t cbor_encode_int64(fr_dbuff_t *dbuff, int64_t neg) |
142 | 0 | { |
143 | 0 | fr_dbuff_t work_dbuff = FR_DBUFF(dbuff); |
144 | 0 | ssize_t slen; |
145 | |
|
146 | 0 | if (neg >= 0) { |
147 | 0 | slen = cbor_encode_integer(&work_dbuff, CBOR_INTEGER, neg); |
148 | 0 | } else { |
149 | 0 | uint64_t data; |
150 | |
|
151 | 0 | neg++; |
152 | 0 | data = -neg; |
153 | 0 | slen = cbor_encode_integer(&work_dbuff, CBOR_NEGATIVE, data); |
154 | 0 | } |
155 | 0 | if (slen <= 0) return_slen; |
156 | | |
157 | 0 | return fr_dbuff_set(dbuff, &work_dbuff); |
158 | 0 | } |
159 | | |
160 | 0 | #define cbor_encode_key cbor_encode_int64 |
161 | | |
162 | | /** Encode CBOR |
163 | | * |
164 | | * Values 0..23 can be encoded in place. Other values can be encoded using the closest smallest integer |
165 | | */ |
166 | | ssize_t fr_cbor_encode_value_box(fr_dbuff_t *dbuff, fr_value_box_t *vb) |
167 | 0 | { |
168 | 0 | fr_dbuff_t work_dbuff = FR_DBUFF(dbuff); |
169 | 0 | uint8_t type = CBOR_INTEGER; |
170 | 0 | uint64_t data; |
171 | 0 | int64_t neg; |
172 | 0 | ssize_t slen; |
173 | 0 | uint8_t const *p, *end; |
174 | |
|
175 | 0 | switch (vb->type) { |
176 | 0 | case FR_TYPE_BOOL: |
177 | | /* |
178 | | * One byte of FLOAT (i.e. special value), and the boolean as a "simple value". |
179 | | */ |
180 | 0 | FR_DBUFF_IN_BYTES_RETURN(&work_dbuff, (uint8_t) ((CBOR_FLOAT << 5) | (20 + vb->vb_bool))); |
181 | 0 | break; |
182 | | |
183 | 0 | case FR_TYPE_UINT8: |
184 | 0 | data = vb->vb_uint8; |
185 | 0 | goto encode_int; |
186 | | |
187 | 0 | case FR_TYPE_UINT16: |
188 | 0 | data = vb->vb_uint16; |
189 | 0 | goto encode_int; |
190 | | |
191 | 0 | case FR_TYPE_UINT32: |
192 | 0 | data = vb->vb_uint32; |
193 | 0 | goto encode_int; |
194 | | |
195 | 0 | case FR_TYPE_UINT64: |
196 | 0 | data = vb->vb_uint64; |
197 | 0 | goto encode_int; |
198 | | |
199 | | /* |
200 | | * Negative numbers. |
201 | | */ |
202 | 0 | case FR_TYPE_INT8: |
203 | 0 | neg = vb->vb_int8; |
204 | 0 | goto encode_neg; |
205 | | |
206 | 0 | case FR_TYPE_INT16: |
207 | 0 | neg = vb->vb_int16; |
208 | 0 | goto encode_neg; |
209 | | |
210 | 0 | case FR_TYPE_INT32: |
211 | 0 | neg = vb->vb_int32; |
212 | 0 | goto encode_neg; |
213 | | |
214 | 0 | case FR_TYPE_INT64: |
215 | 0 | neg = vb->vb_int64; |
216 | 0 | encode_neg: |
217 | 0 | if (neg >= 0) { |
218 | 0 | type = CBOR_INTEGER; |
219 | 0 | data = neg; |
220 | 0 | goto encode_int; |
221 | 0 | } |
222 | | |
223 | | /* |
224 | | * convert -1..-2^63 to 0..-(2^63-1) |
225 | | * and then it fits into a positive integer. |
226 | | */ |
227 | 0 | neg++; |
228 | 0 | data = -neg; |
229 | |
|
230 | 0 | encode_int: |
231 | 0 | return cbor_encode_integer(dbuff, type, data); |
232 | | |
233 | 0 | case FR_TYPE_OCTETS: |
234 | 0 | return cbor_encode_octets(dbuff, vb->vb_octets, vb->vb_length); |
235 | | |
236 | 0 | case FR_TYPE_STRING: |
237 | 0 | slen = cbor_encode_integer(&work_dbuff, CBOR_STRING, vb->vb_length); |
238 | 0 | if (slen <= 0) return_slen; |
239 | | |
240 | 0 | if (vb->vb_length) FR_DBUFF_IN_MEMCPY_RETURN(&work_dbuff, vb->vb_strvalue, vb->vb_length); |
241 | 0 | break; |
242 | | |
243 | | /* |
244 | | * More complex data types are represented by type "tag", followed by a tag number. The |
245 | | * actual data is then encoded as the next item after the tag. |
246 | | */ |
247 | 0 | case FR_TYPE_ETHERNET: |
248 | 0 | slen = cbor_encode_tag(&work_dbuff, cbor_type_to_tag[vb->type]); |
249 | 0 | if (slen <= 0) return_slen; |
250 | | |
251 | 0 | slen = cbor_encode_octets(&work_dbuff, vb->vb_ether, sizeof(vb->vb_ether)); |
252 | 0 | if (slen <= 0) return_slen; |
253 | 0 | break; |
254 | | |
255 | | /* |
256 | | * Tag 1, with integer seconds since epoch. |
257 | | * |
258 | | * @todo - if the input has time resolution, then save it in that format. |
259 | | * |
260 | | * RFC 9581 Section 3. |
261 | | * |
262 | | * A tag with key 1001, and then: a map with required key 1 (integer epoch seconds) and |
263 | | * optional key -3 (milliseconds), -6 (microseconds), or -9 (integer nanoseconds). |
264 | | * |
265 | | * For the encoder, there are a ton of different formats for dates, and we shouldn't |
266 | | * bother to parse them all. :( |
267 | | */ |
268 | 0 | case FR_TYPE_DATE: |
269 | 0 | slen = cbor_encode_tag(&work_dbuff, cbor_type_to_tag[vb->type]); |
270 | 0 | if (slen <= 0) return_slen; |
271 | | |
272 | 0 | neg = fr_unix_time_to_sec(vb->vb_date); |
273 | 0 | slen = cbor_encode_int64(&work_dbuff, neg); |
274 | 0 | if (slen <= 0) return_slen; |
275 | 0 | break; |
276 | | |
277 | | /* |
278 | | * RFC 9581 Section 4. |
279 | | * |
280 | | * A tag with key 1002, and then: a map with required key 1 (integer seconds) and |
281 | | * optional key -3 (milliseconds), -6 (microseconds), or -9 (integer nanoseconds). |
282 | | */ |
283 | 0 | case FR_TYPE_TIME_DELTA: |
284 | 0 | slen = cbor_encode_tag(&work_dbuff, cbor_type_to_tag[vb->type]); |
285 | 0 | if (slen <= 0) return_slen; |
286 | | |
287 | 0 | neg = fr_time_delta_unwrap(vb->vb_time_delta) % NSEC; |
288 | |
|
289 | 0 | slen = cbor_encode_integer(&work_dbuff, CBOR_MAP, 1 + (neg != 0)); |
290 | 0 | if (slen <= 0) return_slen; |
291 | | |
292 | | /* |
293 | | * 1: seconds |
294 | | */ |
295 | 0 | slen = cbor_encode_key(&work_dbuff, 1); |
296 | 0 | if (slen <= 0) return_slen; |
297 | | |
298 | 0 | slen = cbor_encode_int64(&work_dbuff, fr_time_delta_to_sec(vb->vb_time_delta)); |
299 | 0 | if (slen <= 0) return_slen; |
300 | | |
301 | | /* |
302 | | * -9: nanoseconds |
303 | | */ |
304 | 0 | if (neg) { |
305 | 0 | slen = cbor_encode_key(&work_dbuff, -9); |
306 | 0 | if (slen <= 0) return_slen; |
307 | | |
308 | 0 | slen = cbor_encode_int64(&work_dbuff, neg); |
309 | 0 | if (slen <= 0) return_slen; |
310 | 0 | } |
311 | 0 | break; |
312 | | |
313 | | /* |
314 | | * RFC 9164, Section 3.3 |
315 | | * |
316 | | * |
317 | | * tag=IPv4 + address + optional (prefix + scope) |
318 | | */ |
319 | 0 | case FR_TYPE_IPV4_ADDR: |
320 | 0 | slen = cbor_encode_tag(&work_dbuff, cbor_type_to_tag[vb->type]); |
321 | 0 | if (slen <= 0) return_slen; |
322 | | |
323 | 0 | if (vb->vb_ip.scope_id != 0) { |
324 | 0 | FR_DBUFF_IN_BYTES_RETURN(&work_dbuff, (uint8_t) ((CBOR_ARRAY << 5) | 3)); |
325 | |
|
326 | 0 | } |
327 | | |
328 | 0 | slen = cbor_encode_octets(&work_dbuff, (uint8_t const *) &vb->vb_ipv4addr, 4); |
329 | 0 | if (slen <= 0) return_slen; |
330 | | |
331 | 0 | if (vb->vb_ip.scope_id == 0) break; |
332 | | |
333 | 0 | slen = cbor_encode_integer(&work_dbuff, CBOR_INTEGER, (uint8_t) 32); |
334 | 0 | if (slen <= 0) return_slen; |
335 | | |
336 | 0 | slen = cbor_encode_integer(&work_dbuff, CBOR_INTEGER, vb->vb_ip.scope_id); |
337 | 0 | if (slen <= 0) return_slen; |
338 | 0 | break; |
339 | | |
340 | | /* |
341 | | * RFC 9164, Section 3.2 |
342 | | * |
343 | | * tag=IPv6 + address + optional (prefix + scope) |
344 | | */ |
345 | 0 | case FR_TYPE_IPV6_ADDR: |
346 | 0 | slen = cbor_encode_tag(&work_dbuff, cbor_type_to_tag[vb->type]); |
347 | 0 | if (slen <= 0) return_slen; |
348 | | |
349 | 0 | if (vb->vb_ip.scope_id != 0) { |
350 | 0 | FR_DBUFF_IN_BYTES_RETURN(&work_dbuff, (uint8_t) ((CBOR_ARRAY << 5) | 3)); |
351 | 0 | } |
352 | | |
353 | 0 | slen = cbor_encode_octets(&work_dbuff, (uint8_t const *) &vb->vb_ipv6addr, 16); |
354 | 0 | if (slen <= 0) return_slen; |
355 | | |
356 | 0 | if (vb->vb_ip.scope_id == 0) break; |
357 | | |
358 | 0 | slen = cbor_encode_integer(&work_dbuff, CBOR_INTEGER, (uint8_t) 128); |
359 | 0 | if (slen <= 0) return_slen; |
360 | | |
361 | 0 | slen = cbor_encode_integer(&work_dbuff, CBOR_INTEGER, vb->vb_ip.scope_id); |
362 | 0 | if (slen <= 0) return_slen; |
363 | 0 | break; |
364 | | |
365 | | /* |
366 | | * RFC 9164, Section 3.3 |
367 | | * |
368 | | * tag=IPv4 + array(prefix-length, address) |
369 | | */ |
370 | 0 | case FR_TYPE_IPV4_PREFIX: |
371 | 0 | slen = cbor_encode_tag(&work_dbuff, cbor_type_to_tag[vb->type]); |
372 | 0 | if (slen <= 0) return_slen; |
373 | | |
374 | 0 | slen = cbor_encode_array(&work_dbuff, 2); |
375 | 0 | if (slen <= 0) return_slen; |
376 | | |
377 | 0 | slen = cbor_encode_integer(&work_dbuff, CBOR_INTEGER, vb->vb_ip.prefix); |
378 | 0 | if (slen <= 0) return_slen; |
379 | | |
380 | 0 | p = (uint8_t const *) &vb->vb_ipv4addr; |
381 | 0 | end = p + 3; |
382 | | |
383 | | /* |
384 | | * RFC 9164 Section 4.2 - Drop lower octets which are all zero. |
385 | | * |
386 | | * If this results in a zero-length string, so be it. |
387 | | * |
388 | | * Note also that "There is no relationship between the number of bytes omitted and the |
389 | | * prefix length." |
390 | | */ |
391 | 0 | do { |
392 | 0 | if (*end != 0) break; |
393 | | |
394 | 0 | end--; |
395 | 0 | } while (end != p); |
396 | |
|
397 | 0 | slen = cbor_encode_octets(&work_dbuff, p, (end - p) + (*end != 0)); |
398 | 0 | if (slen <= 0) return_slen; |
399 | 0 | break; |
400 | | |
401 | | /* |
402 | | * RFC 9164, Section 3.2 |
403 | | * |
404 | | * tag=IPv6 + array(prefix-length, address) |
405 | | */ |
406 | 0 | case FR_TYPE_IPV6_PREFIX: |
407 | 0 | slen = cbor_encode_tag(&work_dbuff, cbor_type_to_tag[vb->type]); |
408 | 0 | if (slen <= 0) return_slen; |
409 | | |
410 | 0 | slen = cbor_encode_array(&work_dbuff, 2); |
411 | 0 | if (slen <= 0) return_slen; |
412 | | |
413 | 0 | slen = cbor_encode_integer(&work_dbuff, CBOR_INTEGER, vb->vb_ip.prefix); |
414 | 0 | if (slen <= 0) return_slen; |
415 | | |
416 | 0 | p = (uint8_t const *) &vb->vb_ipv6addr; |
417 | 0 | end = p + 15; |
418 | | |
419 | | /* |
420 | | * RFC 9164 Section 4.2 - Drop lower octets which are all zero. |
421 | | * |
422 | | * If this results in a zero-length string, so be it. |
423 | | * |
424 | | * Note also that "There is no relationship between the number of bytes omitted and the |
425 | | * prefix length." |
426 | | */ |
427 | 0 | do { |
428 | 0 | if (*end != 0) break; |
429 | | |
430 | 0 | end--; |
431 | 0 | } while (end != p); |
432 | |
|
433 | 0 | slen = cbor_encode_octets(&work_dbuff, p, (end - p) + (*end != 0)); |
434 | 0 | if (slen <= 0) return_slen; |
435 | | |
436 | 0 | break; |
437 | | |
438 | 0 | case FR_TYPE_FLOAT32: |
439 | 0 | FR_DBUFF_IN_BYTES_RETURN(&work_dbuff, (uint8_t) ((CBOR_FLOAT << 5) | CBOR_4_BYTE)); |
440 | 0 | FR_DBUFF_IN_MEMCPY_RETURN(&work_dbuff, (uint8_t const *) &vb->vb_float32, sizeof(vb->vb_float32)); |
441 | 0 | break; |
442 | | |
443 | 0 | case FR_TYPE_FLOAT64: |
444 | 0 | FR_DBUFF_IN_BYTES_RETURN(&work_dbuff, (uint8_t) ((CBOR_FLOAT << 5) | CBOR_8_BYTE)); |
445 | 0 | FR_DBUFF_IN_MEMCPY_RETURN(&work_dbuff, (uint8_t const *) &vb->vb_float64, sizeof(vb->vb_float64)); |
446 | 0 | break; |
447 | | |
448 | 0 | case FR_TYPE_GROUP: |
449 | | /* |
450 | | * Zero-length array. |
451 | | */ |
452 | 0 | if (fr_value_box_list_num_elements(&vb->vb_group) == 0) { |
453 | 0 | FR_DBUFF_IN_BYTES_RETURN(&work_dbuff, (uint8_t) ((CBOR_ARRAY << 5) | 0)); |
454 | 0 | break; |
455 | 0 | } |
456 | | |
457 | | /* |
458 | | * The value is array(children) |
459 | | */ |
460 | 0 | slen = cbor_encode_integer(&work_dbuff, CBOR_ARRAY, |
461 | 0 | fr_value_box_list_num_elements(&vb->vb_group)); |
462 | 0 | if (slen <= 0) return_slen; |
463 | | |
464 | | |
465 | 0 | fr_value_box_list_foreach(&vb->vb_group, child) { |
466 | 0 | slen = fr_cbor_encode_value_box(&work_dbuff, child); |
467 | 0 | if (slen <= 0) return_slen; |
468 | 0 | } |
469 | 0 | break; |
470 | | |
471 | | |
472 | 0 | default: |
473 | 0 | fr_strerror_printf("Invalid data type %s for cbor encoding", fr_type_to_str(vb->type)); |
474 | 0 | return -1; |
475 | 0 | } |
476 | | |
477 | 0 | return fr_dbuff_set(dbuff, &work_dbuff); |
478 | 0 | } |
479 | | |
480 | | |
481 | | static ssize_t cbor_decode_integer(uint64_t *out, uint8_t info, fr_dbuff_t *dbuff) |
482 | 0 | { |
483 | 0 | fr_dbuff_t work_dbuff = FR_DBUFF(dbuff); |
484 | |
|
485 | 0 | if (info < 24) { |
486 | 0 | *out = info; |
487 | 0 | return 0; |
488 | 0 | } |
489 | | |
490 | 0 | if (info == CBOR_1_BYTE) { |
491 | 0 | uint8_t value; |
492 | |
|
493 | 0 | FR_DBUFF_OUT_RETURN(&value, &work_dbuff); |
494 | 0 | *out = value; |
495 | 0 | goto done; |
496 | 0 | } |
497 | | |
498 | 0 | if (info == CBOR_2_BYTE) { |
499 | 0 | uint16_t value; |
500 | |
|
501 | 0 | FR_DBUFF_OUT_RETURN(&value, &work_dbuff); |
502 | 0 | *out = value; |
503 | 0 | goto done; |
504 | 0 | } |
505 | | |
506 | 0 | if (info == CBOR_4_BYTE) { |
507 | 0 | uint32_t value; |
508 | |
|
509 | 0 | FR_DBUFF_OUT_RETURN(&value, &work_dbuff); |
510 | 0 | *out = value; |
511 | 0 | goto done; |
512 | 0 | } |
513 | | |
514 | 0 | if (info == CBOR_8_BYTE) { |
515 | 0 | uint64_t value; |
516 | |
|
517 | 0 | FR_DBUFF_OUT_RETURN(&value, &work_dbuff); |
518 | 0 | *out = value; |
519 | 0 | goto done; |
520 | 0 | } |
521 | | |
522 | | /* |
523 | | * 28 and greater are invalid according to the RFCs. |
524 | | */ |
525 | 0 | if (info > CBOR_8_BYTE) return -1; |
526 | | |
527 | 0 | done: |
528 | 0 | return fr_dbuff_set(dbuff, &work_dbuff); |
529 | 0 | } |
530 | | |
531 | | static ssize_t cbor_decode_count(uint64_t *out, int expected, fr_dbuff_t *dbuff) |
532 | 0 | { |
533 | 0 | fr_dbuff_t work_dbuff = FR_DBUFF(dbuff); |
534 | 0 | uint8_t major, info; |
535 | 0 | ssize_t slen; |
536 | |
|
537 | 0 | FR_DBUFF_OUT_RETURN(&major, &work_dbuff); |
538 | | |
539 | 0 | info = major & 0x1f; |
540 | 0 | major >>= 5; |
541 | |
|
542 | 0 | if (major != expected) { |
543 | 0 | fr_strerror_printf("Expected cbor type '%s', got unexpected type %d ", |
544 | 0 | cbor_type_to_str[expected], major); |
545 | 0 | return -1; |
546 | 0 | } |
547 | | |
548 | 0 | slen = cbor_decode_integer(out, info, &work_dbuff); |
549 | 0 | if (slen < 0) return_slen; |
550 | | |
551 | 0 | return fr_dbuff_set(dbuff, &work_dbuff); |
552 | 0 | } |
553 | | |
554 | | typedef ssize_t (*cbor_decode_type_t)(TALLOC_CTX *ctx, fr_value_box_t *vb, fr_dbuff_t *dbuff); |
555 | | |
556 | | static ssize_t cbor_decode_octets_memcpy(uint8_t *dst, size_t dst_min, size_t dst_max, fr_dbuff_t *dbuff) |
557 | 0 | { |
558 | 0 | fr_dbuff_t work_dbuff = FR_DBUFF(dbuff); |
559 | 0 | ssize_t slen; |
560 | 0 | uint64_t value = 0; |
561 | |
|
562 | 0 | slen = cbor_decode_count(&value, CBOR_OCTETS, &work_dbuff); |
563 | 0 | if (slen < 0) return_slen; |
564 | | |
565 | 0 | if (value < dst_min) { |
566 | 0 | fr_strerror_printf("Invalid length for data - expected at least %zu got %" PRIu64, dst_min, value); |
567 | 0 | return -1; |
568 | 0 | } |
569 | | |
570 | 0 | if (value > dst_max) { |
571 | 0 | fr_strerror_printf("Invalid length for data - expected no more than %zu got %" PRIu64, dst_max, value); |
572 | 0 | return -1; |
573 | 0 | } |
574 | | |
575 | 0 | FR_DBUFF_OUT_MEMCPY_RETURN(dst, &work_dbuff, value); |
576 | | |
577 | 0 | return fr_dbuff_set(dbuff, &work_dbuff); |
578 | 0 | } |
579 | | |
580 | | #if 0 |
581 | | static ssize_t *cbor_decode_octets_memdup(TALLOC_CTX *ctx, uint8_t **out, fr_dbuff_t *dbuff) |
582 | | { |
583 | | fr_dbuff_t work_dbuff = FR_DBUFF(dbuff); |
584 | | ssize_t slen; |
585 | | uint64_t value; |
586 | | uint8_t *ptr; |
587 | | |
588 | | slen = cbor_decode_count(&value, CBOR_OCTETS, &work_dbuff); |
589 | | if (slen < 0) return_slen; |
590 | | |
591 | | if (value > (1 << 20)) { |
592 | | fr_strerror_printf("cbor data string is too long (%" PRIu64 ")", value); |
593 | | return -1; |
594 | | } |
595 | | |
596 | | ptr = talloc_array(ctx, uint8_t, value); |
597 | | if (!ptr) { |
598 | | fr_strerror_const("Out of memory"); |
599 | | return -1; |
600 | | } |
601 | | |
602 | | FR_DBUFF_OUT_MEMCPY_RETURN(ptr, &work_dbuff, value); |
603 | | *out = ptr; |
604 | | |
605 | | return fr_dbuff_set(dbuff, &work_dbuff); |
606 | | } |
607 | | #endif |
608 | | |
609 | | static ssize_t cbor_decode_ethernet(UNUSED TALLOC_CTX *ctx, fr_value_box_t *vb, fr_dbuff_t *dbuff) |
610 | 0 | { |
611 | 0 | return cbor_decode_octets_memcpy(vb->vb_ether, sizeof(vb->vb_ether), sizeof(vb->vb_ether), dbuff); |
612 | 0 | } |
613 | | |
614 | | static ssize_t cbor_decode_ipv4_addr(UNUSED TALLOC_CTX *ctx, fr_value_box_t *vb, fr_dbuff_t *dbuff) |
615 | 0 | { |
616 | 0 | fr_dbuff_t work_dbuff = FR_DBUFF(dbuff); |
617 | 0 | ssize_t slen; |
618 | 0 | uint8_t header; |
619 | 0 | size_t count = 0; |
620 | 0 | uint64_t value = 0; |
621 | |
|
622 | 0 | FR_DBUFF_EXTEND_LOWAT_OR_RETURN(&work_dbuff, 1); |
623 | | |
624 | 0 | header = *fr_dbuff_current(&work_dbuff); |
625 | 0 | if ((header >> 5) == CBOR_ARRAY) { |
626 | 0 | count = header & 0x1f; |
627 | |
|
628 | 0 | if ((count != 2) && (count != 3)) { |
629 | 0 | fr_strerror_printf("Invalid IPv4 interface - expected array of 2-3 elements, got %02x", |
630 | 0 | header); |
631 | 0 | return -1; |
632 | 0 | } |
633 | | |
634 | 0 | fr_dbuff_advance(&work_dbuff, 1); |
635 | 0 | } |
636 | | |
637 | 0 | vb->vb_ip.prefix = 32; |
638 | | |
639 | | /* |
640 | | * Get the IP address. |
641 | | */ |
642 | 0 | slen = cbor_decode_octets_memcpy((uint8_t *) &vb->vb_ipv4addr, |
643 | 0 | sizeof(vb->vb_ipv4addr), |
644 | 0 | sizeof(vb->vb_ipv4addr), &work_dbuff); |
645 | 0 | if (slen <= 0) return_slen; |
646 | | |
647 | 0 | if (!count) return fr_dbuff_set(dbuff, &work_dbuff); |
648 | | |
649 | 0 | slen = cbor_decode_count(&value, CBOR_INTEGER, &work_dbuff); |
650 | 0 | if (slen <= 0) return_slen; |
651 | | |
652 | 0 | if (value != 32) { |
653 | 0 | fr_strerror_printf("Invalid IPv4 address - expected prefix = 32 got %" PRIu64, value); |
654 | 0 | return -fr_dbuff_used(&work_dbuff); |
655 | 0 | } |
656 | | |
657 | 0 | if (count == 2) return fr_dbuff_set(dbuff, &work_dbuff); |
658 | | |
659 | | /* |
660 | | * Get the scope ID |
661 | | */ |
662 | 0 | slen = cbor_decode_count(&value, CBOR_INTEGER, &work_dbuff); |
663 | 0 | if (slen <= 0) return_slen; |
664 | | |
665 | 0 | vb->vb_ip.scope_id = value; |
666 | |
|
667 | 0 | return fr_dbuff_set(dbuff, &work_dbuff); |
668 | 0 | } |
669 | | |
670 | | static ssize_t cbor_decode_ipv6_addr(UNUSED TALLOC_CTX *ctx, fr_value_box_t *vb, fr_dbuff_t *dbuff) |
671 | 0 | { |
672 | 0 | fr_dbuff_t work_dbuff = FR_DBUFF(dbuff); |
673 | 0 | ssize_t slen; |
674 | 0 | uint8_t header; |
675 | 0 | size_t count = 0; |
676 | 0 | uint64_t value = 0; |
677 | |
|
678 | 0 | FR_DBUFF_EXTEND_LOWAT_OR_RETURN(&work_dbuff, 1); |
679 | | |
680 | 0 | header = *fr_dbuff_current(&work_dbuff); |
681 | 0 | if ((header >> 5) == CBOR_ARRAY) { |
682 | 0 | count = header & 0x1f; |
683 | |
|
684 | 0 | if ((count != 2) && (count != 3)) { |
685 | 0 | fr_strerror_printf("Invalid IPv4 interface - expected array of 2-3 elements, got %02x", |
686 | 0 | header); |
687 | 0 | return -1; |
688 | 0 | } |
689 | | |
690 | 0 | fr_dbuff_advance(&work_dbuff, 1); |
691 | 0 | } |
692 | | |
693 | 0 | vb->vb_ip.prefix = 128; |
694 | | |
695 | | /* |
696 | | * Get the IP address. |
697 | | */ |
698 | 0 | slen = cbor_decode_octets_memcpy((uint8_t *) &vb->vb_ipv6addr, |
699 | 0 | sizeof(vb->vb_ipv6addr), |
700 | 0 | sizeof(vb->vb_ipv6addr), &work_dbuff); |
701 | |
|
702 | 0 | if (slen <= 0) return_slen; |
703 | | |
704 | 0 | if (!count) return fr_dbuff_set(dbuff, &work_dbuff); |
705 | | |
706 | 0 | slen = cbor_decode_count(&value, CBOR_INTEGER, &work_dbuff); |
707 | 0 | if (slen <= 0) return_slen; |
708 | | |
709 | 0 | if (value != 128) { |
710 | 0 | fr_strerror_printf("Invalid IPv6 address - expected prefix = 128 got %" PRIu64, value); |
711 | 0 | return -fr_dbuff_used(&work_dbuff); |
712 | 0 | } |
713 | | |
714 | 0 | vb->vb_ip.prefix = value; |
715 | |
|
716 | 0 | if (count == 2) return fr_dbuff_set(dbuff, &work_dbuff); |
717 | | |
718 | | /* |
719 | | * Get the scope ID |
720 | | */ |
721 | 0 | slen = cbor_decode_count(&value, CBOR_INTEGER, &work_dbuff); |
722 | 0 | if (slen <= 0) return_slen; |
723 | | |
724 | 0 | vb->vb_ip.scope_id = value; |
725 | |
|
726 | 0 | return fr_dbuff_set(dbuff, &work_dbuff); |
727 | 0 | } |
728 | | |
729 | | static ssize_t cbor_decode_ipv4_prefix(UNUSED TALLOC_CTX *ctx, fr_value_box_t *vb, fr_dbuff_t *dbuff) |
730 | 0 | { |
731 | 0 | fr_dbuff_t work_dbuff = FR_DBUFF(dbuff); |
732 | 0 | ssize_t slen; |
733 | 0 | uint8_t header; |
734 | 0 | uint64_t value = 0; |
735 | 0 | uint8_t buffer[sizeof(vb->vb_ipv4addr)]; |
736 | |
|
737 | 0 | FR_DBUFF_OUT_RETURN(&header, &work_dbuff); |
738 | | |
739 | 0 | if (header != ((CBOR_ARRAY << 5) | 2)) { |
740 | 0 | fr_strerror_printf("Invalid IPv4 prefix - expected array of 2 elements, got %02x", |
741 | 0 | header); |
742 | 0 | return -1; |
743 | 0 | } |
744 | | |
745 | 0 | slen = cbor_decode_count(&value, CBOR_INTEGER, &work_dbuff); |
746 | 0 | if (slen <= 0) return_slen; |
747 | | |
748 | 0 | if (value > 32) { |
749 | 0 | fr_strerror_printf("Invalid IPv4 prefix - expected prefix < 32, got %" PRIu64, value); |
750 | 0 | return -1; |
751 | 0 | } |
752 | | |
753 | | /* |
754 | | * RFC 9164 Section 4.3 - Trailing bytes of zero are omitted, so we |
755 | | * first copy the data to a fixed-sized buffer which was |
756 | | * zeroed out, and then (@todo) also check that unused bits in |
757 | | * the last byte are all zero. |
758 | | */ |
759 | 0 | memset(buffer, 0, sizeof(buffer)); |
760 | |
|
761 | 0 | slen = cbor_decode_octets_memcpy(buffer, 0, sizeof(buffer), &work_dbuff); |
762 | 0 | if (slen <= 0) return_slen; |
763 | | |
764 | 0 | memcpy((uint8_t *) &vb->vb_ipv4addr, buffer, sizeof(vb->vb_ipv4addr)); |
765 | 0 | vb->vb_ip.prefix = value; |
766 | |
|
767 | 0 | return fr_dbuff_set(dbuff, &work_dbuff); |
768 | 0 | } |
769 | | |
770 | | static ssize_t cbor_decode_ipv6_prefix(UNUSED TALLOC_CTX *ctx, fr_value_box_t *vb, fr_dbuff_t *dbuff) |
771 | 0 | { |
772 | 0 | fr_dbuff_t work_dbuff = FR_DBUFF(dbuff); |
773 | 0 | ssize_t slen; |
774 | 0 | uint8_t header; |
775 | 0 | uint64_t value = 0; |
776 | 0 | uint8_t buffer[sizeof(vb->vb_ipv6addr)]; |
777 | |
|
778 | 0 | FR_DBUFF_OUT_RETURN(&header, &work_dbuff); |
779 | | |
780 | 0 | if (header != ((CBOR_ARRAY << 5) | 2)) { |
781 | 0 | fr_strerror_printf("Invalid IPv6 prefix - expected array of 2 elements, got %02x", |
782 | 0 | header); |
783 | 0 | return -1; |
784 | 0 | } |
785 | | |
786 | 0 | slen = cbor_decode_count(&value, CBOR_INTEGER, &work_dbuff); |
787 | 0 | if (slen <= 0) return_slen; |
788 | | |
789 | 0 | if (value > 128) { |
790 | 0 | fr_strerror_printf("Invalid IPv6 prefix - expected prefix < 128, got %" PRIu64, value); |
791 | 0 | return -1; |
792 | 0 | } |
793 | | |
794 | | /* |
795 | | * RFC 9164 Section 4.3 - Trailing bytes of zero are omitted, so we |
796 | | * first copy the data to a fixed-sized buffer which was |
797 | | * zeroed out, and then (@todo) also check that unused bits in |
798 | | * the last byte are all zero. |
799 | | */ |
800 | 0 | memset(buffer, 0, sizeof(buffer)); |
801 | |
|
802 | 0 | slen = cbor_decode_octets_memcpy(buffer, 0, sizeof(buffer), &work_dbuff); |
803 | 0 | if (slen <= 0) return_slen; |
804 | | |
805 | 0 | memcpy((uint8_t *) &vb->vb_ipv6addr, buffer, sizeof(vb->vb_ipv6addr)); |
806 | 0 | vb->vb_ip.prefix = value; |
807 | |
|
808 | 0 | return fr_dbuff_set(dbuff, &work_dbuff); |
809 | 0 | } |
810 | | |
811 | | static ssize_t cbor_decode_int64(int64_t *out, fr_dbuff_t *dbuff, fr_type_t type) |
812 | 0 | { |
813 | 0 | fr_dbuff_t work_dbuff = FR_DBUFF(dbuff); |
814 | 0 | ssize_t slen; |
815 | 0 | uint8_t major, info; |
816 | 0 | uint64_t value = 0; |
817 | 0 | int64_t neg; |
818 | |
|
819 | 0 | FR_DBUFF_OUT_RETURN(&major, &work_dbuff); |
820 | | |
821 | 0 | info = major & 0x1f; |
822 | 0 | major >>= 5; |
823 | |
|
824 | 0 | switch (major) { |
825 | 0 | case CBOR_INTEGER: |
826 | 0 | slen = cbor_decode_integer(&value, info, &work_dbuff); |
827 | 0 | if (slen < 0) return_slen; |
828 | | |
829 | 0 | if (value >= ((uint64_t) 1) << 63) { /* equal! */ |
830 | 0 | invalid: |
831 | 0 | fr_strerror_printf("cbor value is too large for output data type %s", |
832 | 0 | fr_type_to_str(type)); |
833 | 0 | return -1; |
834 | 0 | } |
835 | | |
836 | 0 | *out = value; |
837 | 0 | break; |
838 | | |
839 | 0 | case CBOR_NEGATIVE: |
840 | 0 | slen = cbor_decode_integer(&value, info, &work_dbuff); |
841 | 0 | if (slen < 0) return_slen; |
842 | | |
843 | 0 | if (value >= ((uint64_t) 1) << 63) goto invalid; /* greater than! */ |
844 | | |
845 | | /* |
846 | | * Convert 0..(2^63-1) into -0..-(2^63-1) |
847 | | * then conver to -1..-(2^63) |
848 | | */ |
849 | 0 | neg = -value; |
850 | 0 | neg--; |
851 | |
|
852 | 0 | *out = neg; |
853 | 0 | break; |
854 | | |
855 | 0 | default: |
856 | 0 | fr_strerror_printf("cbor data contains invalid content %d for expected data type %s", |
857 | 0 | major, fr_type_to_str(type)); |
858 | 0 | return -1; |
859 | 0 | } |
860 | | |
861 | 0 | return fr_dbuff_set(dbuff, &work_dbuff); |
862 | |
|
863 | 0 | } |
864 | | |
865 | | static ssize_t cbor_decode_date(UNUSED TALLOC_CTX *ctx, fr_value_box_t *vb, fr_dbuff_t *dbuff) |
866 | 0 | { |
867 | 0 | fr_dbuff_t work_dbuff = FR_DBUFF(dbuff); |
868 | 0 | ssize_t slen; |
869 | 0 | int64_t neg; |
870 | |
|
871 | 0 | slen = cbor_decode_int64(&neg, &work_dbuff, FR_TYPE_DATE); |
872 | 0 | if (slen <= 0) return_slen; |
873 | | |
874 | 0 | vb->vb_date = fr_unix_time_from_sec(neg); |
875 | |
|
876 | 0 | return fr_dbuff_set(dbuff, &work_dbuff); |
877 | 0 | } |
878 | | |
879 | | /* |
880 | | * Tag 1002, followed by map of at least 2 elements |
881 | | * key 1: seconds |
882 | | * key -9: nanoseconds |
883 | | */ |
884 | | static ssize_t cbor_decode_time_delta(UNUSED TALLOC_CTX *ctx, fr_value_box_t *vb, fr_dbuff_t *dbuff) |
885 | 0 | { |
886 | 0 | fr_dbuff_t work_dbuff = FR_DBUFF(dbuff); |
887 | 0 | uint64_t count; |
888 | 0 | ssize_t slen; |
889 | 0 | int64_t key, seconds, fraction, scale; |
890 | |
|
891 | 0 | slen = cbor_decode_count(&count, CBOR_MAP, &work_dbuff); |
892 | 0 | if (slen < 0) return_slen; |
893 | | |
894 | 0 | if (!count || (count > 2)) { |
895 | 0 | fr_strerror_printf("Unexpected count %" PRIu64" for time_delta, expected map of 1-2 elements", count); |
896 | 0 | return -1; |
897 | 0 | } |
898 | | |
899 | 0 | key = seconds = fraction = 0; |
900 | | |
901 | | /* |
902 | | * Expect key 1:seconds |
903 | | */ |
904 | 0 | slen = cbor_decode_int64(&key, &work_dbuff, FR_TYPE_TIME_DELTA); |
905 | 0 | if (slen < 0) return_slen; |
906 | | |
907 | 0 | if (key != 1) { |
908 | 0 | fr_strerror_printf("Unexpected key %" PRIi64 " for time_delta, expected key 1", key); |
909 | 0 | return -1; |
910 | 0 | } |
911 | | |
912 | 0 | slen = cbor_decode_int64(&seconds, &work_dbuff, FR_TYPE_TIME_DELTA); |
913 | 0 | if (slen < 0) return_slen; |
914 | | |
915 | 0 | if (count > 1) { |
916 | 0 | slen = cbor_decode_int64(&key, &work_dbuff, FR_TYPE_TIME_DELTA); |
917 | 0 | if (slen < 0) return_slen; |
918 | | |
919 | 0 | switch (key) { |
920 | 0 | case -3: |
921 | 0 | scale = MSEC; |
922 | 0 | break; |
923 | | |
924 | 0 | case -6: |
925 | 0 | scale = USEC; |
926 | 0 | break; |
927 | | |
928 | 0 | case -9: |
929 | 0 | scale = NSEC; |
930 | 0 | break; |
931 | | |
932 | 0 | default: |
933 | 0 | fr_strerror_printf("Unsupported time_delta key %" PRIi64, key); |
934 | 0 | return -fr_dbuff_used(&work_dbuff); /* point to actual key? */ |
935 | |
|
936 | 0 | } |
937 | | |
938 | 0 | slen = cbor_decode_int64(&fraction, &work_dbuff, FR_TYPE_TIME_DELTA); |
939 | 0 | if (slen < 0) return_slen; |
940 | | |
941 | 0 | if ((fraction < 0) || (fraction > scale)) fraction = 0; |
942 | 0 | } else { |
943 | 0 | scale = NSEC; |
944 | 0 | fraction = 0; |
945 | 0 | } |
946 | | |
947 | 0 | if (seconds > (INT64_MAX / scale)) { |
948 | 0 | vb->vb_time_delta = fr_time_delta_max(); |
949 | |
|
950 | 0 | } else if (seconds < (INT64_MIN / scale)) { |
951 | 0 | vb->vb_time_delta = fr_time_delta_min(); |
952 | |
|
953 | 0 | } else { |
954 | 0 | seconds *= scale; |
955 | |
|
956 | 0 | if (seconds < 0) { |
957 | 0 | seconds -= fraction; |
958 | 0 | } else { |
959 | 0 | seconds += fraction; |
960 | 0 | } |
961 | |
|
962 | 0 | vb->vb_time_delta = fr_time_delta_wrap(seconds); |
963 | 0 | } |
964 | |
|
965 | 0 | return fr_dbuff_set(dbuff, &work_dbuff); |
966 | 0 | } |
967 | | |
968 | | |
969 | | static cbor_decode_type_t cbor_decode_type[FR_TYPE_MAX] = { |
970 | | [FR_TYPE_ETHERNET] = cbor_decode_ethernet, |
971 | | |
972 | | [FR_TYPE_DATE] = cbor_decode_date, |
973 | | [FR_TYPE_TIME_DELTA] = cbor_decode_time_delta, |
974 | | |
975 | | [FR_TYPE_IPV4_ADDR] = cbor_decode_ipv4_addr, |
976 | | [FR_TYPE_IPV6_ADDR] = cbor_decode_ipv6_addr, |
977 | | |
978 | | [FR_TYPE_IPV4_PREFIX] = cbor_decode_ipv4_prefix, |
979 | | [FR_TYPE_IPV6_PREFIX] = cbor_decode_ipv6_prefix, |
980 | | }; |
981 | | |
982 | | /* |
983 | | * @todo - fr_cbor_encode_pair_list(). And then if we have da->flags.array, we encode the _value_ as an |
984 | | * array of indeterminate length. This is a little bit of a special case, but not terrible. |
985 | | */ |
986 | | ssize_t fr_cbor_decode_value_box(TALLOC_CTX *ctx, fr_value_box_t *vb, fr_dbuff_t *dbuff, |
987 | | fr_type_t type, fr_dict_attr_t const *enumv, bool tainted) |
988 | 0 | { |
989 | 0 | fr_dbuff_t work_dbuff = FR_DBUFF(dbuff); |
990 | 0 | bool indefinite; |
991 | 0 | uint8_t major, info; |
992 | 0 | ssize_t slen; |
993 | 0 | int64_t neg; |
994 | 0 | uint64_t value; |
995 | 0 | uint8_t *ptr; |
996 | |
|
997 | 0 | FR_DBUFF_OUT_RETURN(&major, &work_dbuff); |
998 | | |
999 | 0 | if (type == FR_TYPE_NULL) { |
1000 | 0 | type = cbor_guess_type(&work_dbuff, false); |
1001 | 0 | if (type == FR_TYPE_NULL) { |
1002 | 0 | fr_strerror_const("Unable to determine data type from cbor"); |
1003 | 0 | return -1; |
1004 | 0 | } |
1005 | 0 | } |
1006 | | |
1007 | 0 | fr_value_box_init(vb, type, enumv, tainted); |
1008 | |
|
1009 | 0 | info = major & 0x1f; |
1010 | 0 | major >>= 5; |
1011 | | |
1012 | | /* |
1013 | | * Invalid combinations. |
1014 | | */ |
1015 | 0 | if (((info >= 28) && (info <= 30)) || |
1016 | 0 | ((info == 31) && ((major == 0) || (major == 1) || (major == 6)))) { |
1017 | 0 | fr_strerror_const("Invalid cbor data - input is not 'well formed'"); |
1018 | 0 | return -1; |
1019 | 0 | } |
1020 | | |
1021 | 0 | switch (major) { |
1022 | 0 | case CBOR_STRING: |
1023 | 0 | if (type != FR_TYPE_STRING) { |
1024 | 0 | mismatch: |
1025 | 0 | fr_strerror_printf("cbor data contains invalid content %d for expected data type %s", |
1026 | 0 | major, fr_type_to_str(type)); |
1027 | 0 | return -1; |
1028 | 0 | } |
1029 | | |
1030 | 0 | if (info == 31) { |
1031 | 0 | no_chunks: |
1032 | 0 | fr_strerror_const("Chunked strings are not supported"); |
1033 | 0 | return -1; |
1034 | 0 | } |
1035 | | |
1036 | | |
1037 | | /* |
1038 | | * @todo - undefinite length strings. Which are really "chunked" strings. |
1039 | | */ |
1040 | 0 | slen = cbor_decode_integer(&value, info, &work_dbuff); |
1041 | 0 | if (slen < 0) return_slen; |
1042 | | |
1043 | | /* |
1044 | | * A little bit of sanity check. |
1045 | | */ |
1046 | 0 | if (value > (1 << 20)) { |
1047 | 0 | fr_strerror_printf("cbor data string is too long (%" PRIu64 ")", value); |
1048 | 0 | return -1; |
1049 | 0 | } |
1050 | | |
1051 | 0 | ptr = talloc_array(ctx, uint8_t, value + 1); |
1052 | 0 | if (!ptr) { |
1053 | 0 | fr_strerror_const("Out of memory"); |
1054 | 0 | return -1; |
1055 | 0 | } |
1056 | 0 | talloc_set_type(ptr, char); |
1057 | 0 | if (value) FR_DBUFF_OUT_MEMCPY_RETURN(ptr, &work_dbuff, value); |
1058 | 0 | ptr[value] = '\0'; |
1059 | |
|
1060 | 0 | fr_value_box_strdup_shallow(vb, NULL, (char const *) ptr, tainted); |
1061 | |
|
1062 | 0 | break; |
1063 | | |
1064 | 0 | case CBOR_OCTETS: |
1065 | 0 | if (type != FR_TYPE_OCTETS) goto mismatch; |
1066 | | |
1067 | 0 | if (info == 31) goto no_chunks; |
1068 | | |
1069 | | /* |
1070 | | * @todo - indefinite length octet strings. Which are really "chunked" octet strings. |
1071 | | */ |
1072 | 0 | slen = cbor_decode_integer(&value, info, &work_dbuff); |
1073 | 0 | if (slen < 0) return_slen; |
1074 | | |
1075 | | /* |
1076 | | * A little bit of sanity check. |
1077 | | */ |
1078 | 0 | if (value > (1 << 20)) { |
1079 | 0 | fr_strerror_printf("cbor data string is too long (%" PRIu64 ")", value); |
1080 | 0 | return -1; |
1081 | 0 | } |
1082 | | |
1083 | 0 | ptr = talloc_array(ctx, uint8_t, value); |
1084 | 0 | if (!ptr) { |
1085 | 0 | fr_strerror_const("Out of memory"); |
1086 | 0 | return -1; |
1087 | 0 | } |
1088 | | |
1089 | 0 | fr_value_box_memdup_shallow(vb, NULL, (uint8_t const *) ptr, value, false); /* tainted? */ |
1090 | |
|
1091 | 0 | if (value) FR_DBUFF_OUT_MEMCPY_RETURN(ptr, &work_dbuff, value); |
1092 | 0 | break; |
1093 | | |
1094 | 0 | case CBOR_INTEGER: |
1095 | 0 | slen = cbor_decode_integer(&value, info, &work_dbuff); |
1096 | 0 | if (slen < 0) return_slen; |
1097 | | |
1098 | 0 | switch (type) { |
1099 | 0 | case FR_TYPE_BOOL: |
1100 | 0 | if (value > 1) goto invalid_bool; |
1101 | 0 | vb->vb_bool = value; |
1102 | 0 | break; |
1103 | | |
1104 | 0 | case FR_TYPE_UINT8: |
1105 | 0 | if (value > UINT8_MAX) { |
1106 | 0 | invalid: |
1107 | 0 | fr_strerror_printf("cbor value is too large for output data type %s", |
1108 | 0 | fr_type_to_str(type)); |
1109 | 0 | return -1; |
1110 | 0 | } |
1111 | 0 | vb->vb_uint8 = value; |
1112 | 0 | break; |
1113 | | |
1114 | 0 | case FR_TYPE_UINT16: |
1115 | 0 | if (value > UINT16_MAX) goto invalid; |
1116 | 0 | vb->vb_uint16 = value; |
1117 | 0 | break; |
1118 | | |
1119 | 0 | case FR_TYPE_UINT32: |
1120 | 0 | if (value > UINT32_MAX) goto invalid; |
1121 | 0 | vb->vb_uint32 = value; |
1122 | 0 | break; |
1123 | | |
1124 | 0 | case FR_TYPE_UINT64: |
1125 | 0 | vb->vb_uint64 = value; |
1126 | 0 | break; |
1127 | | |
1128 | 0 | case FR_TYPE_INT8: |
1129 | 0 | if (value > INT8_MAX) goto invalid; |
1130 | 0 | vb->vb_int8 = value; |
1131 | 0 | break; |
1132 | | |
1133 | 0 | case FR_TYPE_INT16: |
1134 | 0 | if (value > INT16_MAX) goto invalid; |
1135 | 0 | vb->vb_int16 = value; |
1136 | 0 | break; |
1137 | | |
1138 | 0 | case FR_TYPE_INT32: |
1139 | 0 | if (value > INT32_MAX) goto invalid; |
1140 | 0 | vb->vb_int32 = value; |
1141 | 0 | break; |
1142 | | |
1143 | 0 | case FR_TYPE_INT64: |
1144 | 0 | if (value > INT64_MAX) goto invalid; |
1145 | 0 | vb->vb_int64 = value; |
1146 | 0 | break; |
1147 | | |
1148 | 0 | default: |
1149 | 0 | integer_type_mismatch: |
1150 | 0 | fr_strerror_printf("Unexpected cbor type 'integer' when decoding data type %s", |
1151 | 0 | fr_type_to_str(type)); |
1152 | 0 | return -1; |
1153 | 0 | } |
1154 | 0 | break; |
1155 | | |
1156 | 0 | case CBOR_NEGATIVE: |
1157 | 0 | slen = cbor_decode_integer(&value, info, &work_dbuff); |
1158 | 0 | if (slen < 0) return_slen; |
1159 | | |
1160 | | /* |
1161 | | * Signed numbers only go down to -2^63 |
1162 | | * so value must be less than 2^63 |
1163 | | */ |
1164 | 0 | if (value >= ((uint64_t) 1) << 63) goto invalid; |
1165 | | |
1166 | | /* |
1167 | | * Convert 0..(2^63-1) into -0..-(2^63-1) |
1168 | | * then conver to -1..-(2^63) |
1169 | | */ |
1170 | 0 | neg = -value; |
1171 | 0 | neg--; |
1172 | |
|
1173 | 0 | switch (type) { |
1174 | 0 | case FR_TYPE_INT8: |
1175 | 0 | if (neg < INT8_MIN) goto invalid; |
1176 | 0 | vb->vb_int8 = neg; |
1177 | 0 | break; |
1178 | | |
1179 | 0 | case FR_TYPE_INT16: |
1180 | 0 | if (neg < INT16_MIN) goto invalid; |
1181 | 0 | vb->vb_int16 = neg; |
1182 | 0 | break; |
1183 | | |
1184 | 0 | case FR_TYPE_INT32: |
1185 | 0 | if (neg < INT32_MIN) goto invalid; |
1186 | 0 | vb->vb_int32 = neg; |
1187 | 0 | break; |
1188 | | |
1189 | 0 | case FR_TYPE_INT64: |
1190 | 0 | vb->vb_int64 = neg; |
1191 | 0 | break; |
1192 | | |
1193 | 0 | default: |
1194 | 0 | goto integer_type_mismatch; |
1195 | 0 | } |
1196 | 0 | break; |
1197 | | |
1198 | 0 | case CBOR_FLOAT: |
1199 | | /* |
1200 | | * Simple values. See RFC 8489 Section 3.3. |
1201 | | * |
1202 | | * 20 - false |
1203 | | * 21 - true |
1204 | | * 22 - NULL |
1205 | | */ |
1206 | 0 | if (info < 24) { |
1207 | 0 | switch (type) { |
1208 | 0 | case FR_TYPE_BOOL: |
1209 | 0 | if (info == 20) { |
1210 | 0 | vb->vb_bool = false; |
1211 | 0 | break; |
1212 | 0 | } |
1213 | | |
1214 | 0 | if (info == 21) { |
1215 | 0 | vb->vb_bool = true; |
1216 | 0 | break; |
1217 | 0 | } |
1218 | | |
1219 | 0 | invalid_bool: |
1220 | 0 | fr_strerror_printf("Invalid cbor - boolean is not encoded as 'true' or 'false'"); |
1221 | 0 | return -fr_dbuff_used(&work_dbuff); |
1222 | | |
1223 | 0 | case FR_TYPE_OCTETS: |
1224 | 0 | case FR_TYPE_STRING: |
1225 | 0 | case FR_TYPE_TLV: |
1226 | 0 | case FR_TYPE_VENDOR: |
1227 | 0 | case FR_TYPE_GROUP: |
1228 | 0 | case FR_TYPE_STRUCT: |
1229 | | /* |
1230 | | * Be a little forgiving. 22 is NULL, so we treat that as "nothing". |
1231 | | * |
1232 | | * i.e. empty string, empty set, etc. |
1233 | | */ |
1234 | 0 | if (info == 22) break; |
1235 | | |
1236 | 0 | FALL_THROUGH; |
1237 | |
|
1238 | 0 | default: |
1239 | 0 | fr_strerror_printf("Invalid cbor - unexpected 'simple value' %u", info); |
1240 | 0 | return -fr_dbuff_used(&work_dbuff); |
1241 | 0 | } |
1242 | 0 | break; |
1243 | 0 | } |
1244 | | |
1245 | | /* |
1246 | | * Or as one-byte integers. |
1247 | | */ |
1248 | 0 | if (info == CBOR_1_BYTE) { |
1249 | 0 | uint8_t data; |
1250 | |
|
1251 | 0 | FR_DBUFF_OUT_RETURN(&data, &work_dbuff); |
1252 | | |
1253 | 0 | switch (type) { |
1254 | 0 | case FR_TYPE_FLOAT32: |
1255 | 0 | vb->vb_float32 = data; |
1256 | 0 | break; |
1257 | | |
1258 | 0 | case FR_TYPE_FLOAT64: |
1259 | 0 | vb->vb_float64 = data; |
1260 | 0 | break; |
1261 | | |
1262 | 0 | default: |
1263 | 0 | float_type_mismatch: |
1264 | 0 | fr_strerror_printf("Unexpected cbor type 'float' when decoding data type %s", |
1265 | 0 | fr_type_to_str(type)); |
1266 | 0 | return -1; |
1267 | 0 | } |
1268 | | |
1269 | 0 | break; |
1270 | 0 | } |
1271 | | |
1272 | | /* |
1273 | | * We don't support float16 |
1274 | | */ |
1275 | | |
1276 | 0 | if (info == CBOR_4_BYTE) { |
1277 | 0 | float data; |
1278 | |
|
1279 | 0 | FR_DBUFF_OUT_RETURN(&data, &work_dbuff); |
1280 | | |
1281 | 0 | switch (type) { |
1282 | 0 | case FR_TYPE_FLOAT32: |
1283 | 0 | vb->vb_float32 = data; |
1284 | 0 | break; |
1285 | | |
1286 | 0 | case FR_TYPE_FLOAT64: |
1287 | 0 | vb->vb_float64 = (double) data; |
1288 | 0 | break; |
1289 | | |
1290 | 0 | default: |
1291 | 0 | goto float_type_mismatch; |
1292 | 0 | } |
1293 | | |
1294 | 0 | break; |
1295 | 0 | } |
1296 | | |
1297 | 0 | if (info == CBOR_8_BYTE) { |
1298 | 0 | double data; |
1299 | |
|
1300 | 0 | FR_DBUFF_OUT_RETURN(&data, &work_dbuff); |
1301 | | |
1302 | 0 | switch (type) { |
1303 | 0 | case FR_TYPE_FLOAT32: |
1304 | 0 | vb->vb_float32 = data; /* maybe loses precision? */ |
1305 | 0 | break; |
1306 | | |
1307 | 0 | case FR_TYPE_FLOAT64: |
1308 | 0 | vb->vb_float64 = data; |
1309 | 0 | break; |
1310 | | |
1311 | 0 | default: |
1312 | 0 | goto float_type_mismatch; |
1313 | 0 | } |
1314 | | |
1315 | 0 | break; |
1316 | 0 | } |
1317 | | |
1318 | | /* |
1319 | | * 24 is FLOAT16, which we don't support. |
1320 | | * 31 is BREAK, which the caller should have checked for. |
1321 | | */ |
1322 | 0 | goto float_type_mismatch; |
1323 | | |
1324 | 0 | case CBOR_TAG: |
1325 | | /* |
1326 | | * We only support a limited number of tags. |
1327 | | */ |
1328 | 0 | slen = cbor_decode_integer(&value, info, &work_dbuff); |
1329 | 0 | if (slen < 0) return_slen; |
1330 | | |
1331 | | /* |
1332 | | * No tag defined for this data type, that's on us. |
1333 | | */ |
1334 | 0 | if (!cbor_type_to_tag[type]) { |
1335 | 0 | fr_strerror_printf("Unknown cbor tag %" PRIu64 " for expected data type %s", |
1336 | 0 | value, fr_type_to_str(type)); |
1337 | 0 | return -fr_dbuff_used(&work_dbuff); |
1338 | 0 | } |
1339 | | |
1340 | | /* |
1341 | | * Wrong tag for this data type, that's on them. |
1342 | | */ |
1343 | 0 | if (cbor_type_to_tag[type] != value) { |
1344 | 0 | fr_strerror_printf("Invalid cbor tag %" PRIu64 " for expected data type %s", |
1345 | 0 | value, fr_type_to_str(type)); |
1346 | 0 | return -fr_dbuff_used(&work_dbuff); |
1347 | 0 | } |
1348 | | |
1349 | 0 | fr_value_box_init(vb, type, enumv, tainted); |
1350 | |
|
1351 | 0 | slen = cbor_decode_type[type](ctx, vb, &work_dbuff); |
1352 | 0 | if (slen < 0) return_slen; |
1353 | 0 | break; |
1354 | | |
1355 | 0 | case CBOR_ARRAY: |
1356 | 0 | if (type != FR_TYPE_GROUP) goto invalid_type; |
1357 | | |
1358 | | /* |
1359 | | * Loop until done. |
1360 | | */ |
1361 | 0 | if (info == 31) { |
1362 | 0 | value = ~0; |
1363 | 0 | indefinite = true; |
1364 | |
|
1365 | 0 | } else { |
1366 | 0 | slen = cbor_decode_integer(&value, info, &work_dbuff); |
1367 | 0 | if (slen < 0) return_slen; |
1368 | | |
1369 | 0 | indefinite = false; |
1370 | 0 | } |
1371 | | |
1372 | | #ifdef STATIC_ANALYZER |
1373 | | if (value > fr_dbuff_remaining(&work_dbuff)) return -1; |
1374 | | #endif |
1375 | | |
1376 | | /* |
1377 | | * Loop until we decode everything. For simplicity, we handle indefinite and definite |
1378 | | * length arrays in the same loop. |
1379 | | */ |
1380 | 0 | for (/* nothing */; value > 0; value--) { |
1381 | 0 | uint8_t header; |
1382 | 0 | fr_value_box_t *child; |
1383 | | |
1384 | | /* |
1385 | | * Require at least one byte in the buffer. |
1386 | | */ |
1387 | 0 | FR_DBUFF_EXTEND_LOWAT_OR_RETURN(&work_dbuff, 1); |
1388 | | |
1389 | | /* |
1390 | | * Peek ahead for a break. |
1391 | | */ |
1392 | 0 | header = *fr_dbuff_current(&work_dbuff); |
1393 | 0 | if (header == 0xff) { |
1394 | 0 | if (!indefinite) { |
1395 | 0 | fr_strerror_const("Unexpected 'break' found in cbor data"); |
1396 | 0 | return -fr_dbuff_used(&work_dbuff); |
1397 | 0 | } |
1398 | | |
1399 | | /* |
1400 | | * Done! |
1401 | | */ |
1402 | 0 | fr_dbuff_advance(&work_dbuff, 1); |
1403 | 0 | break; |
1404 | 0 | } |
1405 | | |
1406 | 0 | child = fr_value_box_alloc(ctx, FR_TYPE_NULL, NULL); |
1407 | 0 | if (!child) { |
1408 | 0 | fr_strerror_const("Out of memory"); |
1409 | 0 | return -fr_dbuff_used(&work_dbuff); |
1410 | 0 | } |
1411 | | |
1412 | | /* |
1413 | | * We have to decode at least one value. |
1414 | | */ |
1415 | 0 | slen = fr_cbor_decode_value_box(child, child, &work_dbuff, FR_TYPE_NULL, NULL, tainted); |
1416 | 0 | if (slen <= 0) { |
1417 | 0 | talloc_free(child); |
1418 | 0 | return_slen; |
1419 | 0 | } |
1420 | | |
1421 | 0 | fr_value_box_list_insert_tail(&vb->vb_group, child); |
1422 | 0 | } |
1423 | 0 | break; |
1424 | | |
1425 | | /* |
1426 | | * These are not value-box types. |
1427 | | */ |
1428 | 0 | case CBOR_MAP: |
1429 | 0 | invalid_type: |
1430 | 0 | fr_strerror_printf("Invalid data type %s for cbor to value-box", fr_type_to_str(type)); |
1431 | 0 | return -1; |
1432 | 0 | } |
1433 | | |
1434 | 0 | return fr_dbuff_set(dbuff, &work_dbuff); |
1435 | 0 | } |
1436 | | |
1437 | | /** Encode a pair |
1438 | | * |
1439 | | */ |
1440 | | ssize_t fr_cbor_encode_pair(fr_dbuff_t *dbuff, fr_pair_t *vp) |
1441 | 0 | { |
1442 | 0 | fr_dbuff_t work_dbuff = FR_DBUFF(dbuff); |
1443 | 0 | ssize_t slen; |
1444 | 0 | fr_dict_attr_t const *parent; |
1445 | 0 | size_t count; |
1446 | |
|
1447 | 0 | FR_DBUFF_IN_BYTES_RETURN(&work_dbuff, (uint8_t) ((CBOR_MAP << 5) | 1)); /* map of 1 item */ |
1448 | | |
1449 | | /* |
1450 | | * Key is the attribute number. |
1451 | | */ |
1452 | 0 | slen = cbor_encode_integer(&work_dbuff, CBOR_INTEGER, vp->da->attr); |
1453 | 0 | if (slen <= 0) return_slen; |
1454 | | |
1455 | | /* |
1456 | | * Value is the actual value of the leaf, or the array of children. |
1457 | | */ |
1458 | 0 | switch (vp->vp_type) { |
1459 | 0 | case FR_TYPE_LEAF: |
1460 | 0 | slen = fr_cbor_encode_value_box(&work_dbuff, &vp->data); |
1461 | 0 | if (slen <= 0) return_slen; |
1462 | 0 | break; |
1463 | | |
1464 | | /* |
1465 | | * Groups reparent to the ref. |
1466 | | */ |
1467 | 0 | case FR_TYPE_GROUP: |
1468 | 0 | parent = fr_dict_attr_ref(vp->da); |
1469 | 0 | fr_assert(parent != NULL); |
1470 | 0 | goto encode_children; |
1471 | | |
1472 | | /* |
1473 | | * The only difference between TLV and VSA is that the children of VSA are all VENDORs. |
1474 | | */ |
1475 | 0 | case FR_TYPE_VENDOR: |
1476 | 0 | case FR_TYPE_VSA: |
1477 | 0 | case FR_TYPE_TLV: |
1478 | 0 | parent = vp->da; |
1479 | | |
1480 | | /* |
1481 | | * The value is array(children) |
1482 | | */ |
1483 | 0 | encode_children: |
1484 | 0 | if (fr_pair_list_num_elements(&vp->vp_group) == 0) { |
1485 | 0 | FR_DBUFF_IN_BYTES_RETURN(&work_dbuff, (uint8_t) ((CBOR_FLOAT << 5) | 22)); /* NULL */ |
1486 | 0 | break; |
1487 | 0 | } |
1488 | | |
1489 | | /* |
1490 | | * The groups, etc. may contain internal attributes. We don't yet deal with those. |
1491 | | */ |
1492 | 0 | count = 0; |
1493 | 0 | fr_pair_list_foreach(&vp->vp_group, child) { |
1494 | 0 | if (child->da->parent != parent) continue; |
1495 | 0 | count++; |
1496 | 0 | } |
1497 | |
|
1498 | 0 | slen = cbor_encode_integer(&work_dbuff, CBOR_ARRAY, count); |
1499 | 0 | if (slen <= 0) return_slen; |
1500 | | |
1501 | 0 | fr_pair_list_foreach(&vp->vp_group, child) { |
1502 | | /* |
1503 | | * We don't allow changing dictionaries here. |
1504 | | */ |
1505 | 0 | if (child->da->parent != parent) continue; |
1506 | | |
1507 | 0 | slen = fr_cbor_encode_pair(&work_dbuff, child); |
1508 | 0 | if (slen <= 0) return_slen; |
1509 | 0 | } |
1510 | 0 | break; |
1511 | | |
1512 | | /* |
1513 | | * @todo - struct, except if we hit the end of the struct, check if the next child is the child |
1514 | | * of the key? That makes it all more annoying :( |
1515 | | */ |
1516 | | |
1517 | 0 | default: |
1518 | 0 | fr_strerror_printf("Invalid data type %s for cbor encoding", fr_type_to_str(vp->vp_type)); |
1519 | 0 | return -1; |
1520 | 0 | } |
1521 | | |
1522 | 0 | return fr_dbuff_set(dbuff, &work_dbuff); |
1523 | 0 | } |
1524 | | |
1525 | | /** Guess the data type of the CBOR data. |
1526 | | * |
1527 | | * We've parsed the attribute number, and found that we don't have a dictionary entry for it. But rather |
1528 | | * than create an attribute of type octets, we try to guess the data type. |
1529 | | */ |
1530 | | static fr_type_t cbor_guess_type(fr_dbuff_t *dbuff, bool pair) |
1531 | 0 | { |
1532 | 0 | fr_dbuff_t work_dbuff = FR_DBUFF(dbuff); |
1533 | 0 | ssize_t slen; |
1534 | 0 | uint8_t major, info; |
1535 | 0 | uint64_t value; |
1536 | | |
1537 | | /* |
1538 | | * get the next byte, which is a CBOR header. |
1539 | | */ |
1540 | 0 | slen = fr_dbuff_out(&major, &work_dbuff); |
1541 | 0 | if (slen <= 0) { |
1542 | 0 | no_data: |
1543 | 0 | fr_strerror_const("Invalid cbor - insufficient data"); |
1544 | 0 | return FR_TYPE_NULL; |
1545 | 0 | } |
1546 | | |
1547 | 0 | info = major & 0x1f; |
1548 | 0 | major >>= 5; |
1549 | |
|
1550 | 0 | switch (major) { |
1551 | 0 | case CBOR_INTEGER: |
1552 | 0 | return FR_TYPE_UINT64; |
1553 | | |
1554 | 0 | case CBOR_NEGATIVE: |
1555 | 0 | return FR_TYPE_INT64; |
1556 | | |
1557 | 0 | case CBOR_STRING: |
1558 | 0 | return FR_TYPE_STRING; |
1559 | | |
1560 | 0 | case CBOR_OCTETS: |
1561 | 0 | return FR_TYPE_OCTETS; |
1562 | | |
1563 | 0 | case CBOR_ARRAY: |
1564 | 0 | if (!pair) return FR_TYPE_GROUP; |
1565 | 0 | break; |
1566 | | |
1567 | 0 | case CBOR_MAP: |
1568 | 0 | return FR_TYPE_TLV; |
1569 | | |
1570 | | /* |
1571 | | * Look at the tag to determine what it is |
1572 | | */ |
1573 | 0 | case CBOR_TAG: |
1574 | 0 | slen = cbor_decode_integer(&value, info, &work_dbuff); |
1575 | 0 | if (slen < 0) break; |
1576 | | |
1577 | 0 | switch (value) { |
1578 | 0 | case 1: |
1579 | 0 | case 1001: |
1580 | 0 | return FR_TYPE_DATE; |
1581 | | |
1582 | 0 | case 1002: |
1583 | 0 | return FR_TYPE_TIME_DELTA; |
1584 | | |
1585 | 0 | case 48: |
1586 | 0 | return FR_TYPE_ETHERNET; |
1587 | | |
1588 | 0 | case 52: |
1589 | 0 | slen = fr_dbuff_out(&major, &work_dbuff); |
1590 | 0 | if (slen <= 0) goto no_data; |
1591 | | |
1592 | 0 | major >>= 5; |
1593 | |
|
1594 | 0 | if (major == CBOR_ARRAY) { |
1595 | 0 | return FR_TYPE_IPV4_PREFIX; |
1596 | 0 | } |
1597 | 0 | return FR_TYPE_IPV4_ADDR; |
1598 | | |
1599 | 0 | case 54: |
1600 | 0 | slen = fr_dbuff_out(&major, &work_dbuff); |
1601 | 0 | if (slen <= 0) goto no_data; |
1602 | | |
1603 | 0 | major >>= 5; |
1604 | |
|
1605 | 0 | if (major == CBOR_ARRAY) { |
1606 | 0 | return FR_TYPE_IPV6_PREFIX; |
1607 | 0 | } |
1608 | 0 | return FR_TYPE_IPV6_ADDR; |
1609 | | |
1610 | 0 | default: |
1611 | 0 | break; |
1612 | 0 | } |
1613 | | |
1614 | 0 | break; |
1615 | | |
1616 | 0 | case CBOR_FLOAT: |
1617 | | /* |
1618 | | * In-place values are special. false / true / NULL |
1619 | | */ |
1620 | 0 | if (info < 24) { |
1621 | 0 | if ((info == 20) || (info == 21)) return FR_TYPE_BOOL; |
1622 | | |
1623 | 0 | return FR_TYPE_NULL; |
1624 | 0 | } |
1625 | | |
1626 | 0 | return FR_TYPE_FLOAT64; |
1627 | 0 | } |
1628 | | |
1629 | | |
1630 | | /* |
1631 | | * No idea. :( |
1632 | | * |
1633 | | * @todo - also check the cbor data, and return the length of cbor data which needs to be |
1634 | | * converted to data type 'octets'. This work involves mostly parsing the cbor data, which isn't |
1635 | | * trivial. |
1636 | | */ |
1637 | 0 | fr_strerror_const("Invalid cbor - unable to determine data type"); |
1638 | 0 | return FR_TYPE_NULL; |
1639 | 0 | } |
1640 | | |
1641 | | static ssize_t cbor_decode_pair(TALLOC_CTX *ctx, fr_pair_list_t *out, fr_dbuff_t *dbuff, |
1642 | | fr_dict_attr_t const *parent, bool tainted, int depth) |
1643 | 0 | { |
1644 | 0 | fr_dbuff_t work_dbuff = FR_DBUFF(dbuff); |
1645 | 0 | uint8_t header, major, info; |
1646 | 0 | bool indefinite; |
1647 | 0 | ssize_t slen; |
1648 | 0 | fr_pair_t *vp; |
1649 | 0 | uint64_t value = 0; |
1650 | 0 | fr_dict_attr_t const *da; |
1651 | |
|
1652 | 0 | FR_DBUFF_OUT_RETURN(&header, &work_dbuff); |
1653 | | |
1654 | | /* |
1655 | | * We require a 2-element array(attribute number, value) |
1656 | | */ |
1657 | 0 | if (header != (((CBOR_MAP) << 5) | 1)) { |
1658 | 0 | fr_strerror_printf("Invalid cbor header - expected map of 1 elements, got %02x", header); |
1659 | 0 | return -1; |
1660 | 0 | } |
1661 | | |
1662 | | /* |
1663 | | * This should be a CBOR_INTEGER. |
1664 | | */ |
1665 | 0 | FR_DBUFF_OUT_RETURN(&major, &work_dbuff); |
1666 | | |
1667 | 0 | info = major & 0x1f; |
1668 | 0 | major >>= 5; |
1669 | |
|
1670 | 0 | if (major != CBOR_INTEGER) { |
1671 | 0 | fr_strerror_printf("Invalid cbor - expected 'integer', got major type %d", |
1672 | 0 | major); |
1673 | 0 | return -1; |
1674 | 0 | } |
1675 | | |
1676 | 0 | slen = cbor_decode_integer(&value, info, &work_dbuff); |
1677 | 0 | if (slen < 0) { |
1678 | 0 | return_slen; |
1679 | 0 | } |
1680 | | |
1681 | | /* |
1682 | | * If the nesting is too deep, decode as raw octets. We have to do this manually in CBOR, |
1683 | | * because the other protocols create a da_stack which limits the depth. |
1684 | | */ |
1685 | 0 | if (depth >= FR_DICT_MAX_TLV_STACK) goto raw; |
1686 | | |
1687 | 0 | da = fr_dict_attr_child_by_num(parent, value); |
1688 | 0 | if (!da) { |
1689 | 0 | fr_type_t type; |
1690 | |
|
1691 | 0 | type = cbor_guess_type(&work_dbuff, true); |
1692 | 0 | if (type == FR_TYPE_NULL) return -fr_dbuff_used(&work_dbuff); |
1693 | | |
1694 | 0 | if (depth >= FR_DICT_MAX_TLV_STACK) { |
1695 | 0 | raw: |
1696 | 0 | type = FR_TYPE_OCTETS; |
1697 | 0 | } |
1698 | | |
1699 | | /* |
1700 | | * @todo - the value here isn't a cbor octets type, but is instead cbor data. Since cbor |
1701 | | * is typed, we _could_ perhaps instead discover the type from the cbor data, and then |
1702 | | * use that instead. This would involve creating a function which maps cbor types to our |
1703 | | * data types. |
1704 | | */ |
1705 | 0 | da = fr_dict_attr_unknown_typed_afrom_num(ctx, parent, value, type); |
1706 | 0 | if (!da) return -fr_dbuff_used(&work_dbuff); |
1707 | 0 | } |
1708 | | |
1709 | 0 | vp = fr_pair_afrom_da(ctx, da); |
1710 | 0 | if (!vp) { |
1711 | 0 | fr_strerror_const("Out of memory"); |
1712 | 0 | return -fr_dbuff_used(&work_dbuff); |
1713 | 0 | } |
1714 | | |
1715 | | /* |
1716 | | * Leaf values are easy. |
1717 | | */ |
1718 | 0 | if (fr_type_is_leaf(da->type)) { |
1719 | 0 | slen = fr_cbor_decode_value_box(vp, &vp->data, &work_dbuff, da->type, da, tainted); |
1720 | 0 | if (slen < 0) { |
1721 | 0 | talloc_free(vp); |
1722 | 0 | return_slen; |
1723 | 0 | } |
1724 | | |
1725 | 0 | goto done; |
1726 | 0 | } |
1727 | | |
1728 | 0 | fr_assert(fr_type_is_structural(da->type)); |
1729 | |
|
1730 | 0 | switch (da->type) { |
1731 | | /* |
1732 | | * All of these are essentially the same. |
1733 | | */ |
1734 | 0 | case FR_TYPE_VENDOR: |
1735 | 0 | case FR_TYPE_VSA: |
1736 | 0 | case FR_TYPE_TLV: |
1737 | 0 | parent = vp->da; |
1738 | 0 | break; |
1739 | | |
1740 | | /* |
1741 | | * Groups reparent to the ref. |
1742 | | */ |
1743 | 0 | case FR_TYPE_GROUP: |
1744 | 0 | parent = fr_dict_attr_ref(vp->da); |
1745 | 0 | fr_assert(parent != NULL); |
1746 | 0 | break; |
1747 | | |
1748 | 0 | default: |
1749 | 0 | fr_strerror_printf("Invalid data type %s for child %s of %s", |
1750 | 0 | fr_type_to_str(da->type), vp->da->name, parent->name); |
1751 | 0 | talloc_free(vp); |
1752 | 0 | return -1; |
1753 | 0 | } |
1754 | | |
1755 | | /* |
1756 | | * This should be a CBOR_ARRAY. |
1757 | | */ |
1758 | 0 | FR_DBUFF_OUT_RETURN(&major, &work_dbuff); |
1759 | | |
1760 | 0 | info = major & 0x1f; |
1761 | 0 | major >>= 5; |
1762 | |
|
1763 | 0 | if (major != CBOR_ARRAY) { |
1764 | | /* |
1765 | | * Allow NULL as a synonym for "no children". |
1766 | | */ |
1767 | 0 | if ((major == CBOR_FLOAT) && (info == 22)) goto done; |
1768 | | |
1769 | 0 | talloc_free(vp); |
1770 | 0 | fr_strerror_printf("Invalid cbor - expected 'array', got major type %d", |
1771 | 0 | major); |
1772 | 0 | return -1; |
1773 | 0 | } |
1774 | | |
1775 | 0 | if (info == 31) { |
1776 | 0 | value = ~0; |
1777 | 0 | indefinite = true; |
1778 | |
|
1779 | 0 | } else { |
1780 | 0 | slen = cbor_decode_integer(&value, info, &work_dbuff); |
1781 | 0 | if (slen < 0) { |
1782 | 0 | talloc_free(vp); |
1783 | 0 | return_slen; |
1784 | 0 | } |
1785 | | |
1786 | 0 | indefinite = false; |
1787 | 0 | } |
1788 | | |
1789 | | #ifdef STATIC_ANALYZER |
1790 | | if (value > fr_dbuff_remaining(&work_dbuff)) return -1; |
1791 | | #endif |
1792 | | |
1793 | | /* |
1794 | | * Loop until we decode everything. For simplicity, we handle indefinite and definite |
1795 | | * length arrays in the same loop. |
1796 | | */ |
1797 | 0 | for (/* nothing */; value > 0; value--) { |
1798 | | /* |
1799 | | * Require at least one byte in the buffer. |
1800 | | */ |
1801 | 0 | if (fr_dbuff_extend_lowat(NULL, &work_dbuff, 1) == 0) { |
1802 | 0 | talloc_free(vp); |
1803 | 0 | return -fr_dbuff_used(&work_dbuff); |
1804 | 0 | } |
1805 | | |
1806 | | /* |
1807 | | * Peek ahead for a break. |
1808 | | */ |
1809 | 0 | header = *fr_dbuff_current(&work_dbuff); |
1810 | 0 | if (header == 0xff) { |
1811 | 0 | if (!indefinite) { |
1812 | 0 | talloc_free(vp); |
1813 | 0 | fr_strerror_const("Unexpected 'break' found in cbor data"); |
1814 | 0 | return -fr_dbuff_used(&work_dbuff); |
1815 | 0 | } |
1816 | | |
1817 | | /* |
1818 | | * Done! |
1819 | | */ |
1820 | 0 | fr_dbuff_advance(&work_dbuff, 1); |
1821 | 0 | break; |
1822 | 0 | } |
1823 | | |
1824 | 0 | slen = cbor_decode_pair(vp, &vp->vp_group, &work_dbuff, parent, tainted, depth + 1); |
1825 | 0 | if (slen <= 0) { |
1826 | 0 | talloc_free(vp); |
1827 | 0 | return_slen; |
1828 | 0 | } |
1829 | 0 | } |
1830 | | |
1831 | 0 | done: |
1832 | 0 | PAIR_VERIFY(vp); |
1833 | |
|
1834 | 0 | fr_pair_append(out, vp); |
1835 | 0 | return fr_dbuff_set(dbuff, &work_dbuff); |
1836 | 0 | } |
1837 | | |
1838 | | ssize_t fr_cbor_decode_pair(TALLOC_CTX *ctx, fr_pair_list_t *out, fr_dbuff_t *dbuff, |
1839 | | fr_dict_attr_t const *parent, bool tainted) |
1840 | 0 | { |
1841 | 0 | return cbor_decode_pair(ctx, out, dbuff, parent, tainted, 0); |
1842 | 0 | } |
1843 | | |
1844 | | /* |
1845 | | * @todo - cbor_print |
1846 | | * [] for array |
1847 | | * [_...] for indefinite array |
1848 | | * {a:b} for map |
1849 | | * digits for integer |
1850 | | * 'string' for string |
1851 | | * h'HHHH' for octets |
1852 | | * |
1853 | | * https://datatracker.ietf.org/doc/html/draft-ietf-cbor-edn-literals |
1854 | | */ |