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