plist_extractor.c (38019B)
1 /* 2 This file is part of libextractor. 3 Copyright (C) 2026 Vidyut Samanta and Christian Grothoff 4 5 libextractor is free software; you can redistribute it and/or modify 6 it under the terms of the GNU General Public License as published 7 by the Free Software Foundation; either version 3, or (at your 8 option) any later version. 9 10 libextractor is distributed in the hope that it will be useful, but 11 WITHOUT ANY WARRANTY; without even the implied warranty of 12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 General Public License for more details. 14 15 You should have received a copy of the GNU General Public License 16 along with libextractor; see the file COPYING. If not, write to the 17 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 18 Boston, MA 02110-1301, USA. 19 */ 20 /** 21 * @file plugins/plist_extractor.c 22 * @brief plugin to support Apple property lists, binary and XML 23 * @author Christian Grothoff 24 * 25 * Property lists are the configuration and manifest format of macOS 26 * and iOS; the forensically interesting ones are the `Info.plist' of 27 * an application bundle (which names the vendor, the SDK and the tool 28 * chain that built it) and the `Info.plist' / `Manifest.plist' of an 29 * iOS backup (which names the device, its serial number and when it 30 * was last backed up). 31 * 32 * Two on-disk representations exist. The binary one is documented by 33 * Apple's CoreFoundation sources (CFBinaryPlist.c): a `bplist00' 34 * signature, a body of tagged objects, an offset table and a 32-byte 35 * trailer at the very end of the file. The XML one is a DTD-bound 36 * document of alternating <key> and value elements. 37 * 38 * This is a first-pass identifier, not a plist library. We decode the 39 * top-level dictionary one level deep and stop; we never build an 40 * object graph, never follow arrays or nested dictionaries, and never 41 * read more than a bounded slice of the file. 42 */ 43 #include "platform.h" 44 #include "extractor.h" 45 #include "forensics.h" 46 47 #include <math.h> 48 49 50 /** 51 * Upper bound on the object count we accept from the trailer. A 52 * plist with more objects than this is either not a plist or is not 53 * something a first pass should be walking. 54 */ 55 #define PLIST_MAX_OBJECTS 100000 56 57 /** 58 * How many key/value pairs of the top-level dictionary we decode. 59 */ 60 #define PLIST_MAX_PAIRS 64 61 62 /** 63 * How many pairs whose key we do not recognise we report as 64 * #EXTRACTOR_METATYPE_UNKNOWN. The point is a characterisation, not 65 * a dump of the file. 66 */ 67 #define PLIST_MAX_UNKNOWN 12 68 69 /** 70 * Longest unrecognised value we are willing to report in the 71 * "key: value" form. 72 */ 73 #define PLIST_MAX_UNKNOWN_VALUE 80 74 75 /** 76 * Bytes we look at to decide whether an XML file is a plist. 77 */ 78 #define PLIST_SNIFF_SIZE 1024 79 80 /** 81 * Bytes of an XML plist we scan for key/value pairs. 82 */ 83 #define PLIST_XML_SCAN_SIZE (64 * 1024) 84 85 /** 86 * Size of the buffers holding one decoded key or value, in bytes. 87 * Keys and values in these files are identifiers, versions and dates. 88 */ 89 #define PLIST_MAX_TEXT 256 90 91 /** 92 * Seconds between the Unix epoch and the Core Foundation epoch of 93 * 2001-01-01. Binary plist dates count from the latter. 94 */ 95 #define PLIST_EPOCH_DELTA 978307200LL 96 97 /** 98 * Name we report our meta data under. 99 */ 100 #define PLIST_PLUGIN "plist" 101 102 /** 103 * MIME type of both representations. `application/xml' would also be 104 * true of the XML form, but it is useless for identification. 105 */ 106 #define PLIST_MIME "application/x-plist" 107 108 109 /** 110 * A well-known property list key and the meta data type it maps to. 111 */ 112 struct PlistKey 113 { 114 /** 115 * Key as it appears in the file. 116 */ 117 const char *key; 118 119 /** 120 * Type to report the associated value as. 121 */ 122 enum EXTRACTOR_MetaType type; 123 }; 124 125 126 /** 127 * Keys worth reporting as something better than "unknown". The 128 * CFBundle* and DT* families come from application bundles, the rest 129 * from iOS backup manifests. 130 */ 131 static const struct PlistKey plist_keys[] = { 132 { "CFBundleIdentifier", EXTRACTOR_METATYPE_APPLICATION_ID }, 133 { "CFBundleName", EXTRACTOR_METATYPE_PACKAGE_NAME }, 134 { "CFBundleDisplayName", EXTRACTOR_METATYPE_PACKAGE_NAME }, 135 { "CFBundleShortVersionString", EXTRACTOR_METATYPE_SOFTWARE_VERSION }, 136 { "CFBundleVersion", EXTRACTOR_METATYPE_PACKAGE_VERSION }, 137 { "CFBundleExecutable", EXTRACTOR_METATYPE_FILENAME }, 138 { "CFBundlePackageType", EXTRACTOR_METATYPE_RESOURCE_TYPE }, 139 { "MinimumOSVersion", EXTRACTOR_METATYPE_MINIMUM_OS_VERSION }, 140 { "LSMinimumSystemVersion", EXTRACTOR_METATYPE_MINIMUM_OS_VERSION }, 141 { "NSHumanReadableCopyright", EXTRACTOR_METATYPE_COPYRIGHT }, 142 { "DTPlatformName", EXTRACTOR_METATYPE_TARGET_PLATFORM }, 143 { "DTSDKName", EXTRACTOR_METATYPE_TARGET_PLATFORM }, 144 { "UIDeviceFamily", EXTRACTOR_METATYPE_TARGET_PLATFORM }, 145 { "DTXcode", EXTRACTOR_METATYPE_TOOLCHAIN }, 146 { "DTCompiler", EXTRACTOR_METATYPE_TOOLCHAIN }, 147 { "BuildMachineOSBuild", EXTRACTOR_METATYPE_AUTHORING_OS }, 148 { "DeviceName", EXTRACTOR_METATYPE_DEVICE_MODEL }, 149 { "ProductType", EXTRACTOR_METATYPE_DEVICE_MODEL }, 150 { "ProductVersion", EXTRACTOR_METATYPE_DEVICE_MODEL }, 151 { "SerialNumber", EXTRACTOR_METATYPE_SERIAL }, 152 { "UniqueDeviceID", EXTRACTOR_METATYPE_SERIAL }, 153 { "LastBackupDate", EXTRACTOR_METATYPE_MODIFICATION_DATE }, 154 { NULL, EXTRACTOR_METATYPE_RESERVED } 155 }; 156 157 158 /** 159 * State of the binary plist we are decoding: everything the trailer 160 * told us, already validated against the file size. 161 */ 162 struct BPlist 163 { 164 /** 165 * Number of objects, from the trailer. 166 */ 167 uint64_t num_objects; 168 169 /** 170 * Absolute offset of the offset table. 171 */ 172 uint64_t offset_table; 173 174 /** 175 * Index of the root object. 176 */ 177 uint64_t top_object; 178 179 /** 180 * Width in bytes of an entry in the offset table, 1 to 8. 181 */ 182 unsigned int offset_int_size; 183 184 /** 185 * Width in bytes of an object reference, 1 to 8. 186 */ 187 unsigned int object_ref_size; 188 }; 189 190 191 /** 192 * Look up @a key in #plist_keys. 193 * 194 * @param key NUL-terminated key from the file 195 * @param[out] type meta data type to use for the value 196 * @return 1 if the key is well-known, 0 if not 197 */ 198 static int 199 lookup_key (const char *key, 200 enum EXTRACTOR_MetaType *type) 201 { 202 for (unsigned int i = 0; NULL != plist_keys[i].key; i++) 203 if (0 == strcmp (key, 204 plist_keys[i].key)) 205 { 206 *type = plist_keys[i].type; 207 return 1; 208 } 209 return 0; 210 } 211 212 213 /** 214 * Read a big-endian unsigned integer of @a len bytes. 215 * 216 * @param p the bytes 217 * @param len number of bytes, 1 to 8 218 * @return the value 219 */ 220 static uint64_t 221 be_n (const unsigned char *p, 222 unsigned int len) 223 { 224 uint64_t v = 0; 225 226 for (unsigned int i = 0; i < len; i++) 227 v = (v << 8) | (uint64_t) p[i]; 228 return v; 229 } 230 231 232 /** 233 * Convert a UTF-16BE string to UTF-8. Binary plists store their 234 * non-ASCII strings this way; #EXTRACTOR_forensic_emit_utf16le_() 235 * cannot be used because it reads the other byte order. 236 * 237 * @param in the UTF-16BE bytes 238 * @param bytes number of bytes (not code units) in @a in 239 * @param[out] out where to write the NUL-terminated UTF-8 240 * @param out_size size of @a out in bytes 241 * @return 1 on success, 0 if the input is malformed or does not fit 242 */ 243 static int 244 utf16be_to_utf8 (const unsigned char *in, 245 size_t bytes, 246 char *out, 247 size_t out_size) 248 { 249 size_t o = 0; 250 size_t i = 0; 251 252 while (i + 1 < bytes) 253 { 254 uint32_t cp = EXTRACTOR_forensic_be16_ (&in[i]); 255 256 i += 2; 257 if (0 == cp) 258 break; /* NUL terminator */ 259 if ( (0xD800 <= cp) && (cp <= 0xDBFF) ) 260 { 261 uint32_t lo; 262 263 if (i + 1 >= bytes) 264 return 0; /* truncated surrogate pair */ 265 lo = EXTRACTOR_forensic_be16_ (&in[i]); 266 if ( (lo < 0xDC00) || (lo > 0xDFFF) ) 267 return 0; /* unpaired high surrogate */ 268 i += 2; 269 cp = 0x10000 + ((cp - 0xD800) << 10) + (lo - 0xDC00); 270 } 271 else if ( (0xDC00 <= cp) && (cp <= 0xDFFF) ) 272 { 273 return 0; /* stray low surrogate */ 274 } 275 if (o + 4 >= out_size) 276 return 0; /* does not fit */ 277 if (cp < 0x80) 278 { 279 out[o++] = (char) cp; 280 } 281 else if (cp < 0x800) 282 { 283 out[o++] = (char) (0xC0 | (cp >> 6)); 284 out[o++] = (char) (0x80 | (cp & 0x3F)); 285 } 286 else if (cp < 0x10000) 287 { 288 out[o++] = (char) (0xE0 | (cp >> 12)); 289 out[o++] = (char) (0x80 | ((cp >> 6) & 0x3F)); 290 out[o++] = (char) (0x80 | (cp & 0x3F)); 291 } 292 else 293 { 294 out[o++] = (char) (0xF0 | (cp >> 18)); 295 out[o++] = (char) (0x80 | ((cp >> 12) & 0x3F)); 296 out[o++] = (char) (0x80 | ((cp >> 6) & 0x3F)); 297 out[o++] = (char) (0x80 | (cp & 0x3F)); 298 } 299 } 300 out[o] = '\0'; 301 return 1; 302 } 303 304 305 /** 306 * Format @a unix_time as ISO 8601 in UTC, the spelling 307 * #EXTRACTOR_forensic_emit_unix_time_() uses. 308 * 309 * @param unix_time seconds since 1970-01-01 310 * @param[out] out where to write the NUL-terminated result 311 * @param out_size size of @a out in bytes, at least 21 312 * @return 1 on success, 0 if the value is implausible 313 */ 314 static int 315 format_time (int64_t unix_time, 316 char *out, 317 size_t out_size) 318 { 319 struct tm tm; 320 time_t t = (time_t) unix_time; 321 322 if ( (unix_time < 315532800LL) || /* 1980-01-01 */ 323 (unix_time > 4102444800LL) ) /* 2100-01-01 */ 324 return 0; 325 if (NULL == gmtime_r (&t, 326 &tm)) 327 return 0; 328 return (0 != strftime (out, 329 out_size, 330 "%Y-%m-%dT%H:%M:%SZ", 331 &tm)) ? 1 : 0; 332 } 333 334 335 /** 336 * Resolve an object reference to its absolute offset in the file. 337 * 338 * The offset table is read one entry at a time rather than in one 339 * block: at #PLIST_MAX_PAIRS pairs we need at most 129 entries, and 340 * eight-byte offsets times #PLIST_MAX_OBJECTS would otherwise be an 341 * 800 KB read on a file we may well reject a moment later. 342 * 343 * @param ec extraction context 344 * @param st validated trailer data 345 * @param ref object index 346 * @param[out] off absolute offset of the object 347 * @return 1 on success, 0 if @a ref is out of range or the offset it 348 * names does not point into the object area 349 */ 350 static int 351 object_offset (struct EXTRACTOR_ExtractContext *ec, 352 const struct BPlist *st, 353 uint64_t ref, 354 uint64_t *off) 355 { 356 unsigned char raw[8]; 357 uint64_t v; 358 359 if (ref >= st->num_objects) 360 return 0; 361 /* no overflow: num_objects <= PLIST_MAX_OBJECTS, offset_int_size <= 8 362 and offset_table + num_objects * offset_int_size was checked to be 363 within the file when the trailer was parsed */ 364 if (! EXTRACTOR_forensic_read_ (ec, 365 (int64_t) (st->offset_table 366 + ref * st->offset_int_size), 367 raw, 368 st->offset_int_size)) 369 return 0; 370 v = be_n (raw, 371 st->offset_int_size); 372 /* objects live between the 8-byte signature and the offset table */ 373 if ( (v < 8) || 374 (v >= st->offset_table) ) 375 return 0; 376 *off = v; 377 return 1; 378 } 379 380 381 /** 382 * Read the object reference stored at @a pos. 383 * 384 * @param ec extraction context 385 * @param st validated trailer data 386 * @param pos absolute offset of the reference 387 * @param[out] ref where to store the object index 388 * @return 1 on success, 0 on error 389 */ 390 static int 391 read_ref (struct EXTRACTOR_ExtractContext *ec, 392 const struct BPlist *st, 393 uint64_t pos, 394 uint64_t *ref) 395 { 396 unsigned char raw[8]; 397 398 if (! EXTRACTOR_forensic_read_ (ec, 399 (int64_t) pos, 400 raw, 401 st->object_ref_size)) 402 return 0; 403 *ref = be_n (raw, 404 st->object_ref_size); 405 return 1; 406 } 407 408 409 /** 410 * Decode the integer object at @a pos. 411 * 412 * @param ec extraction context 413 * @param st validated trailer data 414 * @param pos absolute offset of the object 415 * @param[out] value the value; eight-byte integers are signed, the 416 * shorter widths unsigned, as CFBinaryPlist writes them 417 * @param[out] next offset just past the object, may be NULL 418 * @return 1 on success, 0 if there is no integer object at @a pos 419 */ 420 static int 421 read_integer (struct EXTRACTOR_ExtractContext *ec, 422 const struct BPlist *st, 423 uint64_t pos, 424 int64_t *value, 425 uint64_t *next) 426 { 427 unsigned char marker; 428 unsigned char raw[8]; 429 unsigned int nbytes; 430 431 if (pos >= st->offset_table) 432 return 0; 433 if (! EXTRACTOR_forensic_read_ (ec, 434 (int64_t) pos, 435 &marker, 436 1)) 437 return 0; 438 if (0x10 != (marker & 0xF0)) 439 return 0; 440 if (3 < (marker & 0x0F)) 441 return 0; /* 128-bit integers exist; we do not need them */ 442 nbytes = 1u << (marker & 0x0F); 443 if ( (pos + 1 + nbytes) > st->offset_table) 444 return 0; 445 if (! EXTRACTOR_forensic_read_ (ec, 446 (int64_t) (pos + 1), 447 raw, 448 nbytes)) 449 return 0; 450 /* one, two and four byte integers are unsigned and always fit; the 451 eight byte form is signed, which is exactly what the cast does */ 452 *value = (int64_t) be_n (raw, 453 nbytes); 454 if (NULL != next) 455 *next = pos + 1 + nbytes; 456 return 1; 457 } 458 459 460 /** 461 * Decode the marker byte at @a pos and the collection/string length 462 * that goes with it. A low nibble of 0x0F means the length follows as 463 * an integer object rather than being the nibble itself. 464 * 465 * @param ec extraction context 466 * @param st validated trailer data 467 * @param pos absolute offset of the object 468 * @param[out] marker the marker byte 469 * @param[out] len the decoded length 470 * @param[out] body offset of the first byte after marker and length 471 * @return 1 on success, 0 on error 472 */ 473 static int 474 read_marker (struct EXTRACTOR_ExtractContext *ec, 475 const struct BPlist *st, 476 uint64_t pos, 477 unsigned char *marker, 478 uint64_t *len, 479 uint64_t *body) 480 { 481 if (pos >= st->offset_table) 482 return 0; 483 if (! EXTRACTOR_forensic_read_ (ec, 484 (int64_t) pos, 485 marker, 486 1)) 487 return 0; 488 if (0x0F != (*marker & 0x0F)) 489 { 490 *len = *marker & 0x0F; 491 *body = pos + 1; 492 return 1; 493 } 494 { 495 int64_t v; 496 497 if (! read_integer (ec, 498 st, 499 pos + 1, 500 &v, 501 body)) 502 return 0; 503 if ( (v < 0) || 504 ((uint64_t) v > st->offset_table) ) 505 return 0; 506 *len = (uint64_t) v; 507 } 508 return 1; 509 } 510 511 512 /** 513 * Render the object @a ref as a string, if it is one of the scalar 514 * kinds we care about. Anything else -- arrays, sets, dictionaries, 515 * UIDs, nulls -- is deliberately not decoded. 516 * 517 * @param ec extraction context 518 * @param st validated trailer data 519 * @param ref object index 520 * @param[out] out where to write the NUL-terminated UTF-8 rendering 521 * @param out_size size of @a out in bytes 522 * @return 1 on success, 0 if the object is of another kind or is 523 * malformed 524 */ 525 static int 526 render_value (struct EXTRACTOR_ExtractContext *ec, 527 const struct BPlist *st, 528 uint64_t ref, 529 char *out, 530 size_t out_size) 531 { 532 uint64_t pos; 533 uint64_t len; 534 uint64_t body; 535 unsigned char marker; 536 537 if (! object_offset (ec, 538 st, 539 ref, 540 &pos)) 541 return 0; 542 if (! read_marker (ec, 543 st, 544 pos, 545 &marker, 546 &len, 547 &body)) 548 return 0; 549 switch (marker & 0xF0) 550 { 551 case 0x00: 552 if (0x08 == marker) 553 { 554 strcpy (out, 555 "false"); 556 return 1; 557 } 558 if (0x09 == marker) 559 { 560 strcpy (out, 561 "true"); 562 return 1; 563 } 564 return 0; 565 case 0x10: 566 { 567 int64_t v; 568 569 if (! read_integer (ec, 570 st, 571 pos, 572 &v, 573 NULL)) 574 return 0; 575 snprintf (out, 576 out_size, 577 "%lld", 578 (long long) v); 579 return 1; 580 } 581 case 0x20: 582 { 583 unsigned char raw[8]; 584 unsigned int nbytes = 1u << (marker & 0x0F); 585 double d; 586 587 if ( (4 != nbytes) && 588 (8 != nbytes) ) 589 return 0; 590 if ( (pos + 1 + nbytes) > st->offset_table) 591 return 0; 592 if (! EXTRACTOR_forensic_read_ (ec, 593 (int64_t) (pos + 1), 594 raw, 595 nbytes)) 596 return 0; 597 if (4 == nbytes) 598 { 599 uint32_t bits = EXTRACTOR_forensic_be32_ (raw); 600 float f; 601 602 memcpy (&f, 603 &bits, 604 sizeof (f)); 605 d = (double) f; 606 } 607 else 608 { 609 uint64_t bits = EXTRACTOR_forensic_be64_ (raw); 610 611 memcpy (&d, 612 &bits, 613 sizeof (d)); 614 } 615 if (! isfinite (d)) 616 return 0; 617 snprintf (out, 618 out_size, 619 "%.10g", 620 d); 621 return 1; 622 } 623 case 0x30: 624 { 625 unsigned char raw[8]; 626 uint64_t bits; 627 double d; 628 629 if (0x33 != marker) 630 return 0; 631 if ( (pos + 9) > st->offset_table) 632 return 0; 633 if (! EXTRACTOR_forensic_read_ (ec, 634 (int64_t) (pos + 1), 635 raw, 636 8)) 637 return 0; 638 bits = EXTRACTOR_forensic_be64_ (raw); 639 memcpy (&d, 640 &bits, 641 sizeof (d)); 642 /* seconds since 2001-01-01; reject anything that cannot be a 643 date before converting, so the cast below is defined */ 644 if ( (! isfinite (d)) || 645 (d < -1.0e12) || 646 (d > 1.0e12) ) 647 return 0; 648 return format_time ((int64_t) d + PLIST_EPOCH_DELTA, 649 out, 650 out_size); 651 } 652 case 0x40: 653 /* the payload of a data object is not text; its size is still 654 worth knowing */ 655 if (body + len < body) 656 return 0; /* overflow */ 657 if (body + len > st->offset_table) 658 return 0; 659 snprintf (out, 660 out_size, 661 "%llu bytes of data", 662 (unsigned long long) len); 663 return 1; 664 case 0x50: 665 { 666 unsigned char raw[PLIST_MAX_TEXT]; 667 668 if (0 == len) 669 return 0; 670 if (len > sizeof (raw)) 671 len = sizeof (raw); 672 if (body + len > st->offset_table) 673 return 0; 674 if (! EXTRACTOR_forensic_read_ (ec, 675 (int64_t) body, 676 raw, 677 (size_t) len)) 678 return 0; 679 /* an "ASCII" string with the high bit set is not one; refuse it 680 rather than guess at a code page */ 681 for (uint64_t i = 0; i < len; i++) 682 if (0 != (raw[i] & 0x80)) 683 return 0; 684 if (len >= out_size) 685 len = out_size - 1; 686 memcpy (out, 687 raw, 688 (size_t) len); 689 out[len] = '\0'; 690 return 1; 691 } 692 case 0x60: 693 { 694 unsigned char raw[2 * PLIST_MAX_TEXT]; 695 uint64_t bytes; 696 697 if (0 == len) 698 return 0; 699 if (len > sizeof (raw) / 2) 700 len = sizeof (raw) / 2; 701 bytes = 2 * len; 702 if (body + bytes > st->offset_table) 703 return 0; 704 if (! EXTRACTOR_forensic_read_ (ec, 705 (int64_t) body, 706 raw, 707 (size_t) bytes)) 708 return 0; 709 return utf16be_to_utf8 (raw, 710 (size_t) bytes, 711 out, 712 out_size); 713 } 714 default: 715 return 0; 716 } 717 } 718 719 720 /** 721 * Decode the top-level dictionary of a binary property list and report 722 * the keys we recognise. 723 * 724 * @param ec extraction context 725 * @param st validated trailer data 726 * @return 1 if the caller should stop extracting, 0 to continue 727 */ 728 static int 729 extract_binary_dict (struct EXTRACTOR_ExtractContext *ec, 730 const struct BPlist *st) 731 { 732 uint64_t pos; 733 uint64_t count; 734 uint64_t body; 735 uint64_t pairs; 736 unsigned char marker; 737 unsigned int unknown = 0; 738 739 if (! object_offset (ec, 740 st, 741 st->top_object, 742 &pos)) 743 return 0; 744 if (! read_marker (ec, 745 st, 746 pos, 747 &marker, 748 &count, 749 &body)) 750 return 0; 751 if (0xD0 != (marker & 0xF0)) 752 return 0; /* the root is not a dictionary; nothing for us here */ 753 if ( (0 == count) || 754 (count > PLIST_MAX_OBJECTS) ) 755 return 0; 756 /* the reference block holds count keys followed by count values */ 757 if (count > (UINT64_MAX / (2 * (uint64_t) st->object_ref_size))) 758 return 0; 759 if (body + 2 * count * st->object_ref_size > st->offset_table) 760 return 0; 761 pairs = (count < PLIST_MAX_PAIRS) ? count : PLIST_MAX_PAIRS; 762 for (uint64_t i = 0; i < pairs; i++) 763 { 764 char key[PLIST_MAX_TEXT]; 765 char value[PLIST_MAX_TEXT]; 766 uint64_t kref; 767 uint64_t vref; 768 enum EXTRACTOR_MetaType type; 769 770 if (! read_ref (ec, 771 st, 772 body + i * st->object_ref_size, 773 &kref)) 774 return 0; 775 if (! read_ref (ec, 776 st, 777 body + (count + i) * st->object_ref_size, 778 &vref)) 779 return 0; 780 if (! render_value (ec, 781 st, 782 kref, 783 key, 784 sizeof (key))) 785 continue; /* key is not a string; skip the pair */ 786 if (! render_value (ec, 787 st, 788 vref, 789 value, 790 sizeof (value))) 791 continue; /* value is a collection or something we do not decode */ 792 if (lookup_key (key, 793 &type)) 794 { 795 if (EXTRACTOR_forensic_emit_text_ (ec, 796 PLIST_PLUGIN, 797 type, 798 value, 799 strlen (value))) 800 return 1; 801 continue; 802 } 803 if (unknown >= PLIST_MAX_UNKNOWN) 804 continue; 805 if (strlen (value) > PLIST_MAX_UNKNOWN_VALUE) 806 continue; 807 unknown++; 808 if (EXTRACTOR_forensic_emit_ (ec, 809 PLIST_PLUGIN, 810 EXTRACTOR_METATYPE_UNKNOWN, 811 "%s: %s", 812 key, 813 value)) 814 return 1; 815 } 816 return 0; 817 } 818 819 820 /** 821 * Handle a file that starts with the `bplist' signature. 822 * 823 * @param ec extraction context 824 * @param size size of the file 825 * @param header the first eight bytes of the file 826 */ 827 static void 828 extract_binary (struct EXTRACTOR_ExtractContext *ec, 829 uint64_t size, 830 const unsigned char *header) 831 { 832 unsigned char trailer[32]; 833 struct BPlist st; 834 835 if (! EXTRACTOR_forensic_read_ (ec, 836 (int64_t) (size - 32), 837 trailer, 838 sizeof (trailer))) 839 return; 840 /* CFBinaryPlistTrailer: five unused bytes, sortVersion, 841 offsetIntSize, objectRefSize, then three big-endian 64-bit 842 fields. (Note that the leading run of unused bytes is five, not 843 six: the sixth byte is _sortVersion.) */ 844 st.offset_int_size = trailer[6]; 845 st.object_ref_size = trailer[7]; 846 st.num_objects = EXTRACTOR_forensic_be64_ (&trailer[8]); 847 st.top_object = EXTRACTOR_forensic_be64_ (&trailer[16]); 848 st.offset_table = EXTRACTOR_forensic_be64_ (&trailer[24]); 849 if ( (st.offset_int_size < 1) || 850 (st.offset_int_size > 8) || 851 (st.object_ref_size < 1) || 852 (st.object_ref_size > 8) ) 853 return; 854 if ( (0 == st.num_objects) || 855 (st.num_objects > PLIST_MAX_OBJECTS) ) 856 return; 857 if (st.top_object >= st.num_objects) 858 return; 859 if ( (st.offset_table < 8) || 860 (st.offset_table > size - 32) ) 861 return; 862 /* no overflow: num_objects <= 100000 and offset_int_size <= 8 */ 863 if (st.num_objects * st.offset_int_size > size - 32 - st.offset_table) 864 return; 865 if (EXTRACTOR_forensic_emit_text_ (ec, 866 PLIST_PLUGIN, 867 EXTRACTOR_METATYPE_MIMETYPE, 868 PLIST_MIME, 869 strlen (PLIST_MIME))) 870 return; 871 if (EXTRACTOR_forensic_emit_text_ (ec, 872 PLIST_PLUGIN, 873 EXTRACTOR_METATYPE_FORMAT, 874 "Binary property list", 875 strlen ("Binary property list"))) 876 return; 877 if (EXTRACTOR_forensic_emit_text_ (ec, 878 PLIST_PLUGIN, 879 EXTRACTOR_METATYPE_FORMAT_VERSION, 880 (const char *) &header[6], 881 2)) 882 return; 883 if (EXTRACTOR_forensic_emit_size_ (ec, 884 PLIST_PLUGIN, 885 EXTRACTOR_METATYPE_ENTRY_COUNT, 886 st.num_objects)) 887 return; 888 (void) extract_binary_dict (ec, 889 &st); 890 } 891 892 893 /** 894 * Find @a needle in @a haystack. Written out rather than using 895 * memmem(), which is a GNU extension. 896 * 897 * @param haystack where to search 898 * @param hlen number of bytes in @a haystack 899 * @param needle what to search for 900 * @param nlen number of bytes in @a needle 901 * @return pointer to the first occurrence, NULL if there is none 902 */ 903 static const char * 904 find_bytes (const char *haystack, 905 size_t hlen, 906 const char *needle, 907 size_t nlen) 908 { 909 if ( (0 == nlen) || 910 (hlen < nlen) ) 911 return NULL; 912 for (size_t i = 0; i + nlen <= hlen; i++) 913 if (0 == memcmp (&haystack[i], 914 needle, 915 nlen)) 916 return &haystack[i]; 917 return NULL; 918 } 919 920 921 /** 922 * Copy XML character data, resolving the five predefined entities and 923 * numeric character references. 924 * 925 * @param in the text between the tags 926 * @param len number of bytes in @a in 927 * @param[out] out where to write the NUL-terminated result 928 * @param out_size size of @a out in bytes 929 * @return 1 on success, 0 if the text does not fit 930 */ 931 static int 932 xml_unescape (const char *in, 933 size_t len, 934 char *out, 935 size_t out_size) 936 { 937 size_t o = 0; 938 size_t i = 0; 939 940 while (i < len) 941 { 942 const char *semi; 943 size_t elen; 944 945 if ('&' != in[i]) 946 { 947 if (o + 1 >= out_size) 948 return 0; 949 out[o++] = in[i++]; 950 continue; 951 } 952 semi = find_bytes (&in[i], 953 len - i, 954 ";", 955 1); 956 if ( (NULL == semi) || 957 ((size_t) (semi - &in[i]) > 10) ) 958 { 959 if (o + 1 >= out_size) 960 return 0; 961 out[o++] = in[i++]; /* a bare ampersand; keep it */ 962 continue; 963 } 964 elen = (size_t) (semi - &in[i]) + 1; 965 if (o + 4 >= out_size) 966 return 0; 967 if ( (5 == elen) && 968 (0 == memcmp (&in[i], "&", 5)) ) 969 out[o++] = '&'; 970 else if ( (4 == elen) && 971 (0 == memcmp (&in[i], "<", 4)) ) 972 out[o++] = '<'; 973 else if ( (4 == elen) && 974 (0 == memcmp (&in[i], ">", 4)) ) 975 out[o++] = '>'; 976 else if ( (6 == elen) && 977 (0 == memcmp (&in[i], """, 6)) ) 978 out[o++] = '"'; 979 else if ( (6 == elen) && 980 (0 == memcmp (&in[i], "'", 6)) ) 981 out[o++] = '\''; 982 else if ( (2 < elen) && 983 ('#' == in[i + 1]) ) 984 { 985 unsigned long cp; 986 char num[12]; 987 size_t nlen = elen - 3; 988 989 if (nlen >= sizeof (num)) 990 return 0; 991 memcpy (num, 992 &in[i + 2], 993 nlen); 994 num[nlen] = '\0'; 995 cp = strtoul (('x' == num[0]) || ('X' == num[0]) 996 ? &num[1] : num, 997 NULL, 998 (('x' == num[0]) || ('X' == num[0])) ? 16 : 10); 999 if ( (0 == cp) || 1000 (cp > 0x10FFFF) || 1001 ( (0xD800 <= cp) && (cp <= 0xDFFF) ) ) 1002 return 0; 1003 if (cp < 0x80) 1004 { 1005 out[o++] = (char) cp; 1006 } 1007 else if (cp < 0x800) 1008 { 1009 out[o++] = (char) (0xC0 | (cp >> 6)); 1010 out[o++] = (char) (0x80 | (cp & 0x3F)); 1011 } 1012 else if (cp < 0x10000) 1013 { 1014 out[o++] = (char) (0xE0 | (cp >> 12)); 1015 out[o++] = (char) (0x80 | ((cp >> 6) & 0x3F)); 1016 out[o++] = (char) (0x80 | (cp & 0x3F)); 1017 } 1018 else 1019 { 1020 out[o++] = (char) (0xF0 | (cp >> 18)); 1021 out[o++] = (char) (0x80 | ((cp >> 12) & 0x3F)); 1022 out[o++] = (char) (0x80 | ((cp >> 6) & 0x3F)); 1023 out[o++] = (char) (0x80 | (cp & 0x3F)); 1024 } 1025 } 1026 else 1027 { 1028 out[o++] = '&'; /* entity we do not know; keep the text */ 1029 i++; 1030 continue; 1031 } 1032 i += elen; 1033 } 1034 out[o] = '\0'; 1035 return 1; 1036 } 1037 1038 1039 /** 1040 * One value element we are prepared to read out of an XML plist. 1041 */ 1042 struct XmlValueTag 1043 { 1044 /** 1045 * Opening tag, including the angle brackets. 1046 */ 1047 const char *open; 1048 1049 /** 1050 * Matching closing tag, NULL for an empty element such as <true/>. 1051 */ 1052 const char *close; 1053 1054 /** 1055 * Text to report for an empty element, NULL otherwise. 1056 */ 1057 const char *literal; 1058 }; 1059 1060 1061 /** 1062 * Value elements of the plist DTD that carry a scalar. Collections 1063 * (<dict>, <array>) are absent on purpose: we do not descend. 1064 */ 1065 static const struct XmlValueTag xml_value_tags[] = { 1066 { "<string>", "</string>", NULL }, 1067 { "<integer>", "</integer>", NULL }, 1068 { "<real>", "</real>", NULL }, 1069 { "<date>", "</date>", NULL }, 1070 { "<true/>", NULL, "true" }, 1071 { "<false/>", NULL, "false" }, 1072 { NULL, NULL, NULL } 1073 }; 1074 1075 1076 /** 1077 * Scan an XML property list for <key>/value pairs. 1078 * 1079 * This is a bounded byte scan and not an XML parser: it will be 1080 * confused by a <key> inside a comment or a CDATA section, and it does 1081 * not track nesting, so a key of the same name inside a nested 1082 * dictionary is treated like a top-level one. For a first pass whose 1083 * job is to say "this bundle is org.example.app, built with that SDK" 1084 * that is an acceptable trade; adding a real XML parser would mean a 1085 * dependency and a great deal more attack surface. What it must never 1086 * do -- and does not -- is read outside @a buf. 1087 * 1088 * @param ec extraction context 1089 * @param buf the first bytes of the file 1090 * @param len number of bytes in @a buf 1091 * @return 1 if the caller should stop extracting, 0 to continue 1092 */ 1093 static int 1094 scan_xml (struct EXTRACTOR_ExtractContext *ec, 1095 const char *buf, 1096 size_t len) 1097 { 1098 const char *p = buf; 1099 const char *end = buf + len; 1100 unsigned int pairs = 0; 1101 unsigned int unknown = 0; 1102 1103 while ( (p < end) && 1104 (pairs < PLIST_MAX_PAIRS) ) 1105 { 1106 const char *k; 1107 const char *ke; 1108 const char *q; 1109 char key[PLIST_MAX_TEXT]; 1110 char value[PLIST_MAX_TEXT]; 1111 enum EXTRACTOR_MetaType type; 1112 unsigned int i; 1113 1114 k = find_bytes (p, 1115 (size_t) (end - p), 1116 "<key>", 1117 5); 1118 if (NULL == k) 1119 break; 1120 k += 5; 1121 ke = find_bytes (k, 1122 (size_t) (end - k), 1123 "</key>", 1124 6); 1125 if (NULL == ke) 1126 break; 1127 p = ke + 6; 1128 pairs++; 1129 if (! xml_unescape (k, 1130 (size_t) (ke - k), 1131 key, 1132 sizeof (key))) 1133 continue; 1134 q = p; 1135 while ( (q < end) && 1136 ( (' ' == *q) || ('\t' == *q) || 1137 ('\r' == *q) || ('\n' == *q) ) ) 1138 q++; 1139 if ( (q >= end) || 1140 ('<' != *q) ) 1141 continue; 1142 for (i = 0; NULL != xml_value_tags[i].open; i++) 1143 { 1144 size_t olen = strlen (xml_value_tags[i].open); 1145 1146 if ((size_t) (end - q) < olen) 1147 continue; 1148 if (0 != memcmp (q, 1149 xml_value_tags[i].open, 1150 olen)) 1151 continue; 1152 if (NULL == xml_value_tags[i].close) 1153 { 1154 strcpy (value, 1155 xml_value_tags[i].literal); 1156 p = q + olen; 1157 } 1158 else 1159 { 1160 const char *vs = q + olen; 1161 const char *ve; 1162 size_t clen = strlen (xml_value_tags[i].close); 1163 1164 ve = find_bytes (vs, 1165 (size_t) (end - vs), 1166 xml_value_tags[i].close, 1167 clen); 1168 if (NULL == ve) 1169 return 0; /* truncated; nothing more to be had */ 1170 p = ve + clen; 1171 if (! xml_unescape (vs, 1172 (size_t) (ve - vs), 1173 value, 1174 sizeof (value))) 1175 value[0] = '\0'; 1176 } 1177 break; 1178 } 1179 if (NULL == xml_value_tags[i].open) 1180 continue; /* <dict>, <array>, <data> or an unknown element */ 1181 if ('\0' == value[0]) 1182 continue; 1183 if (lookup_key (key, 1184 &type)) 1185 { 1186 if (EXTRACTOR_forensic_emit_text_ (ec, 1187 PLIST_PLUGIN, 1188 type, 1189 value, 1190 strlen (value))) 1191 return 1; 1192 continue; 1193 } 1194 if (unknown >= PLIST_MAX_UNKNOWN) 1195 continue; 1196 if (strlen (value) > PLIST_MAX_UNKNOWN_VALUE) 1197 continue; 1198 unknown++; 1199 if (EXTRACTOR_forensic_emit_ (ec, 1200 PLIST_PLUGIN, 1201 EXTRACTOR_METATYPE_UNKNOWN, 1202 "%s: %s", 1203 key, 1204 value)) 1205 return 1; 1206 } 1207 return 0; 1208 } 1209 1210 1211 /** 1212 * Handle a file that looks like an XML property list. 1213 * 1214 * @param ec extraction context 1215 * @param size size of the file 1216 */ 1217 static void 1218 extract_xml (struct EXTRACTOR_ExtractContext *ec, 1219 uint64_t size) 1220 { 1221 char *buf; 1222 size_t len; 1223 1224 len = (size < PLIST_XML_SCAN_SIZE) 1225 ? (size_t) size 1226 : PLIST_XML_SCAN_SIZE; 1227 buf = malloc (len); 1228 if (NULL == buf) 1229 return; 1230 if (! EXTRACTOR_forensic_read_ (ec, 1231 0, 1232 buf, 1233 len)) 1234 { 1235 free (buf); 1236 return; 1237 } 1238 if (EXTRACTOR_forensic_emit_text_ (ec, 1239 PLIST_PLUGIN, 1240 EXTRACTOR_METATYPE_MIMETYPE, 1241 PLIST_MIME, 1242 strlen (PLIST_MIME))) 1243 { 1244 free (buf); 1245 return; 1246 } 1247 if (EXTRACTOR_forensic_emit_text_ (ec, 1248 PLIST_PLUGIN, 1249 EXTRACTOR_METATYPE_FORMAT, 1250 "XML property list", 1251 strlen ("XML property list"))) 1252 { 1253 free (buf); 1254 return; 1255 } 1256 (void) scan_xml (ec, 1257 buf, 1258 len); 1259 free (buf); 1260 } 1261 1262 1263 /** 1264 * Decide whether @a buf is the beginning of an XML property list. 1265 * 1266 * Both conditions matter. The `<plist' is what keeps us from 1267 * claiming every XML document on the volume; the leading `<' is what 1268 * keeps us from claiming a file that merely mentions a plist in its 1269 * first kilobyte. 1270 * 1271 * @param buf the first bytes of the file 1272 * @param len number of bytes in @a buf 1273 * @return 1 if this is an XML plist, 0 if not 1274 */ 1275 static int 1276 looks_like_xml_plist (const char *buf, 1277 size_t len) 1278 { 1279 size_t i = 0; 1280 1281 if ( (len >= 3) && 1282 (0xEF == (unsigned char) buf[0]) && 1283 (0xBB == (unsigned char) buf[1]) && 1284 (0xBF == (unsigned char) buf[2]) ) 1285 i = 3; /* UTF-8 byte order mark */ 1286 while ( (i < len) && 1287 ( (' ' == buf[i]) || ('\t' == buf[i]) || 1288 ('\r' == buf[i]) || ('\n' == buf[i]) ) ) 1289 i++; 1290 if ( (i >= len) || 1291 ('<' != buf[i]) ) 1292 return 0; 1293 if (NULL != find_bytes (buf, 1294 len, 1295 "<plist", 1296 6)) 1297 return 1; 1298 if (NULL != find_bytes (buf, 1299 len, 1300 "<!DOCTYPE plist", 1301 15)) 1302 return 1; 1303 return 0; 1304 } 1305 1306 1307 /** 1308 * Main entry method for the plist extraction plugin. 1309 * 1310 * @param ec extraction context provided to the plugin 1311 */ 1312 void 1313 EXTRACTOR_plist_extract_method (struct EXTRACTOR_ExtractContext *ec); 1314 1315 void 1316 EXTRACTOR_plist_extract_method (struct EXTRACTOR_ExtractContext *ec) 1317 { 1318 unsigned char head[PLIST_SNIFF_SIZE]; 1319 uint64_t size; 1320 size_t hlen; 1321 1322 size = ec->get_size (ec->cls); 1323 if ( (UINT64_MAX == size) || 1324 (size < 16) ) 1325 return; 1326 hlen = (size < sizeof (head)) ? (size_t) size : sizeof (head); 1327 if (! EXTRACTOR_forensic_read_ (ec, 1328 0, 1329 head, 1330 hlen)) 1331 return; 1332 if ( (hlen >= 8) && 1333 (0 == memcmp (head, 1334 "bplist", 1335 6)) && 1336 (isdigit ((unsigned char) head[6])) && 1337 (isdigit ((unsigned char) head[7])) ) 1338 { 1339 if (size < 40) 1340 return; /* eight-byte signature plus a 32-byte trailer at least */ 1341 extract_binary (ec, 1342 size, 1343 head); 1344 return; 1345 } 1346 if (looks_like_xml_plist ((const char *) head, 1347 hlen)) 1348 extract_xml (ec, 1349 size); 1350 } 1351 1352 1353 /* end of plist_extractor.c */