1 /* $NetBSD: prop_data.c,v 1.9 2007/08/30 12:23:54 joerg Exp $ */ 2 3 /*- 4 * Copyright (c) 2006 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 * 3. All advertising materials mentioning features or use of this software 19 * must display the following acknowledgement: 20 * This product includes software developed by the NetBSD 21 * Foundation, Inc. and its contributors. 22 * 4. Neither the name of The NetBSD Foundation nor the names of its 23 * contributors may be used to endorse or promote products derived 24 * from this software without specific prior written permission. 25 * 26 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 27 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 28 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 29 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 30 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 31 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 32 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 33 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 34 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 35 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 36 * POSSIBILITY OF SUCH DAMAGE. 37 */ 38 39 #include <prop/prop_data.h> 40 #include "prop_object_impl.h" 41 42 #if defined(_KERNEL) 43 #include <sys/systm.h> 44 #elif defined(_STANDALONE) 45 #include <sys/param.h> 46 #include <lib/libkern/libkern.h> 47 #else 48 #include <errno.h> 49 #include <limits.h> 50 #include <stdlib.h> 51 #endif 52 53 struct _prop_data { 54 struct _prop_object pd_obj; 55 union { 56 void * pdu_mutable; 57 const void * pdu_immutable; 58 } pd_un; 59 #define pd_mutable pd_un.pdu_mutable 60 #define pd_immutable pd_un.pdu_immutable 61 size_t pd_size; 62 int pd_flags; 63 }; 64 65 #define PD_F_NOCOPY 0x01 66 67 _PROP_POOL_INIT(_prop_data_pool, sizeof(struct _prop_data), "propdata") 68 69 _PROP_MALLOC_DEFINE(M_PROP_DATA, "prop data", 70 "property data container object") 71 72 static int _prop_data_free(prop_stack_t, prop_object_t *); 73 static bool _prop_data_externalize( 74 struct _prop_object_externalize_context *, 75 void *); 76 static bool _prop_data_equals(prop_object_t, prop_object_t, 77 void **, void **, 78 prop_object_t *, prop_object_t *); 79 80 static const struct _prop_object_type _prop_object_type_data = { 81 .pot_type = PROP_TYPE_DATA, 82 .pot_free = _prop_data_free, 83 .pot_extern = _prop_data_externalize, 84 .pot_equals = _prop_data_equals, 85 }; 86 87 #define prop_object_is_data(x) \ 88 ((x) != NULL && (x)->pd_obj.po_type == &_prop_object_type_data) 89 90 /* ARGSUSED */ 91 static int 92 _prop_data_free(prop_stack_t stack, prop_object_t *obj) 93 { 94 prop_data_t pd = *obj; 95 96 if ((pd->pd_flags & PD_F_NOCOPY) == 0 && pd->pd_mutable != NULL) 97 _PROP_FREE(pd->pd_mutable, M_PROP_DATA); 98 _PROP_POOL_PUT(_prop_data_pool, pd); 99 100 return (_PROP_OBJECT_FREE_DONE); 101 } 102 103 static const char _prop_data_base64[] = 104 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; 105 static const char _prop_data_pad64 = '='; 106 107 static bool 108 _prop_data_externalize(struct _prop_object_externalize_context *ctx, void *v) 109 { 110 prop_data_t pd = v; 111 size_t i, srclen; 112 const uint8_t *src; 113 uint8_t output[4]; 114 uint8_t input[3]; 115 116 if (pd->pd_size == 0) 117 return (_prop_object_externalize_empty_tag(ctx, "data")); 118 119 if (_prop_object_externalize_start_tag(ctx, "data") == false) 120 return (false); 121 122 for (src = pd->pd_immutable, srclen = pd->pd_size; 123 srclen > 2; srclen -= 3) { 124 input[0] = *src++; 125 input[1] = *src++; 126 input[2] = *src++; 127 128 output[0] = (uint32_t)input[0] >> 2; 129 output[1] = ((uint32_t)(input[0] & 0x03) << 4) + 130 ((uint32_t)input[1] >> 4); 131 output[2] = ((u_int32_t)(input[1] & 0x0f) << 2) + 132 ((uint32_t)input[2] >> 6); 133 output[3] = input[2] & 0x3f; 134 _PROP_ASSERT(output[0] < 64); 135 _PROP_ASSERT(output[1] < 64); 136 _PROP_ASSERT(output[2] < 64); 137 _PROP_ASSERT(output[3] < 64); 138 139 if (_prop_object_externalize_append_char(ctx, 140 _prop_data_base64[output[0]]) == false || 141 _prop_object_externalize_append_char(ctx, 142 _prop_data_base64[output[1]]) == false || 143 _prop_object_externalize_append_char(ctx, 144 _prop_data_base64[output[2]]) == false || 145 _prop_object_externalize_append_char(ctx, 146 _prop_data_base64[output[3]]) == false) 147 return (false); 148 } 149 150 if (srclen != 0) { 151 input[0] = input[1] = input[2] = '\0'; 152 for (i = 0; i < srclen; i++) 153 input[i] = *src++; 154 155 output[0] = (uint32_t)input[0] >> 2; 156 output[1] = ((uint32_t)(input[0] & 0x03) << 4) + 157 ((uint32_t)input[1] >> 4); 158 output[2] = ((u_int32_t)(input[1] & 0x0f) << 2) + 159 ((uint32_t)input[2] >> 6); 160 _PROP_ASSERT(output[0] < 64); 161 _PROP_ASSERT(output[1] < 64); 162 _PROP_ASSERT(output[2] < 64); 163 164 if (_prop_object_externalize_append_char(ctx, 165 _prop_data_base64[output[0]]) == false || 166 _prop_object_externalize_append_char(ctx, 167 _prop_data_base64[output[1]]) == false || 168 _prop_object_externalize_append_char(ctx, 169 srclen == 1 ? _prop_data_pad64 170 : _prop_data_base64[output[2]]) == false || 171 _prop_object_externalize_append_char(ctx, 172 _prop_data_pad64) == false) 173 return (false); 174 } 175 176 if (_prop_object_externalize_end_tag(ctx, "data") == false) 177 return (false); 178 179 return (true); 180 } 181 182 /* ARGSUSED */ 183 static bool 184 _prop_data_equals(prop_object_t v1, prop_object_t v2, 185 void **stored_pointer1, void **stored_pointer2, 186 prop_object_t *next_obj1, prop_object_t *next_obj2) 187 { 188 prop_data_t pd1 = v1; 189 prop_data_t pd2 = v2; 190 191 if (pd1 == pd2) 192 return (_PROP_OBJECT_EQUALS_TRUE); 193 if (pd1->pd_size != pd2->pd_size) 194 return (_PROP_OBJECT_EQUALS_FALSE); 195 if (pd1->pd_size == 0) { 196 _PROP_ASSERT(pd1->pd_immutable == NULL); 197 _PROP_ASSERT(pd2->pd_immutable == NULL); 198 return (_PROP_OBJECT_EQUALS_TRUE); 199 } 200 if (memcmp(pd1->pd_immutable, pd2->pd_immutable, pd1->pd_size) == 0) 201 return _PROP_OBJECT_EQUALS_TRUE; 202 else 203 return _PROP_OBJECT_EQUALS_FALSE; 204 } 205 206 static prop_data_t 207 _prop_data_alloc(void) 208 { 209 prop_data_t pd; 210 211 pd = _PROP_POOL_GET(_prop_data_pool); 212 if (pd != NULL) { 213 _prop_object_init(&pd->pd_obj, &_prop_object_type_data); 214 215 pd->pd_mutable = NULL; 216 pd->pd_size = 0; 217 pd->pd_flags = 0; 218 } 219 220 return (pd); 221 } 222 223 /* 224 * prop_data_create_data -- 225 * Create a data container that contains a copy of the data. 226 */ 227 prop_data_t 228 prop_data_create_data(const void *v, size_t size) 229 { 230 prop_data_t pd; 231 void *nv; 232 233 pd = _prop_data_alloc(); 234 if (pd != NULL) { 235 nv = _PROP_MALLOC(size, M_PROP_DATA); 236 if (nv == NULL) { 237 prop_object_release(pd); 238 return (NULL); 239 } 240 memcpy(nv, v, size); 241 pd->pd_mutable = nv; 242 pd->pd_size = size; 243 } 244 return (pd); 245 } 246 247 /* 248 * prop_data_create_data_nocopy -- 249 * Create an immutable data container that contains a refrence to the 250 * provided external data. 251 */ 252 prop_data_t 253 prop_data_create_data_nocopy(const void *v, size_t size) 254 { 255 prop_data_t pd; 256 257 pd = _prop_data_alloc(); 258 if (pd != NULL) { 259 pd->pd_immutable = v; 260 pd->pd_size = size; 261 pd->pd_flags |= PD_F_NOCOPY; 262 } 263 return (pd); 264 } 265 266 /* 267 * prop_data_copy -- 268 * Copy a data container. If the original data is external, then 269 * the copy is also references the same external data. 270 */ 271 prop_data_t 272 prop_data_copy(prop_data_t opd) 273 { 274 prop_data_t pd; 275 276 if (! prop_object_is_data(opd)) 277 return (NULL); 278 279 pd = _prop_data_alloc(); 280 if (pd != NULL) { 281 pd->pd_size = opd->pd_size; 282 pd->pd_flags = opd->pd_flags; 283 if (opd->pd_flags & PD_F_NOCOPY) 284 pd->pd_immutable = opd->pd_immutable; 285 else if (opd->pd_size != 0) { 286 void *nv = _PROP_MALLOC(pd->pd_size, M_PROP_DATA); 287 if (nv == NULL) { 288 prop_object_release(pd); 289 return (NULL); 290 } 291 memcpy(nv, opd->pd_immutable, opd->pd_size); 292 pd->pd_mutable = nv; 293 } 294 } 295 return (pd); 296 } 297 298 /* 299 * prop_data_size -- 300 * Return the size of the data. 301 */ 302 size_t 303 prop_data_size(prop_data_t pd) 304 { 305 306 if (! prop_object_is_data(pd)) 307 return (0); 308 309 return (pd->pd_size); 310 } 311 312 /* 313 * prop_data_data -- 314 * Return a copy of the contents of the data container. 315 * The data is allocated with the M_TEMP malloc type. 316 * If the data container is empty, NULL is returned. 317 */ 318 void * 319 prop_data_data(prop_data_t pd) 320 { 321 void *v; 322 323 if (! prop_object_is_data(pd)) 324 return (NULL); 325 326 if (pd->pd_size == 0) { 327 _PROP_ASSERT(pd->pd_immutable == NULL); 328 return (NULL); 329 } 330 331 _PROP_ASSERT(pd->pd_immutable != NULL); 332 333 v = _PROP_MALLOC(pd->pd_size, M_TEMP); 334 if (v != NULL) 335 memcpy(v, pd->pd_immutable, pd->pd_size); 336 337 return (v); 338 } 339 340 /* 341 * prop_data_data_nocopy -- 342 * Return an immutable reference to the contents of the data 343 * container. 344 */ 345 const void * 346 prop_data_data_nocopy(prop_data_t pd) 347 { 348 349 if (! prop_object_is_data(pd)) 350 return (NULL); 351 352 _PROP_ASSERT((pd->pd_size == 0 && pd->pd_immutable == NULL) || 353 (pd->pd_size != 0 && pd->pd_immutable != NULL)); 354 355 return (pd->pd_immutable); 356 } 357 358 /* 359 * prop_data_equals -- 360 * Return true if two strings are equivalent. 361 */ 362 bool 363 prop_data_equals(prop_data_t pd1, prop_data_t pd2) 364 { 365 if (!prop_object_is_data(pd1) || !prop_object_is_data(pd2)) 366 return (false); 367 368 return (prop_object_equals(pd1, pd2)); 369 } 370 371 /* 372 * prop_data_equals_data -- 373 * Return true if the contained data is equivalent to the specified 374 * external data. 375 */ 376 bool 377 prop_data_equals_data(prop_data_t pd, const void *v, size_t size) 378 { 379 380 if (! prop_object_is_data(pd)) 381 return (false); 382 383 if (pd->pd_size != size) 384 return (false); 385 return (memcmp(pd->pd_immutable, v, size) == 0); 386 } 387 388 static bool 389 _prop_data_internalize_decode(struct _prop_object_internalize_context *ctx, 390 uint8_t *target, size_t targsize, size_t *sizep, 391 const char **cpp) 392 { 393 const char *src; 394 size_t tarindex; 395 int state, ch; 396 const char *pos; 397 398 state = 0; 399 tarindex = 0; 400 src = ctx->poic_cp; 401 402 for (;;) { 403 ch = (unsigned char) *src++; 404 if (_PROP_EOF(ch)) 405 return (false); 406 if (_PROP_ISSPACE(ch)) 407 continue; 408 if (ch == '<') { 409 src--; 410 break; 411 } 412 if (ch == _prop_data_pad64) 413 break; 414 415 pos = strchr(_prop_data_base64, ch); 416 if (pos == NULL) 417 return (false); 418 419 switch (state) { 420 case 0: 421 if (target) { 422 if (tarindex >= targsize) 423 return (false); 424 target[tarindex] = 425 (uint8_t)((pos - _prop_data_base64) << 2); 426 } 427 state = 1; 428 break; 429 430 case 1: 431 if (target) { 432 if (tarindex + 1 >= targsize) 433 return (false); 434 target[tarindex] |= 435 (uint32_t)(pos - _prop_data_base64) >> 4; 436 target[tarindex + 1] = 437 (uint8_t)(((pos - _prop_data_base64) & 0xf) 438 << 4); 439 } 440 tarindex++; 441 state = 2; 442 break; 443 444 case 2: 445 if (target) { 446 if (tarindex + 1 >= targsize) 447 return (false); 448 target[tarindex] |= 449 (uint32_t)(pos - _prop_data_base64) >> 2; 450 target[tarindex + 1] = 451 (uint8_t)(((pos - _prop_data_base64) 452 & 0x3) << 6); 453 } 454 tarindex++; 455 state = 3; 456 break; 457 458 case 3: 459 if (target) { 460 if (tarindex >= targsize) 461 return (false); 462 target[tarindex] |= (uint8_t) 463 (pos - _prop_data_base64); 464 } 465 tarindex++; 466 state = 0; 467 break; 468 469 default: 470 _PROP_ASSERT(/*CONSTCOND*/0); 471 } 472 } 473 474 /* 475 * We are done decoding the Base64 characters. Let's see if we 476 * ended up on a byte boundary and/or with unrecognized trailing 477 * characters. 478 */ 479 if (ch == _prop_data_pad64) { 480 ch = (unsigned char) *src; /* src already advanced */ 481 if (_PROP_EOF(ch)) 482 return (false); 483 switch (state) { 484 case 0: /* Invalid = in first position */ 485 case 1: /* Invalid = in second position */ 486 return (false); 487 488 case 2: /* Valid, one byte of info */ 489 /* Skip whitespace */ 490 for (ch = (unsigned char) *src++; 491 ch != '<'; ch = (unsigned char) *src++) { 492 if (_PROP_EOF(ch)) 493 return (false); 494 if (!_PROP_ISSPACE(ch)) 495 break; 496 } 497 /* Make sure there is another trailing = */ 498 if (ch != _prop_data_pad64) 499 return (false); 500 ch = (unsigned char) *src; 501 /* FALLTHROUGH */ 502 503 case 3: /* Valid, two bytes of info */ 504 /* 505 * We know this char is a =. Is there anything but 506 * whitespace after it? 507 */ 508 for (ch = (unsigned char) *src++; 509 ch != '<'; ch = (unsigned char) *src++) { 510 if (_PROP_EOF(ch)) 511 return (false); 512 if (!_PROP_ISSPACE(ch)) 513 return (false); 514 } 515 /* back up to '<' */ 516 src--; 517 } 518 } else { 519 /* 520 * We ended by seeing the end of the Base64 string. Make 521 * sure there are no partial bytes lying around. 522 */ 523 if (state != 0) 524 return (false); 525 } 526 527 _PROP_ASSERT(*src == '<'); 528 if (sizep != NULL) 529 *sizep = tarindex; 530 if (cpp != NULL) 531 *cpp = src; 532 533 return (true); 534 } 535 536 /* 537 * _prop_data_internalize -- 538 * Parse a <data>...</data> and return the object created from the 539 * external representation. 540 */ 541 542 /* strtoul is used for parsing, enforce. */ 543 typedef int PROP_DATA_ASSERT[/* CONSTCOND */sizeof(size_t) == sizeof(unsigned long) ? 1 : -1]; 544 545 /* ARGSUSED */ 546 bool 547 _prop_data_internalize(prop_stack_t stack, prop_object_t *obj, 548 struct _prop_object_internalize_context *ctx) 549 { 550 prop_data_t data; 551 uint8_t *buf; 552 size_t len, alen; 553 554 /* We don't accept empty elements. */ 555 if (ctx->poic_is_empty_element) 556 return (true); 557 558 /* 559 * If we got a "size" attribute, get the size of the data blob 560 * from that. Otherwise, we have to figure it out from the base64. 561 */ 562 if (ctx->poic_tagattr != NULL) { 563 char *cp; 564 565 if (!_PROP_TAGATTR_MATCH(ctx, "size") || 566 ctx->poic_tagattrval_len == 0) 567 return (true); 568 569 #ifndef _KERNEL 570 errno = 0; 571 #endif 572 len = strtoul(ctx->poic_tagattrval, &cp, 0); 573 #ifndef _KERNEL /* XXX can't check for ERANGE in the kernel */ 574 if (len == ULONG_MAX && errno == ERANGE) 575 return (true); 576 #endif 577 if (cp != ctx->poic_tagattrval + ctx->poic_tagattrval_len) 578 return (true); 579 _PROP_ASSERT(*cp == '\"'); 580 } else if (_prop_data_internalize_decode(ctx, NULL, 0, &len, 581 NULL) == false) 582 return (true); 583 584 /* 585 * Always allocate one extra in case we don't land on an even byte 586 * boundary during the decode. 587 */ 588 buf = _PROP_MALLOC(len + 1, M_PROP_DATA); 589 if (buf == NULL) 590 return (true); 591 592 if (_prop_data_internalize_decode(ctx, buf, len + 1, &alen, 593 &ctx->poic_cp) == false) { 594 _PROP_FREE(buf, M_PROP_DATA); 595 return (true); 596 } 597 if (alen != len) { 598 _PROP_FREE(buf, M_PROP_DATA); 599 return (true); 600 } 601 602 if (_prop_object_internalize_find_tag(ctx, "data", 603 _PROP_TAG_TYPE_END) == false) { 604 _PROP_FREE(buf, M_PROP_DATA); 605 return (true); 606 } 607 608 data = _prop_data_alloc(); 609 if (data == NULL) { 610 _PROP_FREE(buf, M_PROP_DATA); 611 return (true); 612 } 613 614 data->pd_mutable = buf; 615 data->pd_size = len; 616 617 *obj = data; 618 return (true); 619 } 620