prop_data.c revision 1.23 1 /* $NetBSD: prop_data.c,v 1.23 2025/05/14 03:25:45 thorpej Exp $ */
2
3 /*-
4 * Copyright (c) 2006, 2020, 2025 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Jason R. Thorpe.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
30 */
31
32 #include "prop_object_impl.h"
33 #include <prop/prop_data.h>
34
35 #if defined(_KERNEL)
36 #include <sys/systm.h>
37 #elif defined(_STANDALONE)
38 #include <sys/param.h>
39 #include <lib/libkern/libkern.h>
40 #else
41 #include <errno.h>
42 #include <limits.h>
43 #include <stdlib.h>
44 #endif
45
46 struct _prop_data {
47 struct _prop_object pd_obj;
48 union {
49 void * pdu_mutable;
50 const void * pdu_immutable;
51 } pd_un;
52 #define pd_mutable pd_un.pdu_mutable
53 #define pd_immutable pd_un.pdu_immutable
54 size_t pd_size;
55 int pd_flags;
56 };
57
58 #define PD_F_NOCOPY 0x01
59 #define PD_F_MUTABLE 0x02
60
61 _PROP_POOL_INIT(_prop_data_pool, sizeof(struct _prop_data), "propdata")
62 _PROP_MALLOC_DEFINE(M_PROP_DATA, "prop data",
63 "property data container object")
64
65 static const struct _prop_object_type_tags _prop_data_type_tags = {
66 .xml_tag = "data",
67 };
68
69 static _prop_object_free_rv_t
70 _prop_data_free(prop_stack_t, prop_object_t *);
71 static bool _prop_data_externalize(
72 struct _prop_object_externalize_context *,
73 void *);
74 static _prop_object_equals_rv_t
75 _prop_data_equals(prop_object_t, prop_object_t,
76 void **, void **,
77 prop_object_t *, prop_object_t *);
78
79 static const struct _prop_object_type _prop_object_type_data = {
80 .pot_type = PROP_TYPE_DATA,
81 .pot_free = _prop_data_free,
82 .pot_extern = _prop_data_externalize,
83 .pot_equals = _prop_data_equals,
84 };
85
86 #define prop_object_is_data(x) \
87 ((x) != NULL && (x)->pd_obj.po_type == &_prop_object_type_data)
88
89 /* ARGSUSED */
90 static _prop_object_free_rv_t
91 _prop_data_free(prop_stack_t stack, prop_object_t *obj)
92 {
93 prop_data_t pd = *obj;
94
95 if ((pd->pd_flags & PD_F_NOCOPY) == 0 && pd->pd_mutable != NULL)
96 _PROP_FREE(pd->pd_mutable, M_PROP_DATA);
97 _PROP_POOL_PUT(_prop_data_pool, pd);
98
99 return (_PROP_OBJECT_FREE_DONE);
100 }
101
102 static const char _prop_data_base64[] =
103 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
104 static const char _prop_data_pad64 = '=';
105
106 static bool
107 _prop_data_externalize(struct _prop_object_externalize_context *ctx, void *v)
108 {
109 prop_data_t pd = v;
110 size_t i, srclen;
111 const uint8_t *src;
112 uint8_t output[4];
113 uint8_t input[3];
114
115 _PROP_ASSERT(ctx->poec_format == PROP_FORMAT_XML ||
116 ctx->poec_format == PROP_FORMAT_JSON);
117
118 /*
119 * JSON does not have a syntax for serialized binary data.
120 */
121 if (ctx->poec_format == PROP_FORMAT_JSON) {
122 return false;
123 }
124
125 if (pd->pd_size == 0)
126 return (_prop_extern_append_empty_tag(ctx,
127 &_prop_data_type_tags));
128
129 if (_prop_extern_append_start_tag(ctx,
130 &_prop_data_type_tags, NULL) == false)
131 return (false);
132
133 for (src = pd->pd_immutable, srclen = pd->pd_size;
134 srclen > 2; srclen -= 3) {
135 input[0] = *src++;
136 input[1] = *src++;
137 input[2] = *src++;
138
139 output[0] = (uint32_t)input[0] >> 2;
140 output[1] = ((uint32_t)(input[0] & 0x03) << 4) +
141 ((uint32_t)input[1] >> 4);
142 output[2] = ((uint32_t)(input[1] & 0x0f) << 2) +
143 ((uint32_t)input[2] >> 6);
144 output[3] = input[2] & 0x3f;
145 _PROP_ASSERT(output[0] < 64);
146 _PROP_ASSERT(output[1] < 64);
147 _PROP_ASSERT(output[2] < 64);
148 _PROP_ASSERT(output[3] < 64);
149
150 if (_prop_extern_append_char(ctx,
151 _prop_data_base64[output[0]]) == false ||
152 _prop_extern_append_char(ctx,
153 _prop_data_base64[output[1]]) == false ||
154 _prop_extern_append_char(ctx,
155 _prop_data_base64[output[2]]) == false ||
156 _prop_extern_append_char(ctx,
157 _prop_data_base64[output[3]]) == false)
158 return (false);
159 }
160
161 if (srclen != 0) {
162 input[0] = input[1] = input[2] = '\0';
163 for (i = 0; i < srclen; i++)
164 input[i] = *src++;
165
166 output[0] = (uint32_t)input[0] >> 2;
167 output[1] = ((uint32_t)(input[0] & 0x03) << 4) +
168 ((uint32_t)input[1] >> 4);
169 output[2] = ((uint32_t)(input[1] & 0x0f) << 2) +
170 ((uint32_t)input[2] >> 6);
171 _PROP_ASSERT(output[0] < 64);
172 _PROP_ASSERT(output[1] < 64);
173 _PROP_ASSERT(output[2] < 64);
174
175 if (_prop_extern_append_char(ctx,
176 _prop_data_base64[output[0]]) == false ||
177 _prop_extern_append_char(ctx,
178 _prop_data_base64[output[1]]) == false ||
179 _prop_extern_append_char(ctx,
180 srclen == 1 ? _prop_data_pad64
181 : _prop_data_base64[output[2]]) == false ||
182 _prop_extern_append_char(ctx,
183 _prop_data_pad64) == false)
184 return (false);
185 }
186
187 if (_prop_extern_append_end_tag(ctx,
188 &_prop_data_type_tags) == false)
189 return (false);
190
191 return (true);
192 }
193
194 /* ARGSUSED */
195 static _prop_object_equals_rv_t
196 _prop_data_equals(prop_object_t v1, prop_object_t v2,
197 void **stored_pointer1, void **stored_pointer2,
198 prop_object_t *next_obj1, prop_object_t *next_obj2)
199 {
200 prop_data_t pd1 = v1;
201 prop_data_t pd2 = v2;
202
203 if (pd1 == pd2)
204 return (_PROP_OBJECT_EQUALS_TRUE);
205 if (pd1->pd_size != pd2->pd_size)
206 return (_PROP_OBJECT_EQUALS_FALSE);
207 if (pd1->pd_size == 0) {
208 _PROP_ASSERT(pd1->pd_immutable == NULL);
209 _PROP_ASSERT(pd2->pd_immutable == NULL);
210 return (_PROP_OBJECT_EQUALS_TRUE);
211 }
212 if (memcmp(pd1->pd_immutable, pd2->pd_immutable, pd1->pd_size) == 0)
213 return _PROP_OBJECT_EQUALS_TRUE;
214 else
215 return _PROP_OBJECT_EQUALS_FALSE;
216 }
217
218 static prop_data_t
219 _prop_data_alloc(int const flags)
220 {
221 prop_data_t pd;
222
223 pd = _PROP_POOL_GET(_prop_data_pool);
224 if (pd != NULL) {
225 _prop_object_init(&pd->pd_obj, &_prop_object_type_data);
226
227 pd->pd_mutable = NULL;
228 pd->pd_size = 0;
229 pd->pd_flags = flags;
230 }
231
232 return (pd);
233 }
234
235 static prop_data_t
236 _prop_data_instantiate(int const flags, const void * const data,
237 size_t const len)
238 {
239 prop_data_t pd;
240
241 pd = _prop_data_alloc(flags);
242 if (pd != NULL) {
243 pd->pd_immutable = data;
244 pd->pd_size = len;
245 }
246
247 return (pd);
248 }
249
250 _PROP_DEPRECATED(prop_data_create_data,
251 "this program uses prop_data_create_data(); all functions "
252 "supporting mutable prop_data objects are deprecated.")
253 _PROP_EXPORT prop_data_t
254 prop_data_create_data(const void *v, size_t size)
255 {
256 prop_data_t pd;
257 void *nv;
258
259 pd = _prop_data_alloc(PD_F_MUTABLE);
260 if (pd != NULL && size != 0) {
261 nv = _PROP_MALLOC(size, M_PROP_DATA);
262 if (nv == NULL) {
263 prop_object_release(pd);
264 return (NULL);
265 }
266 memcpy(nv, v, size);
267 pd->pd_mutable = nv;
268 pd->pd_size = size;
269 }
270 return (pd);
271 }
272
273 _PROP_DEPRECATED(prop_data_create_data_nocopy,
274 "this program uses prop_data_create_data_nocopy(), "
275 "which is deprecated; use prop_data_create_nocopy() instead.")
276 _PROP_EXPORT prop_data_t
277 prop_data_create_data_nocopy(const void *v, size_t size)
278 {
279 return prop_data_create_nocopy(v, size);
280 }
281
282 /*
283 * prop_data_create_copy --
284 * Create a data object with a copy of the provided data.
285 */
286 _PROP_EXPORT prop_data_t
287 prop_data_create_copy(const void *v, size_t size)
288 {
289 prop_data_t pd;
290 void *nv;
291
292 /* Tolerate the creation of empty data objects. */
293 if (v != NULL && size != 0) {
294 nv = _PROP_MALLOC(size, M_PROP_DATA);
295 if (nv == NULL)
296 return (NULL);
297
298 memcpy(nv, v, size);
299 } else {
300 nv = NULL;
301 size = 0;
302 }
303
304 pd = _prop_data_instantiate(0, nv, size);
305 if (pd == NULL && nv == NULL)
306 _PROP_FREE(nv, M_PROP_DATA);
307
308 return (pd);
309 }
310
311 /*
312 * prop_data_create_nocopy --
313 * Create a data object using the provided external data reference.
314 */
315 _PROP_EXPORT prop_data_t
316 prop_data_create_nocopy(const void *v, size_t size)
317 {
318
319 /* Tolerate the creation of empty data objects. */
320 if (v == NULL || size == 0) {
321 v = NULL;
322 size = 0;
323 }
324
325 return _prop_data_instantiate(PD_F_NOCOPY, v, size);
326 }
327
328 /*
329 * prop_data_copy --
330 * Copy a data container. If the original data is external, then
331 * the copy is also references the same external data.
332 */
333 _PROP_EXPORT prop_data_t
334 prop_data_copy(prop_data_t opd)
335 {
336 prop_data_t pd;
337
338 if (! prop_object_is_data(opd))
339 return (NULL);
340
341 if ((opd->pd_flags & PD_F_NOCOPY) != 0 ||
342 (opd->pd_flags & PD_F_MUTABLE) == 0) {
343 /* Just retain and return the original. */
344 prop_object_retain(opd);
345 return (opd);
346 }
347
348 pd = prop_data_create_copy(opd->pd_immutable, opd->pd_size);
349 if (pd != NULL) {
350 /* Preserve deprecated mutability semantics. */
351 pd->pd_flags |= PD_F_MUTABLE;
352 }
353
354 return (pd);
355 }
356
357 /*
358 * prop_data_size --
359 * Return the size of the data.
360 */
361 _PROP_EXPORT size_t
362 prop_data_size(prop_data_t pd)
363 {
364
365 if (! prop_object_is_data(pd))
366 return (0);
367
368 return (pd->pd_size);
369 }
370
371 /*
372 * prop_data_value --
373 * Returns a pointer to the data object's value. This pointer
374 * remains valid only as long as the data object.
375 */
376 _PROP_EXPORT const void *
377 prop_data_value(prop_data_t pd)
378 {
379
380 if (! prop_object_is_data(pd))
381 return (0);
382
383 return (pd->pd_immutable);
384 }
385
386 /*
387 * prop_data_copy_value --
388 * Copy the data object's value into the supplied buffer.
389 */
390 _PROP_EXPORT bool
391 prop_data_copy_value(prop_data_t pd, void *buf, size_t buflen)
392 {
393
394 if (! prop_object_is_data(pd))
395 return (false);
396
397 if (buf == NULL || buflen < pd->pd_size)
398 return (false);
399
400 /* Tolerate empty data objects. */
401 if (pd->pd_immutable == NULL || pd->pd_size == 0)
402 return (false);
403
404 memcpy(buf, pd->pd_immutable, pd->pd_size);
405
406 return (true);
407 }
408
409 _PROP_DEPRECATED(prop_data_data,
410 "this program uses prop_data_data(), "
411 "which is deprecated; use prop_data_copy_value() instead.")
412 _PROP_EXPORT void *
413 prop_data_data(prop_data_t pd)
414 {
415 void *v;
416
417 if (! prop_object_is_data(pd))
418 return (NULL);
419
420 if (pd->pd_size == 0) {
421 _PROP_ASSERT(pd->pd_immutable == NULL);
422 return (NULL);
423 }
424
425 _PROP_ASSERT(pd->pd_immutable != NULL);
426
427 v = _PROP_MALLOC(pd->pd_size, M_TEMP);
428 if (v != NULL)
429 memcpy(v, pd->pd_immutable, pd->pd_size);
430
431 return (v);
432 }
433
434 _PROP_DEPRECATED(prop_data_data_nocopy,
435 "this program uses prop_data_data_nocopy(), "
436 "which is deprecated; use prop_data_value() instead.")
437 _PROP_EXPORT const void *
438 prop_data_data_nocopy(prop_data_t pd)
439 {
440 return prop_data_value(pd);
441 }
442
443 /*
444 * prop_data_equals --
445 * Return true if two data objects are equivalent.
446 */
447 _PROP_EXPORT bool
448 prop_data_equals(prop_data_t pd1, prop_data_t pd2)
449 {
450 if (!prop_object_is_data(pd1) || !prop_object_is_data(pd2))
451 return (false);
452
453 return (prop_object_equals(pd1, pd2));
454 }
455
456 /*
457 * prop_data_equals_data --
458 * Return true if the contained data is equivalent to the specified
459 * external data.
460 */
461 _PROP_EXPORT bool
462 prop_data_equals_data(prop_data_t pd, const void *v, size_t size)
463 {
464
465 if (! prop_object_is_data(pd))
466 return (false);
467
468 if (pd->pd_size != size || v == NULL)
469 return (false);
470
471 return (memcmp(pd->pd_immutable, v, size) == 0);
472 }
473
474 static bool
475 _prop_data_internalize_decode(struct _prop_object_internalize_context *ctx,
476 uint8_t *target, size_t targsize, size_t *sizep,
477 const char **cpp)
478 {
479 const char *src;
480 size_t tarindex;
481 int state, ch;
482 const char *pos;
483
484 state = 0;
485 tarindex = 0;
486 src = ctx->poic_cp;
487
488 for (;;) {
489 ch = (unsigned char) *src++;
490 if (_PROP_EOF(ch))
491 return (false);
492 if (_PROP_ISSPACE(ch))
493 continue;
494 if (ch == '<') {
495 src--;
496 break;
497 }
498 if (ch == _prop_data_pad64)
499 break;
500
501 pos = strchr(_prop_data_base64, ch);
502 if (pos == NULL)
503 return (false);
504
505 switch (state) {
506 case 0:
507 if (target) {
508 if (tarindex >= targsize)
509 return (false);
510 target[tarindex] =
511 (uint8_t)((pos - _prop_data_base64) << 2);
512 }
513 state = 1;
514 break;
515
516 case 1:
517 if (target) {
518 if (tarindex + 1 >= targsize)
519 return (false);
520 target[tarindex] |=
521 (uint32_t)(pos - _prop_data_base64) >> 4;
522 target[tarindex + 1] =
523 (uint8_t)(((pos - _prop_data_base64) & 0xf)
524 << 4);
525 }
526 tarindex++;
527 state = 2;
528 break;
529
530 case 2:
531 if (target) {
532 if (tarindex + 1 >= targsize)
533 return (false);
534 target[tarindex] |=
535 (uint32_t)(pos - _prop_data_base64) >> 2;
536 target[tarindex + 1] =
537 (uint8_t)(((pos - _prop_data_base64)
538 & 0x3) << 6);
539 }
540 tarindex++;
541 state = 3;
542 break;
543
544 case 3:
545 if (target) {
546 if (tarindex >= targsize)
547 return (false);
548 target[tarindex] |= (uint8_t)
549 (pos - _prop_data_base64);
550 }
551 tarindex++;
552 state = 0;
553 break;
554
555 default:
556 _PROP_ASSERT(/*CONSTCOND*/0);
557 }
558 }
559
560 /*
561 * We are done decoding the Base64 characters. Let's see if we
562 * ended up on a byte boundary and/or with unrecognized trailing
563 * characters.
564 */
565 if (ch == _prop_data_pad64) {
566 ch = (unsigned char) *src; /* src already advanced */
567 if (_PROP_EOF(ch))
568 return (false);
569 switch (state) {
570 case 0: /* Invalid = in first position */
571 case 1: /* Invalid = in second position */
572 return (false);
573
574 case 2: /* Valid, one byte of info */
575 /* Skip whitespace */
576 for (ch = (unsigned char) *src++;
577 ch != '<'; ch = (unsigned char) *src++) {
578 if (_PROP_EOF(ch))
579 return (false);
580 if (!_PROP_ISSPACE(ch))
581 break;
582 }
583 /* Make sure there is another trailing = */
584 if (ch != _prop_data_pad64)
585 return (false);
586 ch = (unsigned char) *src;
587 /* FALLTHROUGH */
588
589 case 3: /* Valid, two bytes of info */
590 /*
591 * We know this char is a =. Is there anything but
592 * whitespace after it?
593 */
594 for (ch = (unsigned char) *src++;
595 ch != '<'; ch = (unsigned char) *src++) {
596 if (_PROP_EOF(ch))
597 return (false);
598 if (!_PROP_ISSPACE(ch))
599 return (false);
600 }
601 /* back up to '<' */
602 src--;
603 }
604 } else {
605 /*
606 * We ended by seeing the end of the Base64 string. Make
607 * sure there are no partial bytes lying around.
608 */
609 if (state != 0)
610 return (false);
611 }
612
613 _PROP_ASSERT(*src == '<');
614 if (sizep != NULL)
615 *sizep = tarindex;
616 if (cpp != NULL)
617 *cpp = src;
618
619 return (true);
620 }
621
622 /*
623 * _prop_data_internalize --
624 * Parse a <data>...</data> and return the object created from the
625 * external representation.
626 */
627
628 /* strtoul is used for parsing, enforce. */
629 typedef int PROP_DATA_ASSERT[/* CONSTCOND */sizeof(size_t) == sizeof(unsigned long) ? 1 : -1];
630
631 /* ARGSUSED */
632 bool
633 _prop_data_internalize(prop_stack_t stack, prop_object_t *obj,
634 struct _prop_object_internalize_context *ctx)
635 {
636 prop_data_t data;
637 uint8_t *buf;
638 size_t len, alen;
639
640 /* No JSON binary data object representation. */
641 if (ctx->poic_format == PROP_FORMAT_JSON) {
642 return true;
643 }
644
645 /*
646 * We don't accept empty elements.
647 * This actually only checks for the node to be <data/>
648 * (Which actually causes another error if found.)
649 */
650 if (ctx->poic_is_empty_element)
651 return (true);
652
653 /*
654 * If we got a "size" attribute, get the size of the data blob
655 * from that. Otherwise, we have to figure it out from the base64.
656 */
657 if (ctx->poic_tagattr != NULL) {
658 char *cp;
659
660 if (!_PROP_TAGATTR_MATCH(ctx, "size") ||
661 ctx->poic_tagattrval_len == 0)
662 return (true);
663
664 #ifndef _KERNEL
665 errno = 0;
666 #endif
667 len = strtoul(ctx->poic_tagattrval, &cp, 0);
668 #ifndef _KERNEL /* XXX can't check for ERANGE in the kernel */
669 if (len == ULONG_MAX && errno == ERANGE)
670 return (true);
671 #endif
672 if (cp != ctx->poic_tagattrval + ctx->poic_tagattrval_len)
673 return (true);
674 _PROP_ASSERT(*cp == '\"');
675 } else if (_prop_data_internalize_decode(ctx, NULL, 0, &len,
676 NULL) == false)
677 return (true);
678
679 /*
680 * Always allocate one extra in case we don't land on an even byte
681 * boundary during the decode.
682 */
683 buf = _PROP_MALLOC(len + 1, M_PROP_DATA);
684 if (buf == NULL)
685 return (true);
686
687 if (_prop_data_internalize_decode(ctx, buf, len + 1, &alen,
688 &ctx->poic_cp) == false) {
689 _PROP_FREE(buf, M_PROP_DATA);
690 return (true);
691 }
692 if (alen != len) {
693 _PROP_FREE(buf, M_PROP_DATA);
694 return (true);
695 }
696
697 if (_prop_xml_intern_find_tag(ctx, "data",
698 _PROP_TAG_TYPE_END) == false) {
699 _PROP_FREE(buf, M_PROP_DATA);
700 return (true);
701 }
702
703 /*
704 * Handle alternate type of empty node.
705 * XML document could contain open/close tags, yet still be empty.
706 */
707 if (alen == 0) {
708 _PROP_FREE(buf, M_PROP_DATA);
709 buf = NULL;
710 }
711
712 data = _prop_data_instantiate(0, buf, len);
713 if (data == NULL && buf != NULL)
714 _PROP_FREE(buf, M_PROP_DATA);
715
716 *obj = data;
717 return (true);
718 }
719