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