1 /* $NetBSD: prop_data.c,v 1.24 2025/09/23 22:37:13 rillig 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, pos; 481 int state, ch; 482 const char *posptr; 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 posptr = strchr(_prop_data_base64, ch); 502 if (posptr == NULL) 503 return (false); 504 pos = posptr - _prop_data_base64; 505 506 switch (state) { 507 case 0: 508 if (target) { 509 if (tarindex >= targsize) 510 return (false); 511 target[tarindex] = (uint8_t)(pos << 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] |= (uint8_t)(pos >> 4); 521 target[tarindex + 1] = (pos & 0xf) << 4; 522 } 523 tarindex++; 524 state = 2; 525 break; 526 527 case 2: 528 if (target) { 529 if (tarindex + 1 >= targsize) 530 return (false); 531 target[tarindex] |= (uint8_t)(pos >> 2); 532 target[tarindex + 1] = (pos & 0x3) << 6; 533 } 534 tarindex++; 535 state = 3; 536 break; 537 538 case 3: 539 if (target) { 540 if (tarindex >= targsize) 541 return (false); 542 target[tarindex] |= (uint8_t)pos; 543 } 544 tarindex++; 545 state = 0; 546 break; 547 548 default: 549 _PROP_ASSERT(0); 550 } 551 } 552 553 /* 554 * We are done decoding the Base64 characters. Let's see if we 555 * ended up on a byte boundary and/or with unrecognized trailing 556 * characters. 557 */ 558 if (ch == _prop_data_pad64) { 559 ch = (unsigned char) *src; /* src already advanced */ 560 if (_PROP_EOF(ch)) 561 return (false); 562 switch (state) { 563 case 0: /* Invalid = in first position */ 564 case 1: /* Invalid = in second position */ 565 return (false); 566 567 case 2: /* Valid, one byte of info */ 568 /* Skip whitespace */ 569 for (ch = (unsigned char) *src++; 570 ch != '<'; ch = (unsigned char) *src++) { 571 if (_PROP_EOF(ch)) 572 return (false); 573 if (!_PROP_ISSPACE(ch)) 574 break; 575 } 576 /* Make sure there is another trailing = */ 577 if (ch != _prop_data_pad64) 578 return (false); 579 ch = (unsigned char) *src; 580 /* FALLTHROUGH */ 581 582 case 3: /* Valid, two bytes of info */ 583 /* 584 * We know this char is a =. Is there anything but 585 * whitespace after it? 586 */ 587 for (ch = (unsigned char) *src++; 588 ch != '<'; ch = (unsigned char) *src++) { 589 if (_PROP_EOF(ch)) 590 return (false); 591 if (!_PROP_ISSPACE(ch)) 592 return (false); 593 } 594 /* back up to '<' */ 595 src--; 596 } 597 } else { 598 /* 599 * We ended by seeing the end of the Base64 string. Make 600 * sure there are no partial bytes lying around. 601 */ 602 if (state != 0) 603 return (false); 604 } 605 606 _PROP_ASSERT(*src == '<'); 607 if (sizep != NULL) 608 *sizep = tarindex; 609 if (cpp != NULL) 610 *cpp = src; 611 612 return (true); 613 } 614 615 /* 616 * _prop_data_internalize -- 617 * Parse a <data>...</data> and return the object created from the 618 * external representation. 619 */ 620 621 /* strtoul is used for parsing, enforce. */ 622 typedef int PROP_DATA_ASSERT[/* CONSTCOND */sizeof(size_t) == sizeof(unsigned long) ? 1 : -1]; 623 624 /* ARGSUSED */ 625 bool 626 _prop_data_internalize(prop_stack_t stack, prop_object_t *obj, 627 struct _prop_object_internalize_context *ctx) 628 { 629 prop_data_t data; 630 uint8_t *buf; 631 size_t len, alen; 632 633 /* No JSON binary data object representation. */ 634 if (ctx->poic_format == PROP_FORMAT_JSON) { 635 return true; 636 } 637 638 /* 639 * We don't accept empty elements. 640 * This actually only checks for the node to be <data/> 641 * (Which actually causes another error if found.) 642 */ 643 if (ctx->poic_is_empty_element) 644 return (true); 645 646 /* 647 * If we got a "size" attribute, get the size of the data blob 648 * from that. Otherwise, we have to figure it out from the base64. 649 */ 650 if (ctx->poic_tagattr != NULL) { 651 char *cp; 652 653 if (!_PROP_TAGATTR_MATCH(ctx, "size") || 654 ctx->poic_tagattrval_len == 0) 655 return (true); 656 657 #ifndef _KERNEL 658 errno = 0; 659 #endif 660 len = strtoul(ctx->poic_tagattrval, &cp, 0); 661 #ifndef _KERNEL /* XXX can't check for ERANGE in the kernel */ 662 if (len == ULONG_MAX && errno == ERANGE) 663 return (true); 664 #endif 665 if (cp != ctx->poic_tagattrval + ctx->poic_tagattrval_len) 666 return (true); 667 _PROP_ASSERT(*cp == '\"'); 668 } else if (_prop_data_internalize_decode(ctx, NULL, 0, &len, 669 NULL) == false) 670 return (true); 671 672 /* 673 * Always allocate one extra in case we don't land on an even byte 674 * boundary during the decode. 675 */ 676 buf = _PROP_MALLOC(len + 1, M_PROP_DATA); 677 if (buf == NULL) 678 return (true); 679 680 if (_prop_data_internalize_decode(ctx, buf, len + 1, &alen, 681 &ctx->poic_cp) == false) { 682 _PROP_FREE(buf, M_PROP_DATA); 683 return (true); 684 } 685 if (alen != len) { 686 _PROP_FREE(buf, M_PROP_DATA); 687 return (true); 688 } 689 690 if (_prop_xml_intern_find_tag(ctx, "data", 691 _PROP_TAG_TYPE_END) == false) { 692 _PROP_FREE(buf, M_PROP_DATA); 693 return (true); 694 } 695 696 /* 697 * Handle alternate type of empty node. 698 * XML document could contain open/close tags, yet still be empty. 699 */ 700 if (alen == 0) { 701 _PROP_FREE(buf, M_PROP_DATA); 702 buf = NULL; 703 } 704 705 data = _prop_data_instantiate(0, buf, len); 706 if (data == NULL && buf != NULL) 707 _PROP_FREE(buf, M_PROP_DATA); 708 709 *obj = data; 710 return (true); 711 } 712