1/* 2 * Copyright (c) 2006, 2012 Apple Computer, Inc. All rights reserved. 3 * 4 * @APPLE_LICENSE_HEADER_START@ 5 * 6 * This file contains Original Code and/or Modifications of Original Code 7 * as defined in and that are subject to the Apple Public Source License 8 * Version 2.0 (the 'License'). You may not use this file except in 9 * compliance with the License. Please obtain a copy of the License at 10 * http://www.opensource.apple.com/apsl/ and read it before using this 11 * file. 12 * 13 * The Original Code and all software distributed under the License are 14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 18 * Please see the License for the specific language governing rights and 19 * limitations under the License. 20 * 21 * @APPLE_LICENSE_HEADER_END@ 22 */ 23#include <CoreFoundation/CoreFoundation.h> 24#include <CoreFoundation/CFBundlePriv.h> 25#include <errno.h> 26#include <libc.h> 27#include <libgen.h> // dirname() 28#include <Kernel/libkern/mkext.h> 29#include <sys/types.h> 30#include <sys/mman.h> 31#include <fts.h> 32#include <paths.h> 33#include <mach-o/arch.h> 34#include <mach-o/fat.h> 35#include <mach-o/loader.h> 36#include <mach-o/nlist.h> 37#include <mach-o/swap.h> 38#include <mach/mach.h> 39#include <mach/mach_error.h> 40#include <mach/mach_types.h> 41#include <mach/machine/vm_param.h> 42#include <mach/kmod.h> 43#include <notify.h> 44#include <stdlib.h> 45#include <unistd.h> // sleep(3) 46#include <sys/types.h> 47#include <sys/stat.h> 48 49#include <DiskArbitration/DiskArbitrationPrivate.h> 50#include <IOKit/IOTypes.h> 51#include <IOKit/IOKitLib.h> 52#include <IOKit/IOKitServer.h> 53#include <IOKit/IOCFUnserialize.h> 54#include <IOKit/IOCFSerialize.h> 55#include <IOKit/pwr_mgt/IOPMLib.h> 56#include <libkern/OSByteOrder.h> 57 58#include <IOKit/kext/OSKext.h> 59#include <IOKit/kext/OSKextPrivate.h> 60#include <IOKit/kext/macho_util.h> 61#include <bootfiles.h> 62 63#include <IOKit/pwr_mgt/IOPMLib.h> 64 65#include "kextcache_main.h" 66#if !NO_BOOT_ROOT 67#include "bootcaches.h" 68#include "bootroot_internal.h" 69#endif /* !NO_BOOT_ROOT */ 70#include "mkext1_file.h" 71#include "compression.h" 72#include "security.h" 73 74// constants 75#define MKEXT_PERMS (0644) 76 77/* The timeout we use when waiting for the system to get to a low load state. 78 * We're shooting for about 10 minutes, but we don't want to collide with 79 * everyone else who wants to do work 10 minutes after boot, so we just pick 80 * a number in that ballpark. 81 */ 82#define kOSKextSystemLoadTimeout (8 * 60) 83#define kOSKextSystemLoadPauseTime (30) 84 85/******************************************************************************* 86* Program Globals 87*******************************************************************************/ 88const char * progname = "(unknown)"; 89 90CFMutableDictionaryRef sNoLoadKextAlertDict = NULL; 91CFMutableDictionaryRef sInvalidSignedKextAlertDict = NULL; 92//CFMutableDictionaryRef sUnsignedKextAlertDict = NULL; 93CFMutableDictionaryRef sExcludedKextAlertDict = NULL; 94CFMutableDictionaryRef sRevokedKextAlertDict = NULL; 95 96/******************************************************************************* 97* Utility and callback functions. 98*******************************************************************************/ 99// put/take helpers 100static void waitForIOKitQuiescence(void); 101static void waitForGreatSystemLoad(void); 102 103#define kMaxArchs 64 104#define kRootPathLen 256 105 106static u_int usecs_from_timeval(struct timeval *t); 107static void timeval_from_usecs(struct timeval *t, u_int usecs); 108static void timeval_difference(struct timeval *dst, 109 struct timeval *a, struct timeval *b); 110static Boolean isValidKextSigningTargetVolume(CFURLRef theURL); 111static Boolean _isNewerVersionInArray(OSKextRef theKext, CFArrayRef theArray); 112 113/******************************************************************************* 114*******************************************************************************/ 115int main(int argc, char * const * argv) 116{ 117 KextcacheArgs toolArgs; 118 ExitStatus result = EX_SOFTWARE; 119 Boolean fatal = false; 120 121 /***** 122 * Find out what the program was invoked as. 123 */ 124 progname = rindex(argv[0], '/'); 125 if (progname) { 126 progname++; // go past the '/' 127 } else { 128 progname = (char *)argv[0]; 129 } 130 131 /* Set the OSKext log callback right away. 132 */ 133 OSKextSetLogOutputFunction(&tool_log); 134 135 /***** 136 * Check if we were spawned by kextd, set up straightaway 137 * for service log filtering, and hook up to ASL. 138 */ 139 if (getenv("KEXTD_SPAWNED")) { 140 OSKextSetLogFilter(kDefaultServiceLogFilter | kOSKextLogKextOrGlobalMask, 141 /* kernel? */ false); 142 OSKextSetLogFilter(kDefaultServiceLogFilter | kOSKextLogKextOrGlobalMask, 143 /* kernel? */ true); 144 tool_openlog("com.apple.kextcache"); 145 } 146 147#if 0 148 int i; 149 OSKextLog(NULL, 150 kOSKextLogErrorLevel | kOSKextLogGeneralFlag, 151 "%s: kextcache args %d ", 152 __FUNCTION__, argc); 153 for (i = 0; i < argc; i++) { 154 OSKextLog(NULL, 155 kOSKextLogErrorLevel | kOSKextLogGeneralFlag, 156 "%s ", 157 argv[i]); 158 } 159#endif 160 161 /***** 162 * Process args & check for permission to load. 163 */ 164 result = readArgs(&argc, &argv, &toolArgs); 165 if (result != EX_OK) { 166 if (result == kKextcacheExitHelp) { 167 result = EX_OK; 168 } 169 goto finish; 170 } 171 172 /***** 173 * Now that we have a custom verbose level set by options, 174 * check the filter kextd passed in and combine them. 175 */ 176 checkKextdSpawnedFilter(/* kernel? */ false); 177 checkKextdSpawnedFilter(/* kernel? */ true); 178 179 result = checkArgs(&toolArgs); 180 if (result != EX_OK) { 181 goto finish; 182 } 183 184 /* From here on out the default exit status is ok. 185 */ 186 result = EX_OK; 187 188 /* Reduce our priority and throttle I/O, then wait for a good time to run. 189 */ 190 if (toolArgs.lowPriorityFlag) { 191 OSKextLog(/* kext */ NULL, 192 kOSKextLogDetailLevel | kOSKextLogGeneralFlag, 193 "Running in low-priority background mode."); 194 195 setpriority(PRIO_PROCESS, getpid(), 20); // run at really low priority 196 setiopolicy_np(IOPOL_TYPE_DISK, IOPOL_SCOPE_PROCESS, IOPOL_THROTTLE); 197 198 /* When building the prelinked kernel, we try to wait for a good time 199 * to do work. We can't do this for an mkext yet because we don't 200 * have a way to know if we're blocking reboot. 201 */ 202 if (toolArgs.prelinkedKernelPath) { 203 waitForGreatSystemLoad(); 204 } 205 } 206 207 /* The whole point of this program is to update caches, so let's not 208 * try to read any (we'll briefly turn this back on when checking them). 209 */ 210 OSKextSetUsesCaches(false); 211 212#if !NO_BOOT_ROOT 213 /* If it's a Boot!=root update, call checkUpdateCachesAndBoots() and 214 * jump right to exit; B!=R doesn't combine with any other cache building. 215 */ 216 if (toolArgs.updateVolumeURL) { 217 result = doUpdateVolume(&toolArgs); 218 goto finish; 219 } 220#endif /* !NO_BOOT_ROOT */ 221 222 /* If we're uncompressing the prelinked kernel, take care of that here 223 * and exit. 224 */ 225 if (toolArgs.prelinkedKernelPath && !CFArrayGetCount(toolArgs.argURLs) && 226 (toolArgs.compress || toolArgs.uncompress)) 227 { 228 result = compressPrelinkedKernel(toolArgs.prelinkedKernelPath, 229 /* compress */ toolArgs.compress); 230 goto finish; 231 } 232 233 /***** 234 * Read the kexts we'll be working with; first the set of all kexts, then 235 * the repository and named kexts for use with mkext-creation flags. 236 */ 237 if (toolArgs.printTestResults) { 238 OSKextSetRecordsDiagnostics(kOSKextDiagnosticsFlagAll); 239 } 240 toolArgs.allKexts = OSKextCreateKextsFromURLs(kCFAllocatorDefault, toolArgs.argURLs); 241 if (!toolArgs.allKexts || !CFArrayGetCount(toolArgs.allKexts)) { 242 OSKextLog(/* kext */ NULL, 243 kOSKextLogErrorLevel | kOSKextLogGeneralFlag, 244 "No kernel extensions found."); 245 result = EX_SOFTWARE; 246 goto finish; 247 } 248 249 toolArgs.repositoryKexts = OSKextCreateKextsFromURLs(kCFAllocatorDefault, 250 toolArgs.repositoryURLs); 251 toolArgs.namedKexts = OSKextCreateKextsFromURLs(kCFAllocatorDefault, 252 toolArgs.namedKextURLs); 253 if (!toolArgs.repositoryKexts || !toolArgs.namedKexts) { 254 OSKextLog(/* kext */ NULL, 255 kOSKextLogErrorLevel | kOSKextLogGeneralFlag, 256 "Error reading extensions."); 257 result = EX_SOFTWARE; 258 goto finish; 259 } 260 261 if (result != EX_OK) { 262 goto finish; 263 } 264 265 if (toolArgs.needLoadedKextInfo) { 266 result = getLoadedKextInfo(&toolArgs); 267 if (result != EX_OK) { 268 goto finish; 269 } 270 } 271 272 // xxx - we are potentially overwriting error results here 273 if (toolArgs.updateSystemCaches) { 274 result = updateSystemPlistCaches(&toolArgs); 275 // don't goto finish on error here, we might be able to create 276 // the other caches 277 } 278 279 if (toolArgs.mkextPath) { 280 result = createMkext(&toolArgs, &fatal); 281 if (fatal) { 282 goto finish; 283 } 284 } 285 286 if (toolArgs.prelinkedKernelPath) { 287 /* If we're updating the system prelinked kernel, make sure we aren't 288 * Safe Boot, or dire consequences shall result. 289 */ 290 if (toolArgs.needDefaultPrelinkedKernelInfo && 291 OSKextGetActualSafeBoot()) { 292 293 OSKextLog(/* kext */ NULL, 294 kOSKextLogErrorLevel | kOSKextLogGeneralFlag, 295 "Can't update the system prelinked kernel during safe boot."); 296 result = EX_OSERR; 297 goto finish; 298 } 299 300 /* Create/update the prelinked kernel as explicitly requested, or 301 * for the running kernel. 302 */ 303 result = createPrelinkedKernel(&toolArgs); 304 if (result != EX_OK) { 305 goto finish; 306 } 307 308 } 309 310finish: 311 312 /* We're actually not going to free anything else because we're exiting! 313 */ 314 exit(result); 315 316 SAFE_RELEASE(toolArgs.kextIDs); 317 SAFE_RELEASE(toolArgs.argURLs); 318 SAFE_RELEASE(toolArgs.repositoryURLs); 319 SAFE_RELEASE(toolArgs.namedKextURLs); 320 SAFE_RELEASE(toolArgs.allKexts); 321 SAFE_RELEASE(toolArgs.repositoryKexts); 322 SAFE_RELEASE(toolArgs.namedKexts); 323 SAFE_RELEASE(toolArgs.loadedKexts); 324 SAFE_RELEASE(toolArgs.kernelFile); 325 SAFE_RELEASE(toolArgs.symbolDirURL); 326 SAFE_FREE(toolArgs.mkextPath); 327 SAFE_FREE(toolArgs.prelinkedKernelPath); 328 SAFE_FREE(toolArgs.kernelPath); 329 330 return result; 331} 332 333/******************************************************************************* 334*******************************************************************************/ 335ExitStatus readArgs( 336 int * argc, 337 char * const ** argv, 338 KextcacheArgs * toolArgs) 339{ 340 ExitStatus result = EX_USAGE; 341 ExitStatus scratchResult = EX_USAGE; 342 CFStringRef scratchString = NULL; // must release 343 CFNumberRef scratchNumber = NULL; // must release 344 CFURLRef scratchURL = NULL; // must release 345 size_t len = 0; 346 uint32_t i = 0; 347 int optchar = 0; 348 int longindex = -1; 349 struct stat sb; 350 351 bzero(toolArgs, sizeof(*toolArgs)); 352 353 /***** 354 * Allocate collection objects. 355 */ 356 if (!createCFMutableSet(&toolArgs->kextIDs, &kCFTypeSetCallBacks) || 357 !createCFMutableArray(&toolArgs->argURLs, &kCFTypeArrayCallBacks) || 358 !createCFMutableArray(&toolArgs->repositoryURLs, &kCFTypeArrayCallBacks) || 359 !createCFMutableArray(&toolArgs->namedKextURLs, &kCFTypeArrayCallBacks) || 360 !createCFMutableArray(&toolArgs->targetArchs, NULL)) { 361 362 OSKextLogMemError(); 363 result = EX_OSERR; 364 exit(result); 365 } 366 367 /***** 368 * Process command line arguments. 369 */ 370 while ((optchar = getopt_long_only(*argc, *argv, 371 kOptChars, sOptInfo, &longindex)) != -1) { 372 373 SAFE_RELEASE_NULL(scratchString); 374 SAFE_RELEASE_NULL(scratchNumber); 375 SAFE_RELEASE_NULL(scratchURL); 376 377 /* When processing short (single-char) options, there is no way to 378 * express optional arguments. Instead, we suppress missing option 379 * argument errors by adding a leading ':' to the option string. 380 * When getopt detects a missing argument, it will return a ':' so that 381 * we can screen for options that are not required to have an argument. 382 */ 383 if (optchar == ':') { 384 switch (optopt) { 385 case kOptPrelinkedKernel: 386 optchar = optopt; 387 break; 388 default: 389 OSKextLog(/* kext */ NULL, 390 kOSKextLogErrorLevel | kOSKextLogGeneralFlag, 391 "%s: option requires an argument -- -%c.", 392 progname, optopt); 393 break; 394 } 395 } 396 397 /* Catch a -m before the switch and redirect it to the latest supported 398 * mkext version, so we don't have to duplicate the code block. 399 */ 400 if (optchar == kOptMkext) { 401 optchar = 0; 402 longopt = kLongOptMkext; 403 } 404 405 /* Catch a -e/-system-mkext and redirect to -system-prelinked-kernel. 406 */ 407 if (optchar == kOptSystemMkext) { 408 optchar = 0; 409 longopt = kLongOptSystemPrelinkedKernel; 410 } 411 412 switch (optchar) { 413 414 case kOptArch: 415 if (!addArchForName(toolArgs, optarg)) { 416 OSKextLog(/* kext */ NULL, 417 kOSKextLogErrorLevel | kOSKextLogGeneralFlag, 418 "Unknown architecture %s.", optarg); 419 goto finish; 420 } 421 toolArgs->explicitArch = true; 422 break; 423 424 case kOptBundleIdentifier: 425 scratchString = CFStringCreateWithCString(kCFAllocatorDefault, 426 optarg, kCFStringEncodingUTF8); 427 if (!scratchString) { 428 OSKextLogMemError(); 429 result = EX_OSERR; 430 goto finish; 431 } 432 CFSetAddValue(toolArgs->kextIDs, scratchString); 433 break; 434 435 case kOptPrelinkedKernel: 436 scratchResult = readPrelinkedKernelArgs(toolArgs, *argc, *argv, 437 /* isLongopt */ longindex != -1); 438 if (scratchResult != EX_OK) { 439 result = scratchResult; 440 goto finish; 441 } 442 break; 443 444 445#if !NO_BOOT_ROOT 446 case kOptForce: 447 toolArgs->forceUpdateFlag = true; 448 break; 449#endif /* !NO_BOOT_ROOT */ 450 451 case kOptLowPriorityFork: 452 toolArgs->lowPriorityFlag = true; 453 break; 454 455 case kOptHelp: 456 usage(kUsageLevelFull); 457 result = kKextcacheExitHelp; 458 goto finish; 459 460 case kOptRepositoryCaches: 461 OSKextLog(/* kext */ NULL, 462 kOSKextLogWarningLevel | kOSKextLogGeneralFlag, 463 "-%c is no longer used; ignoring.", 464 kOptRepositoryCaches); 465 break; 466 467 case kOptKernel: 468 if (toolArgs->kernelPath) { 469 OSKextLog(/* kext */ NULL, 470 kOSKextLogWarningLevel | kOSKextLogGeneralFlag, 471 "Warning: kernel file already specified; using last."); 472 } else { 473 toolArgs->kernelPath = malloc(PATH_MAX); 474 if (!toolArgs->kernelPath) { 475 OSKextLogMemError(); 476 result = EX_OSERR; 477 goto finish; 478 } 479 } 480 481 len = strlcpy(toolArgs->kernelPath, optarg, PATH_MAX); 482 if (len >= PATH_MAX) { 483 OSKextLog(/* kext */ NULL, 484 kOSKextLogErrorLevel | kOSKextLogGeneralFlag, 485 "Error: kernel filename length exceeds PATH_MAX"); 486 goto finish; 487 } 488 break; 489 490 case kOptLocalRoot: 491 toolArgs->requiredFlagsRepositoriesOnly |= 492 kOSKextOSBundleRequiredLocalRootFlag; 493 break; 494 495 case kOptLocalRootAll: 496 toolArgs->requiredFlagsAll |= 497 kOSKextOSBundleRequiredLocalRootFlag; 498 break; 499 500 case kOptNetworkRoot: 501 toolArgs->requiredFlagsRepositoriesOnly |= 502 kOSKextOSBundleRequiredNetworkRootFlag; 503 break; 504 505 case kOptNetworkRootAll: 506 toolArgs->requiredFlagsAll |= 507 kOSKextOSBundleRequiredNetworkRootFlag; 508 break; 509 510 case kOptAllLoaded: 511 toolArgs->needLoadedKextInfo = true; 512 break; 513 514 case kOptSafeBoot: 515 toolArgs->requiredFlagsRepositoriesOnly |= 516 kOSKextOSBundleRequiredSafeBootFlag; 517 break; 518 519 case kOptSafeBootAll: 520 toolArgs->requiredFlagsAll |= 521 kOSKextOSBundleRequiredSafeBootFlag; 522 break; 523 524 case kOptTests: 525 toolArgs->printTestResults = true; 526 break; 527 528#if !NO_BOOT_ROOT 529 case kOptUpdate: 530 case kOptCheckUpdate: 531 if (toolArgs->updateVolumeURL) { 532 OSKextLog(/* kext */ NULL, 533 kOSKextLogWarningLevel | kOSKextLogGeneralFlag, 534 "Warning: update volume already specified; using last."); 535 SAFE_RELEASE_NULL(toolArgs->updateVolumeURL); 536 } 537 // sanity check that the volume exists 538 if (stat(optarg, &sb)) { 539 OSKextLog(NULL,kOSKextLogWarningLevel|kOSKextLogFileAccessFlag, 540 "%s - %s.", optarg, strerror(errno)); 541 result = EX_NOINPUT; 542 goto finish; 543 } 544 545 scratchURL = CFURLCreateFromFileSystemRepresentation( 546 kCFAllocatorDefault, 547 (const UInt8 *)optarg, strlen(optarg), true); 548 if (!scratchURL) { 549 OSKextLogStringError(/* kext */ NULL); 550 result = EX_OSERR; 551 goto finish; 552 } 553 toolArgs->updateVolumeURL = CFRetain(scratchURL); 554 if (optchar == kOptCheckUpdate) { 555 toolArgs->expectUpToDate = true; 556 } 557 break; 558#endif /* !NO_BOOT_ROOT */ 559 560 case kOptQuiet: 561 beQuiet(); 562 break; 563 564 case kOptVerbose: 565 scratchResult = setLogFilterForOpt(*argc, *argv, 566 /* forceOnFlags */ kOSKextLogKextOrGlobalMask); 567 if (scratchResult != EX_OK) { 568 result = scratchResult; 569 goto finish; 570 } 571 break; 572 573 case kOptNoAuthentication: 574 toolArgs->skipAuthentication = true; 575 break; 576 577 case 0: 578 switch (longopt) { 579 case kLongOptMkext1: 580 case kLongOptMkext2: 581 // note kLongOptMkext == latest supported version 582 if (toolArgs->mkextPath) { 583 OSKextLog(/* kext */ NULL, 584 kOSKextLogWarningLevel | kOSKextLogGeneralFlag, 585 "Warning: output mkext file already specified; using last."); 586 } else { 587 toolArgs->mkextPath = malloc(PATH_MAX); 588 if (!toolArgs->mkextPath) { 589 OSKextLogMemError(); 590 result = EX_OSERR; 591 goto finish; 592 } 593 } 594 595 len = strlcpy(toolArgs->mkextPath, optarg, PATH_MAX); 596 if (len >= PATH_MAX) { 597 OSKextLog(/* kext */ NULL, 598 kOSKextLogErrorLevel | kOSKextLogGeneralFlag, 599 "Error: mkext filename length exceeds PATH_MAX"); 600 goto finish; 601 } 602 603 if (longopt == kLongOptMkext1) { 604 toolArgs->mkextVersion = 1; 605 } else if (longopt == kLongOptMkext2) { 606 toolArgs->mkextVersion = 2; 607 } else { 608 OSKextLog(/* kext */ NULL, 609 kOSKextLogErrorLevel | kOSKextLogGeneralFlag, 610 "Intenral error."); 611 } 612 break; 613 614 case kLongOptVolumeRoot: 615 if (toolArgs->volumeRootURL) { 616 OSKextLog(/* kext */ NULL, 617 kOSKextLogWarningLevel | kOSKextLogGeneralFlag, 618 "Warning: volume root already specified; using last."); 619 SAFE_RELEASE_NULL(toolArgs->volumeRootURL); 620 } 621 622 scratchURL = CFURLCreateFromFileSystemRepresentation( 623 kCFAllocatorDefault, 624 (const UInt8 *)optarg, strlen(optarg), true); 625 if (!scratchURL) { 626 OSKextLogStringError(/* kext */ NULL); 627 result = EX_OSERR; 628 goto finish; 629 } 630 631 toolArgs->volumeRootURL = CFRetain(scratchURL); 632 break; 633 634 635 case kLongOptSystemCaches: 636 toolArgs->updateSystemCaches = true; 637 setSystemExtensionsFolders(toolArgs); 638 break; 639 640 case kLongOptCompressed: 641 toolArgs->compress = true; 642 break; 643 644 case kLongOptUncompressed: 645 toolArgs->uncompress = true; 646 break; 647 648 case kLongOptSymbols: 649 if (toolArgs->symbolDirURL) { 650 OSKextLog(/* kext */ NULL, 651 kOSKextLogWarningLevel | kOSKextLogGeneralFlag, 652 "Warning: symbol directory already specified; using last."); 653 SAFE_RELEASE_NULL(toolArgs->symbolDirURL); 654 } 655 656 scratchURL = CFURLCreateFromFileSystemRepresentation( 657 kCFAllocatorDefault, 658 (const UInt8 *)optarg, strlen(optarg), true); 659 if (!scratchURL) { 660 OSKextLogStringError(/* kext */ NULL); 661 result = EX_OSERR; 662 goto finish; 663 } 664 665 toolArgs->symbolDirURL = CFRetain(scratchURL); 666 toolArgs->generatePrelinkedSymbols = true; 667 break; 668 669 case kLongOptSystemPrelinkedKernel: 670 scratchResult = setPrelinkedKernelArgs(toolArgs, 671 /* filename */ NULL); 672 if (scratchResult != EX_OK) { 673 result = scratchResult; 674 goto finish; 675 } 676 toolArgs->needLoadedKextInfo = true; 677 toolArgs->requiredFlagsRepositoriesOnly |= 678 kOSKextOSBundleRequiredLocalRootFlag; 679 break; 680 681 case kLongOptAllPersonalities: 682 toolArgs->includeAllPersonalities = true; 683 break; 684 685 case kLongOptNoLinkFailures: 686 toolArgs->noLinkFailures = true; 687 break; 688 689 case kLongOptStripSymbols: 690 toolArgs->stripSymbols = true; 691 break; 692 693#if !NO_BOOT_ROOT 694 case kLongOptInstaller: 695 toolArgs->installerCalled = true; 696 break; 697 case kLongOptCachesOnly: 698 toolArgs->cachesOnly = true; 699 break; 700#endif /* !NO_BOOT_ROOT */ 701 702 default: 703 /* Because we use ':', getopt_long doesn't print an error message. 704 */ 705 OSKextLog(/* kext */ NULL, 706 kOSKextLogErrorLevel | kOSKextLogGeneralFlag, 707 "unrecognized option %s", (*argv)[optind-1]); 708 goto finish; 709 break; 710 } 711 break; 712 713 default: 714 /* Because we use ':', getopt_long doesn't print an error message. 715 */ 716 OSKextLog(/* kext */ NULL, 717 kOSKextLogErrorLevel | kOSKextLogGeneralFlag, 718 "unrecognized option %s", (*argv)[optind-1]); 719 goto finish; 720 break; 721 722 } 723 724 /* Reset longindex, because getopt_long_only() is stupid and doesn't. 725 */ 726 longindex = -1; 727 } 728 729 /* Update the argc & argv seen by main() so that boot<>root calls 730 * handle remaining args. 731 */ 732 *argc -= optind; 733 *argv += optind; 734 735 /***** 736 * If we aren't doing a boot<>root update, record the kext & directory names 737 * from the command line. (If we are doing a boot<>root update, remaining 738 * command line args are processed later.) 739 */ 740 if (!toolArgs->updateVolumeURL) { 741 for (i = 0; i < *argc; i++) { 742 SAFE_RELEASE_NULL(scratchURL); 743 SAFE_RELEASE_NULL(scratchString); 744 745 scratchURL = CFURLCreateFromFileSystemRepresentation( 746 kCFAllocatorDefault, 747 (const UInt8 *)(*argv)[i], strlen((*argv)[i]), true); 748 if (!scratchURL) { 749 OSKextLogMemError(); 750 result = EX_OSERR; 751 goto finish; 752 } 753 CFArrayAppendValue(toolArgs->argURLs, scratchURL); 754 755 scratchString = CFURLCopyPathExtension(scratchURL); 756 if (scratchString && CFEqual(scratchString, CFSTR("kext"))) { 757 CFArrayAppendValue(toolArgs->namedKextURLs, scratchURL); 758 } else { 759 CFArrayAppendValue(toolArgs->repositoryURLs, scratchURL); 760 } 761 } 762 } 763 764 result = EX_OK; 765 766finish: 767 SAFE_RELEASE(scratchString); 768 SAFE_RELEASE(scratchNumber); 769 SAFE_RELEASE(scratchURL); 770 771 if (result == EX_USAGE) { 772 usage(kUsageLevelBrief); 773 } 774 return result; 775} 776 777/******************************************************************************* 778*******************************************************************************/ 779ExitStatus readPrelinkedKernelArgs( 780 KextcacheArgs * toolArgs, 781 int argc, 782 char * const * argv, 783 Boolean isLongopt) 784{ 785 char * filename = NULL; // do not free 786 787 if (optarg) { 788 filename = optarg; 789 } else if (isLongopt && optind < argc) { 790 filename = argv[optind]; 791 optind++; 792 } 793 794 if (filename && !filename[0]) { 795 filename = NULL; 796 } 797 798 return setPrelinkedKernelArgs(toolArgs, filename); 799} 800 801/******************************************************************************* 802*******************************************************************************/ 803ExitStatus setPrelinkedKernelArgs( 804 KextcacheArgs * toolArgs, 805 char * filename) 806{ 807 ExitStatus result = EX_USAGE; 808 809 if (toolArgs->prelinkedKernelPath) { 810 OSKextLog(/* kext */ NULL, 811 kOSKextLogWarningLevel | kOSKextLogGeneralFlag, 812 "Warning: prelinked kernel already specified; using last."); 813 } else { 814 toolArgs->prelinkedKernelPath = malloc(PATH_MAX); 815 if (!toolArgs->prelinkedKernelPath) { 816 OSKextLogMemError(); 817 result = EX_OSERR; 818 goto finish; 819 } 820 } 821 822 /* If we don't have a filename we construct a default one, automatically 823 * add the system extensions folders, and note that we're using default 824 * info. 825 */ 826 if (!filename) { 827#if NO_BOOT_ROOT 828 OSKextLog(/* kext */ NULL, 829 kOSKextLogErrorLevel | kOSKextLogGeneralFlag, 830 "Error: prelinked kernel filename required"); 831 goto finish; 832#else 833 if (!setDefaultPrelinkedKernel(toolArgs)) { 834 goto finish; 835 } 836 toolArgs->needDefaultPrelinkedKernelInfo = true; 837 setSystemExtensionsFolders(toolArgs); 838#endif /* NO_BOOT_ROOT */ 839 } else { 840 size_t len = strlcpy(toolArgs->prelinkedKernelPath, filename, PATH_MAX); 841 if (len >= PATH_MAX) { 842 OSKextLog(/* kext */ NULL, 843 kOSKextLogErrorLevel | kOSKextLogGeneralFlag, 844 "Error: prelinked kernel filename length exceeds PATH_MAX"); 845 goto finish; 846 } 847 } 848 849 result = EX_OK; 850finish: 851 return result; 852} 853 854 855#if !NO_BOOT_ROOT 856#ifndef kIOPMAssertNoIdleSystemSleep 857#define kIOPMAssertNoIdleSystemSleep \ 858 kIOPMAssertionTypePreventUserIdleSystemSleep 859#endif 860ExitStatus doUpdateVolume(KextcacheArgs *toolArgs) 861{ 862 ExitStatus rval; // no goto's in this function 863 int result; // errno-type value 864 IOReturn pmres = kIOReturnError; // init against future re-flow 865 IOPMAssertionID awakeForUpdate; // valid if pmres == 0 866 BRUpdateOpts_t opts = kBROptsNone; 867 868 if (toolArgs->forceUpdateFlag) { opts |= kBRUForceUpdateHelpers; } 869 if (toolArgs->expectUpToDate) { opts |= kBRUExpectUpToDate; } 870 if (toolArgs->cachesOnly) { opts |= kBRUCachesOnly; } 871 if (toolArgs->installerCalled) { 872 opts |= kBRUHelpersOptional; 873 opts |= kBRUForceUpdateHelpers; 874 } 875 876 // unless -F is passed, keep machine awake for for duration 877 // (including waiting for any volume locks with kextd) 878 if (toolArgs->lowPriorityFlag == false) { 879 pmres = IOPMAssertionCreateWithName(kIOPMAssertNoIdleSystemSleep, 880 kIOPMAssertionLevelOn, 881 CFSTR("com.apple.kextmanager.update"), 882 &awakeForUpdate); 883 if (pmres) { 884 OSKextLog(NULL, kOSKextLogWarningLevel | kOSKextLogGeneralFlag, 885 "Warning: couldn't block sleep during cache update"); 886 } 887 888 } 889 890 result = checkUpdateCachesAndBoots(toolArgs->updateVolumeURL, opts); 891 // translate known errno -> sysexits(3) value 892 switch (result) { 893 case ENOENT: 894 case EFTYPE: rval = EX_OSFILE; break; 895 default: rval = result; 896 } 897 898 if (toolArgs->lowPriorityFlag == false && pmres == 0) { 899 // drop assertion 900 if (IOPMAssertionRelease(awakeForUpdate)) 901 OSKextLog(NULL, kOSKextLogWarningLevel | kOSKextLogGeneralFlag, 902 "Warning: error re-enabling sleep after cache update"); 903 } 904 905 return rval; 906} 907 908/******************************************************************************* 909*******************************************************************************/ 910Boolean setDefaultKernel(KextcacheArgs * toolArgs) 911{ 912 Boolean result = FALSE; 913 size_t length = 0; 914 struct stat statBuf; 915 916 if (!toolArgs->kernelPath) { 917 toolArgs->kernelPath = malloc(PATH_MAX); 918 if (!toolArgs->kernelPath) { 919 OSKextLogMemError(); 920 result = EX_OSERR; 921 goto finish; 922 } 923 } 924 length = strlcpy(toolArgs->kernelPath, 925 kDefaultKernel, 926 PATH_MAX); 927 if (length >= PATH_MAX) { 928 OSKextLog(/* kext */ NULL, 929 kOSKextLogErrorLevel | kOSKextLogGeneralFlag, 930 "Error: kernel filename length exceeds PATH_MAX"); 931 goto finish; 932 } 933 934 if (EX_OK != statPath(toolArgs->kernelPath, &statBuf)) { 935 goto finish; 936 } 937 TIMESPEC_TO_TIMEVAL(&toolArgs->kernelTimes[0], &statBuf.st_atimespec); 938 TIMESPEC_TO_TIMEVAL(&toolArgs->kernelTimes[1], &statBuf.st_mtimespec); 939 940 result = TRUE; 941 942finish: 943 944 return result; 945} 946 947/******************************************************************************* 948*******************************************************************************/ 949Boolean setDefaultPrelinkedKernel(KextcacheArgs * toolArgs) 950{ 951 Boolean result = FALSE; 952 const char * prelinkedKernelFile = NULL; 953 size_t length = 0; 954 955 prelinkedKernelFile = 956 _kOSKextCachesRootFolder "/" _kOSKextStartupCachesSubfolder "/" 957 _kOSKextPrelinkedKernelBasename; 958 959 length = strlcpy(toolArgs->prelinkedKernelPath, 960 prelinkedKernelFile, PATH_MAX); 961 if (length >= PATH_MAX) { 962 OSKextLog(/* kext */ NULL, 963 kOSKextLogErrorLevel | kOSKextLogGeneralFlag, 964 "Error: prelinked kernel filename length exceeds PATH_MAX"); 965 goto finish; 966 } 967 968 result = TRUE; 969 970finish: 971 return result; 972} 973#endif /* !NO_BOOT_ROOT */ 974 975/******************************************************************************* 976*******************************************************************************/ 977void setSystemExtensionsFolders(KextcacheArgs * toolArgs) 978{ 979 CFArrayRef sysExtensionsFolders = OSKextGetSystemExtensionsFolderURLs(); 980 981 CFArrayAppendArray(toolArgs->argURLs, 982 sysExtensionsFolders, RANGE_ALL(sysExtensionsFolders)); 983 CFArrayAppendArray(toolArgs->repositoryURLs, 984 sysExtensionsFolders, RANGE_ALL(sysExtensionsFolders)); 985 986 return; 987} 988 989/******************************************************************************* 990*******************************************************************************/ 991static void 992waitForIOKitQuiescence(void) 993{ 994 kern_return_t kern_result = 0;; 995 mach_timespec_t waitTime = { 40, 0 }; 996 997 OSKextLog(/* kext */ NULL, 998 kOSKextLogProgressLevel | kOSKextLogIPCFlag, 999 "Waiting for I/O Kit to quiesce."); 1000 1001 kern_result = IOKitWaitQuiet(kIOMasterPortDefault, &waitTime); 1002 if (kern_result == kIOReturnTimeout) { 1003 OSKextLog(/* kext */ NULL, 1004 kOSKextLogErrorLevel | kOSKextLogIPCFlag, 1005 "IOKitWaitQuiet() timed out."); 1006 } else if (kern_result != kOSReturnSuccess) { 1007 OSKextLog(/* kext */ NULL, 1008 kOSKextLogErrorLevel | kOSKextLogGeneralFlag, 1009 "IOKitWaitQuiet() failed - %s.", 1010 safe_mach_error_string(kern_result)); 1011 } 1012} 1013 1014/******************************************************************************* 1015* Wait for the system to report that it's a good time to do work. We define a 1016* good time to be when the IOSystemLoadAdvisory API returns a combined level of 1017* kIOSystemLoadAdvisoryLevelGreat, and we'll wait up to kOSKextSystemLoadTimeout 1018* seconds for the system to enter that state before we begin our work. If there 1019* is an error in this function, we just return and get started with the work. 1020*******************************************************************************/ 1021static void 1022waitForGreatSystemLoad(void) 1023{ 1024 struct timeval currenttime; 1025 struct timeval endtime; 1026 struct timeval timeout; 1027 fd_set readfds; 1028 fd_set tmpfds; 1029 uint64_t systemLoadAdvisoryState = 0; 1030 uint32_t notifyStatus = 0; 1031 uint32_t usecs = 0; 1032 int systemLoadAdvisoryFileDescriptor = 0; // closed by notify_cancel() 1033 int systemLoadAdvisoryToken = 0; // must notify_cancel() 1034 int currentToken = 0; // do not notify_cancel() 1035 int myResult; 1036 1037 bzero(¤ttime, sizeof(currenttime)); 1038 bzero(&endtime, sizeof(endtime)); 1039 bzero(&timeout, sizeof(timeout)); 1040 1041 OSKextLog(/* kext */ NULL, 1042 kOSKextLogProgressLevel | kOSKextLogGeneralFlag, 1043 "Waiting for low system load."); 1044 1045 /* Register for SystemLoadAdvisory notifications */ 1046 1047 notifyStatus = notify_register_file_descriptor(kIOSystemLoadAdvisoryNotifyName, 1048 &systemLoadAdvisoryFileDescriptor, 1049 /* flags */ 0, &systemLoadAdvisoryToken); 1050 if (notifyStatus != NOTIFY_STATUS_OK) { 1051 goto finish; 1052 } 1053 1054 OSKextLog(/* kext */ NULL, 1055 kOSKextLogDebugLevel | kOSKextLogGeneralFlag, 1056 "Received initial system load status %llu", systemLoadAdvisoryState); 1057 1058 /* If it's a good time, we'll just return */ 1059 1060 notifyStatus = notify_get_state(systemLoadAdvisoryToken, &systemLoadAdvisoryState); 1061 if (notifyStatus != NOTIFY_STATUS_OK) { 1062 goto finish; 1063 } 1064 1065 if (systemLoadAdvisoryState == kIOSystemLoadAdvisoryLevelGreat) { 1066 goto finish; 1067 } 1068 1069 /* Set up the select timers */ 1070 1071 myResult = gettimeofday(¤ttime, NULL); 1072 if (myResult < 0) { 1073 goto finish; 1074 } 1075 1076 endtime = currenttime; 1077 endtime.tv_sec += kOSKextSystemLoadTimeout; 1078 1079 timeval_difference(&timeout, &endtime, ¤ttime); 1080 usecs = usecs_from_timeval(&timeout); 1081 1082 FD_ZERO(&readfds); 1083 FD_SET(systemLoadAdvisoryFileDescriptor, &readfds); 1084 1085 /* Check SystemLoadAdvisory notifications until it's a great time to 1086 * do work or we hit the timeout. 1087 */ 1088 1089 while (usecs) { 1090 /* Wait for notifications or the timeout */ 1091 1092 FD_COPY(&readfds, &tmpfds); 1093 myResult = select(systemLoadAdvisoryFileDescriptor + 1, 1094 &tmpfds, NULL, NULL, &timeout); 1095 if (myResult < 0) { 1096 goto finish; 1097 } 1098 1099 /* Set up the next timeout */ 1100 1101 myResult = gettimeofday(¤ttime, NULL); 1102 if (myResult < 0) { 1103 goto finish; 1104 } 1105 1106 timeval_difference(&timeout, &endtime, ¤ttime); 1107 usecs = usecs_from_timeval(&timeout); 1108 1109 /* Check the system load state */ 1110 1111 if (!FD_ISSET(systemLoadAdvisoryFileDescriptor, &tmpfds)) { 1112 continue; 1113 } 1114 1115 myResult = (int)read(systemLoadAdvisoryFileDescriptor, 1116 ¤tToken, sizeof(currentToken)); 1117 if (myResult < 0) { 1118 goto finish; 1119 } 1120 1121 /* The token is written in network byte order. */ 1122 currentToken = ntohl(currentToken); 1123 1124 if (currentToken != systemLoadAdvisoryToken) { 1125 continue; 1126 } 1127 1128 notifyStatus = notify_get_state(systemLoadAdvisoryToken, 1129 &systemLoadAdvisoryState); 1130 if (notifyStatus != NOTIFY_STATUS_OK) { 1131 goto finish; 1132 } 1133 1134 OSKextLog(/* kext */ NULL, 1135 kOSKextLogDebugLevel | kOSKextLogGeneralFlag, 1136 "Received updated system load status %llu", systemLoadAdvisoryState); 1137 1138 if (systemLoadAdvisoryState == kIOSystemLoadAdvisoryLevelGreat) { 1139 break; 1140 } 1141 } 1142 1143 OSKextLog(/* kext */ NULL, 1144 kOSKextLogDebugLevel | kOSKextLogGeneralFlag, 1145 "Pausing for another %d seconds to avoid work contention", 1146 kOSKextSystemLoadPauseTime); 1147 1148 /* We'll wait a random amount longer to avoid colliding with 1149 * other work that is waiting for a great time. 1150 */ 1151 sleep(kOSKextSystemLoadPauseTime); 1152 1153 OSKextLog(/* kext */ NULL, 1154 kOSKextLogDebugLevel | kOSKextLogGeneralFlag, 1155 "System load is low. Proceeding.\n"); 1156finish: 1157 if (systemLoadAdvisoryToken) { 1158 notify_cancel(systemLoadAdvisoryToken); 1159 } 1160 return; 1161} 1162 1163/******************************************************************************* 1164*******************************************************************************/ 1165static u_int 1166usecs_from_timeval(struct timeval *t) 1167{ 1168 u_int usecs = 0; 1169 1170 if (t) { 1171 usecs = (unsigned int)((t->tv_sec * 1000) + t->tv_usec); 1172 } 1173 1174 return usecs; 1175} 1176 1177/******************************************************************************* 1178*******************************************************************************/ 1179static void 1180timeval_from_usecs(struct timeval *t, u_int usecs) 1181{ 1182 if (t) { 1183 if (usecs > 0) { 1184 t->tv_sec = usecs / 1000; 1185 t->tv_usec = usecs % 1000; 1186 } else { 1187 bzero(t, sizeof(*t)); 1188 } 1189 } 1190} 1191 1192/******************************************************************************* 1193* dst = a - b 1194*******************************************************************************/ 1195static void 1196timeval_difference(struct timeval *dst, struct timeval *a, struct timeval *b) 1197{ 1198 u_int ausec = 0, busec = 0, dstusec = 0; 1199 1200 if (dst) { 1201 ausec = usecs_from_timeval(a); 1202 busec = usecs_from_timeval(b); 1203 1204 if (ausec > busec) { 1205 dstusec = ausec - busec; 1206 } 1207 1208 timeval_from_usecs(dst, dstusec); 1209 } 1210} 1211 1212#if !NO_BOOT_ROOT 1213/******************************************************************************* 1214*******************************************************************************/ 1215void setDefaultArchesIfNeeded(KextcacheArgs * toolArgs) 1216{ 1217 /* If no arches were explicitly specified, use the architecture of the 1218 * running kernel. 1219 */ 1220 if (toolArgs->explicitArch) { 1221 return; 1222 } 1223 1224 CFArrayRemoveAllValues(toolArgs->targetArchs); 1225 addArch(toolArgs, OSKextGetRunningKernelArchitecture()); 1226 1227 return; 1228} 1229#endif /* !NO_BOOT_ROOT */ 1230 1231/******************************************************************************* 1232********************************************************************************/ 1233void addArch( 1234 KextcacheArgs * toolArgs, 1235 const NXArchInfo * arch) 1236{ 1237 if (CFArrayContainsValue(toolArgs->targetArchs, 1238 RANGE_ALL(toolArgs->targetArchs), arch)) 1239 { 1240 return; 1241 } 1242 1243 CFArrayAppendValue(toolArgs->targetArchs, arch); 1244} 1245 1246/******************************************************************************* 1247*******************************************************************************/ 1248const NXArchInfo * addArchForName( 1249 KextcacheArgs * toolArgs, 1250 const char * archname) 1251{ 1252 const NXArchInfo * result = NULL; 1253 1254 result = NXGetArchInfoFromName(archname); 1255 if (!result) { 1256 goto finish; 1257 } 1258 1259 addArch(toolArgs, result); 1260 1261finish: 1262 return result; 1263} 1264 1265/******************************************************************************* 1266*******************************************************************************/ 1267void checkKextdSpawnedFilter(Boolean kernelFlag) 1268{ 1269 const char * environmentVariable = NULL; // do not free 1270 char * environmentLogFilterString = NULL; // do not free 1271 1272 if (kernelFlag) { 1273 environmentVariable = "KEXT_LOG_FILTER_KERNEL"; 1274 } else { 1275 environmentVariable = "KEXT_LOG_FILTER_USER"; 1276 } 1277 1278 environmentLogFilterString = getenv(environmentVariable); 1279 1280 /***** 1281 * If we have environment variables for a log spec, take the greater 1282 * of the log levels and OR together the flags from the environment's & 1283 * this process's command-line log specs. This way the most verbose setting 1284 * always applies. 1285 * 1286 * Otherwise, set the environment variable in case we spawn children. 1287 */ 1288 if (environmentLogFilterString) { 1289 OSKextLogSpec toolLogSpec = OSKextGetLogFilter(kernelFlag); 1290 OSKextLogSpec kextdLogSpec = (unsigned int)strtoul(environmentLogFilterString, NULL, 16); 1291 1292 OSKextLogSpec toolLogLevel = toolLogSpec & kOSKextLogLevelMask; 1293 OSKextLogSpec kextdLogLevel = kextdLogSpec & kOSKextLogLevelMask; 1294 OSKextLogSpec comboLogLevel = MAX(toolLogLevel, kextdLogLevel); 1295 1296 OSKextLogSpec toolLogFlags = toolLogSpec & kOSKextLogFlagsMask; 1297 OSKextLogSpec kextdLogFlags = kextdLogSpec & kOSKextLogFlagsMask; 1298 OSKextLogSpec comboLogFlags = toolLogFlags | kextdLogFlags | 1299 kOSKextLogKextOrGlobalMask; 1300 1301 OSKextSetLogFilter(comboLogLevel | comboLogFlags, kernelFlag); 1302 } else { 1303 char logSpecBuffer[16]; // enough for a 64-bit hex value 1304 1305 snprintf(logSpecBuffer, sizeof(logSpecBuffer), "0x%x", 1306 OSKextGetLogFilter(kernelFlag)); 1307 setenv(environmentVariable, logSpecBuffer, /* overwrite */ 1); 1308 } 1309 1310 return; 1311} 1312 1313/******************************************************************************* 1314*******************************************************************************/ 1315ExitStatus checkArgs(KextcacheArgs * toolArgs) 1316{ 1317 ExitStatus result = EX_USAGE; 1318 1319 if (!toolArgs->mkextPath && !toolArgs->prelinkedKernelPath && 1320 !toolArgs->updateVolumeURL && !toolArgs->updateSystemCaches) 1321 { 1322 OSKextLog(/* kext */ NULL, 1323 kOSKextLogErrorLevel | kOSKextLogGeneralFlag, 1324 "No work to do; check options and try again."); 1325 goto finish; 1326 } 1327 1328 if (toolArgs->volumeRootURL && !toolArgs->mkextPath && 1329 !toolArgs->prelinkedKernelPath) 1330 { 1331 OSKextLog(/* kext */ NULL, 1332 kOSKextLogErrorLevel | kOSKextLogGeneralFlag, 1333 "Use -%s only when creating an mkext archive or prelinked kernel.", 1334 kOptNameVolumeRoot); 1335 goto finish; 1336 } 1337 1338 if (!toolArgs->updateVolumeURL && !CFArrayGetCount(toolArgs->argURLs) && 1339 !toolArgs->compress && !toolArgs->uncompress) 1340 { 1341 OSKextLog(/* kext */ NULL, 1342 kOSKextLogErrorLevel | kOSKextLogGeneralFlag, 1343 "No kexts or directories specified."); 1344 goto finish; 1345 } 1346 1347 if (!toolArgs->compress && !toolArgs->uncompress) { 1348 toolArgs->compress = true; 1349 } else if (toolArgs->compress && toolArgs->uncompress) { 1350 OSKextLog(/* kext */ NULL, 1351 kOSKextLogErrorLevel | kOSKextLogGeneralFlag, 1352 "Both -%s and -%s specified; using -%s.", 1353 kOptNameCompressed, kOptNameUncompressed, kOptNameCompressed); 1354 toolArgs->compress = true; 1355 toolArgs->uncompress = false; 1356 } 1357 1358#if !NO_BOOT_ROOT 1359 if (toolArgs->forceUpdateFlag && toolArgs->cachesOnly) { 1360 OSKextLog(NULL, kOSKextLogErrorLevel | kOSKextLogGeneralFlag, 1361 "-%s (%-c) and %-s are mutually exclusive", 1362 kOptNameForce, kOptForce, kOptNameCachesOnly); 1363 goto finish; 1364 } 1365 if (toolArgs->forceUpdateFlag) { 1366 if (toolArgs->expectUpToDate || !toolArgs->updateVolumeURL) { 1367 OSKextLog(/* kext */ NULL, 1368 kOSKextLogErrorLevel | kOSKextLogGeneralFlag, 1369 "-%s (-%c) is allowed only with -%s (-%c).", 1370 kOptNameForce, kOptForce, kOptNameUpdate, kOptUpdate); 1371 goto finish; 1372 } 1373 } 1374 if (toolArgs->installerCalled) { 1375 if (toolArgs->expectUpToDate || !toolArgs->updateVolumeURL) { 1376 OSKextLog(/* kext */ NULL, 1377 kOSKextLogErrorLevel | kOSKextLogGeneralFlag, 1378 "-%s is allowed only with -%s (-%c).", 1379 kOptNameInstaller, kOptNameUpdate, kOptUpdate); 1380 goto finish; 1381 } 1382 } 1383 if (toolArgs->cachesOnly) { 1384 if (toolArgs->expectUpToDate || !toolArgs->updateVolumeURL) { 1385 OSKextLog(/* kext */ NULL, 1386 kOSKextLogErrorLevel | kOSKextLogGeneralFlag, 1387 "-%s is allowed only with -%s (-%c).", 1388 kOptNameCachesOnly, kOptNameUpdate, kOptUpdate); 1389 goto finish; 1390 } 1391 } 1392#endif /* !NO_BOOT_ROOT */ 1393 1394 if (toolArgs->updateVolumeURL) { 1395 if (toolArgs->mkextPath || toolArgs->prelinkedKernelPath) { 1396 OSKextLog(/* kext */ NULL, 1397 kOSKextLogErrorLevel | kOSKextLogGeneralFlag, 1398 "Can't create mkext or prelinked kernel when updating volumes."); 1399 } 1400 } 1401 1402#if !NO_BOOT_ROOT 1403 setDefaultArchesIfNeeded(toolArgs); 1404#endif /* !NO_BOOT_ROOT */ 1405 1406 /* 11860417 - we now support multiple extensions directories, get access and 1407 * mod times from extensions directory with the most current mode date. 1408 */ 1409 if (toolArgs->extensionsDirTimes[1].tv_sec == 0 && 1410 CFArrayGetCount(toolArgs->repositoryURLs)) { 1411 result = getLatestTimesFromCFURLArray(toolArgs->repositoryURLs, 1412 toolArgs->extensionsDirTimes); 1413 if (result != EX_OK) { 1414 OSKextLog(NULL, 1415 kOSKextLogErrorLevel | kOSKextLogGeneralFlag, 1416 "%s: Can't get mod times", __FUNCTION__); 1417 goto finish; 1418 } 1419 } 1420 1421#if !NO_BOOT_ROOT 1422 if (toolArgs->needDefaultPrelinkedKernelInfo && !toolArgs->kernelPath) { 1423 if (!setDefaultKernel(toolArgs)) { 1424 goto finish; 1425 } 1426 } 1427#endif /* !NO_BOOT_ROOT */ 1428 1429 if (toolArgs->prelinkedKernelPath && CFArrayGetCount(toolArgs->argURLs)) { 1430 struct stat myStatBuf; 1431 1432 if (!toolArgs->kernelPath) { 1433 OSKextLog(/* kext */ NULL, 1434 kOSKextLogErrorLevel | kOSKextLogGeneralFlag, 1435 "No kernel specified for prelinked kernel generation."); 1436 goto finish; 1437 } 1438 result = statPath(toolArgs->kernelPath, &myStatBuf); 1439 if (result != EX_OK) { 1440 goto finish; 1441 } 1442 TIMESPEC_TO_TIMEVAL(&toolArgs->kernelTimes[0], &myStatBuf.st_atimespec); 1443 TIMESPEC_TO_TIMEVAL(&toolArgs->kernelTimes[1], &myStatBuf.st_mtimespec); 1444 } 1445 1446 /* Updating system caches requires no additional kexts or repositories, 1447 * and must run as root. 1448 */ 1449 if (toolArgs->needDefaultPrelinkedKernelInfo || 1450 toolArgs->updateSystemCaches) { 1451 1452 if (CFArrayGetCount(toolArgs->namedKextURLs) || CFSetGetCount(toolArgs->kextIDs) || 1453 !CFEqual(toolArgs->repositoryURLs, OSKextGetSystemExtensionsFolderURLs())) { 1454 1455 OSKextLog(/* kext */ NULL, 1456 kOSKextLogErrorLevel | kOSKextLogGeneralFlag, 1457 "Custom kexts and repository directories are not allowed " 1458 "when updating system kext caches."); 1459 result = EX_USAGE; 1460 goto finish; 1461 1462 } 1463 1464 if (geteuid() != 0) { 1465 OSKextLog(/* kext */ NULL, 1466 kOSKextLogErrorLevel | kOSKextLogGeneralFlag, 1467 "You must be running as root to update system kext caches."); 1468 result = EX_NOPERM; 1469 goto finish; 1470 } 1471 } 1472 1473 result = EX_OK; 1474 1475finish: 1476 if (result == EX_USAGE) { 1477 usage(kUsageLevelBrief); 1478 } 1479 return result; 1480} 1481 1482/******************************************************************************* 1483*******************************************************************************/ 1484ExitStatus 1485getLoadedKextInfo( 1486 KextcacheArgs *toolArgs) 1487{ 1488 ExitStatus result = EX_SOFTWARE; 1489 CFArrayRef requestedIdentifiers = NULL; // must release 1490 1491 /* Let I/O Kit settle down before we poke at it. 1492 */ 1493 1494 (void) waitForIOKitQuiescence(); 1495 1496 /* Get the list of requested bundle IDs from the kernel and find all of 1497 * the associated kexts. 1498 */ 1499 1500 requestedIdentifiers = OSKextCopyAllRequestedIdentifiers(); 1501 if (!requestedIdentifiers) { 1502 goto finish; 1503 } 1504 1505 toolArgs->loadedKexts = OSKextCopyKextsWithIdentifiers(requestedIdentifiers); 1506 if (!toolArgs->loadedKexts) { 1507 goto finish; 1508 } 1509 1510 result = EX_OK; 1511 1512finish: 1513 SAFE_RELEASE(requestedIdentifiers); 1514 1515 return result; 1516} 1517 1518#pragma mark System Plist Caches 1519 1520/******************************************************************************* 1521*******************************************************************************/ 1522ExitStatus updateSystemPlistCaches(KextcacheArgs * toolArgs) 1523{ 1524 ExitStatus result = EX_OSERR; 1525 ExitStatus directoryResult = EX_OK; // flipped to error as needed 1526 CFArrayRef systemExtensionsURLs = NULL; // do not release 1527 CFArrayRef kexts = NULL; // must release 1528 CFURLRef folderURL = NULL; // do not release 1529 char folderPath[PATH_MAX] = ""; 1530 const NXArchInfo * startArch = OSKextGetArchitecture(); 1531 CFArrayRef directoryValues = NULL; // must release 1532 CFArrayRef personalities = NULL; // must release 1533 CFIndex count, i; 1534 1535 /* We only care about updating info for the system extensions folders. 1536 */ 1537 systemExtensionsURLs = OSKextGetSystemExtensionsFolderURLs(); 1538 if (!systemExtensionsURLs) { 1539 OSKextLogMemError(); 1540 result = EX_OSERR; 1541 goto finish; 1542 } 1543 1544 kexts = OSKextCreateKextsFromURLs(kCFAllocatorDefault, systemExtensionsURLs); 1545 if (!kexts) { 1546 goto finish; 1547 } 1548 1549 /* Update the global personalities & property-value caches, each per arch. 1550 */ 1551 for (i = 0; i < CFArrayGetCount(toolArgs->targetArchs); i++) { 1552 const NXArchInfo * targetArch = 1553 CFArrayGetValueAtIndex(toolArgs->targetArchs, i); 1554 1555 SAFE_RELEASE_NULL(personalities); 1556 1557 /* Set the active architecture for scooping out personalities and such. 1558 */ 1559 if (!OSKextSetArchitecture(targetArch)) { 1560 goto finish; 1561 } 1562 1563 personalities = OSKextCopyPersonalitiesOfKexts(kexts); 1564 if (!personalities) { 1565 goto finish; 1566 } 1567 1568 if (!_OSKextWriteCache(systemExtensionsURLs, CFSTR(kIOKitPersonalitiesKey), 1569 targetArch, _kOSKextCacheFormatIOXML, personalities)) { 1570 1571 goto finish; 1572 } 1573 1574 /* Loginwindow asks us for this property so let's spare lots of I/O 1575 * by caching it. This read function call updates the caches for us; 1576 * we don't use the output. 1577 */ 1578 if (!readSystemKextPropertyValues(CFSTR(kOSBundleHelperKey), targetArch, 1579 /* forceUpdate? */ true, /* values */ NULL)) { 1580 1581 goto finish; 1582 } 1583 } 1584 1585 /* Update per-directory caches. This is just KextIdentifiers any more. 1586 */ 1587 count = CFArrayGetCount(systemExtensionsURLs); 1588 for (i = 0; i < count; i++) { 1589 1590 folderURL = CFArrayGetValueAtIndex(systemExtensionsURLs, i); 1591 1592 if (!CFURLGetFileSystemRepresentation(folderURL, /* resolveToBase */ true, 1593 (UInt8 *)folderPath, sizeof(folderPath))) { 1594 1595 OSKextLogStringError(/* kext */ NULL); 1596 goto finish; 1597 } 1598 if (EX_OK != updateDirectoryCaches(toolArgs, folderURL)) { 1599 directoryResult = EX_OSERR; 1600 } else { 1601 OSKextLog(/* kext */ NULL, 1602 kOSKextLogBasicLevel | kOSKextLogGeneralFlag, 1603 "Directory caches updated for %s.", folderPath); 1604 } 1605 } 1606 1607 if (directoryResult == EX_OK) { 1608 result = EX_OK; 1609 } 1610 1611finish: 1612 SAFE_RELEASE(kexts); 1613 SAFE_RELEASE(directoryValues); 1614 SAFE_RELEASE(personalities); 1615 1616 OSKextSetArchitecture(startArch); 1617 1618 return result; 1619} 1620 1621/******************************************************************************* 1622*******************************************************************************/ 1623ExitStatus updateDirectoryCaches( 1624 KextcacheArgs * toolArgs, 1625 CFURLRef folderURL) 1626{ 1627 ExitStatus result = EX_OK; // optimistic! 1628 CFArrayRef kexts = NULL; // must release 1629 1630 kexts = OSKextCreateKextsFromURL(kCFAllocatorDefault, folderURL); 1631 if (!kexts) { 1632 result = EX_OSERR; 1633 goto finish; 1634 } 1635 1636 if (!_OSKextWriteIdentifierCacheForKextsInDirectory( 1637 kexts, folderURL, /* force? */ true)) { 1638 result = EX_OSERR; 1639 goto finish; 1640 } 1641 1642 result = EX_OK; 1643 1644finish: 1645 SAFE_RELEASE(kexts); 1646 return result; 1647} 1648 1649#pragma mark Misc Stuff 1650 1651/******************************************************************************* 1652*******************************************************************************/ 1653/******************************************************************************* 1654*******************************************************************************/ 1655/* Open Firmware (PPC only) has an upper limit of 16MB on file transfers, 1656 * so we'll limit ourselves just beneath that. 1657 */ 1658#define kOpenFirmwareMaxFileSize (16 * 1024 * 1024) 1659 1660ExitStatus createMkext( 1661 KextcacheArgs * toolArgs, 1662 Boolean * fatalOut) 1663{ 1664 struct timeval extDirsTimes[2]; 1665 ExitStatus result = EX_SOFTWARE; 1666 CFMutableArrayRef archiveKexts = NULL; // must release 1667 CFMutableArrayRef mkexts = NULL; // must release 1668 CFDataRef mkext = NULL; // must release 1669 const NXArchInfo *targetArch = NULL; // do not free 1670 int i; 1671 1672#if !NO_BOOT_ROOT 1673 /* Try a lock on the volume for the mkext being updated. 1674 * The lock prevents kextd from starting up a competing kextcache. 1675 */ 1676 if (!getenv("_com_apple_kextd_skiplocks")) { 1677 // xxx - updateBoots + related should return only sysexit-type values, not errno 1678 result = takeVolumeForPath(toolArgs->mkextPath); 1679 if (result != EX_OK) { 1680 goto finish; 1681 } 1682 } 1683#endif /* !NO_BOOT_ROOT */ 1684 1685 if (!createCFMutableArray(&mkexts, &kCFTypeArrayCallBacks)) { 1686 OSKextLogMemError(); 1687 result = EX_OSERR; 1688 *fatalOut = true; 1689 goto finish; 1690 } 1691 1692 if (!createCFMutableArray(&archiveKexts, &kCFTypeArrayCallBacks)) { 1693 OSKextLogMemError(); 1694 result = EX_OSERR; 1695 goto finish; 1696 } 1697 1698 for (i = 0; i < CFArrayGetCount(toolArgs->targetArchs); i++) { 1699 targetArch = CFArrayGetValueAtIndex(toolArgs->targetArchs, i); 1700 1701 SAFE_RELEASE_NULL(mkext); 1702 1703 if (!OSKextSetArchitecture(targetArch)) { 1704 OSKextLog(/* kext */ NULL, 1705 kOSKextLogErrorLevel | kOSKextLogGeneralFlag, 1706 "Can't set architecture %s to create mkext.", 1707 targetArch->name); 1708 result = EX_OSERR; 1709 goto finish; 1710 } 1711 1712 /***** 1713 * Figure out which kexts we're actually archiving. 1714 */ 1715 result = filterKextsForCache(toolArgs, archiveKexts, 1716 targetArch, fatalOut); 1717 if (result != EX_OK || *fatalOut) { 1718 goto finish; 1719 } 1720 1721 if (!CFArrayGetCount(archiveKexts)) { 1722 OSKextLog(/* kext */ NULL, 1723 kOSKextLogWarningLevel | kOSKextLogArchiveFlag, 1724 "No kexts found for architecture %s; skipping architecture.", 1725 targetArch->name); 1726 continue; 1727 } 1728 1729 if (toolArgs->mkextVersion == 2) { 1730 mkext = OSKextCreateMkext(kCFAllocatorDefault, archiveKexts, 1731 toolArgs->volumeRootURL, 1732 kOSKextOSBundleRequiredNone, toolArgs->compress); 1733 } else if (toolArgs->mkextVersion == 1) { 1734 mkext = createMkext1ForArch(targetArch, archiveKexts, 1735 toolArgs->compress); 1736 } 1737 if (!mkext) { 1738 // OSKextCreateMkext() logs an error 1739 result = EX_OSERR; 1740 goto finish; 1741 } 1742 if (targetArch == NXGetArchInfoFromName("ppc")) { 1743 if (CFDataGetLength(mkext) > kOpenFirmwareMaxFileSize) { 1744 OSKextLog(/* kext */ NULL, 1745 kOSKextLogErrorLevel | kOSKextLogArchiveFlag, 1746 "PPC archive is too large for Open Firmware; aborting."); 1747 result = EX_SOFTWARE; 1748 *fatalOut = true; 1749 goto finish; 1750 } 1751 } 1752 CFArrayAppendValue(mkexts, mkext); 1753 } 1754 1755 if (!CFArrayGetCount(mkexts)) { 1756 OSKextLog(/* kext */ NULL, 1757 kOSKextLogErrorLevel | kOSKextLogArchiveFlag, 1758 "No mkext archives created."); 1759 goto finish; 1760 } 1761 1762 /* Get access and mod times of the extensions directory with most 1763 * recent mod time. We now support multiple extensions directories. 1764 */ 1765 if (toolArgs->extensionsDirTimes[1].tv_sec != 0) { 1766 result = getLatestTimesFromCFURLArray(toolArgs->repositoryURLs, 1767 extDirsTimes); 1768 if (result != EX_OK) { 1769 goto finish; 1770 } 1771 1772 /* see if an extensions dir has been changed since we started */ 1773 if (timercmp(&toolArgs->extensionsDirTimes[1], &extDirsTimes[1], !=)) { 1774 OSKextLog(/* kext */ NULL, 1775 kOSKextLogErrorLevel | kOSKextLogGeneralFlag | kOSKextLogFileAccessFlag, 1776 "An extensions dir has changed since starting; " 1777 "not saving cache file"); 1778 result = kKextcacheExitStale; 1779 goto finish; 1780 } 1781 /* bump kexts modtime by 1 second */ 1782 extDirsTimes[1].tv_sec++; 1783 } 1784 1785 result = writeFatFile(toolArgs->mkextPath, mkexts, toolArgs->targetArchs, 1786 MKEXT_PERMS, 1787 (toolArgs->extensionsDirTimes[1].tv_sec != 0) ? extDirsTimes : NULL); 1788 if (result != EX_OK) { 1789 goto finish; 1790 } 1791 1792 result = EX_OK; 1793 OSKextLog(/* kext */ NULL, 1794 kOSKextLogBasicLevel | kOSKextLogGeneralFlag | kOSKextLogArchiveFlag, 1795 "Created mkext archive %s.", toolArgs->mkextPath); 1796 1797finish: 1798 SAFE_RELEASE(archiveKexts); 1799 SAFE_RELEASE(mkexts); 1800 SAFE_RELEASE(mkext); 1801 1802#if !NO_BOOT_ROOT 1803 putVolumeForPath(toolArgs->mkextPath, result); 1804#endif /* !NO_BOOT_ROOT */ 1805 1806 return result; 1807} 1808 1809/******************************************************************************* 1810*******************************************************************************/ 1811ExitStatus 1812getFileURLModTimePlusOne( 1813 CFURLRef fileURL, 1814 struct timeval *origModTime, 1815 struct timeval cacheFileTimes[2]) 1816{ 1817 ExitStatus result = EX_SOFTWARE; 1818 char path[PATH_MAX]; 1819 1820 if (!CFURLGetFileSystemRepresentation(fileURL, /* resolveToBase */ true, 1821 (UInt8 *)path, sizeof(path))) 1822 { 1823 OSKextLogStringError(/* kext */ NULL); 1824 goto finish; 1825 } 1826 result = getFilePathModTimePlusOne(path, origModTime, cacheFileTimes); 1827 1828finish: 1829 return result; 1830} 1831 1832/******************************************************************************* 1833*******************************************************************************/ 1834ExitStatus 1835getFilePathModTimePlusOne( 1836 const char * filePath, 1837 struct timeval * origModTime, 1838 struct timeval cacheFileTimes[2]) 1839{ 1840 ExitStatus result = EX_SOFTWARE; 1841 1842 result = getFilePathTimes(filePath, cacheFileTimes); 1843 if (result != EX_OK) { 1844 goto finish; 1845 } 1846 1847 /* If asked, check to see if mod time has changed */ 1848 if (origModTime != NULL) { 1849 if (timercmp(origModTime, &cacheFileTimes[1], !=)) { 1850 OSKextLog(/* kext */ NULL, 1851 kOSKextLogErrorLevel | kOSKextLogGeneralFlag | kOSKextLogFileAccessFlag, 1852 "Source item %s has changed since starting; " 1853 "not saving cache file", filePath); 1854 result = kKextcacheExitStale; 1855 goto finish; 1856 } 1857 } 1858 1859 /* bump modtime by 1 second */ 1860 cacheFileTimes[1].tv_sec++; 1861 result = EX_OK; 1862finish: 1863 return result; 1864} 1865 1866/******************************************************************************* 1867*******************************************************************************/ 1868typedef struct { 1869 KextcacheArgs * toolArgs; 1870 CFMutableArrayRef kextArray; 1871 Boolean error; 1872} FilterIDContext; 1873 1874void filterKextID(const void * vValue, void * vContext) 1875{ 1876 CFStringRef kextID = (CFStringRef)vValue; 1877 FilterIDContext * context = (FilterIDContext *)vContext; 1878 OSKextRef theKext = OSKextGetKextWithIdentifier(kextID); 1879 1880 /* This should really be a fatal error but embedded counts on 1881 * having optional kexts specified by identifier. 1882 */ 1883 if (!theKext) { 1884 char kextIDCString[KMOD_MAX_NAME]; 1885 1886 CFStringGetCString(kextID, kextIDCString, sizeof(kextIDCString), 1887 kCFStringEncodingUTF8); 1888 OSKextLog(/* kext */ NULL, 1889 kOSKextLogErrorLevel | kOSKextLogGeneralFlag, 1890 "Can't find kext with optional identifier %s; skipping.", kextIDCString); 1891#if 0 1892 OSKextLog(/* kext */ NULL, 1893 kOSKextLogErrorLevel | kOSKextLogGeneralFlag, 1894 "Error - can't find kext with identifier %s.", kextIDCString); 1895 context->error = TRUE; 1896#endif /* 0 */ 1897 goto finish; 1898 } 1899 1900 if (kextMatchesFilter(context->toolArgs, theKext, 1901 context->toolArgs->requiredFlagsAll) && 1902 !CFArrayContainsValue(context->kextArray, 1903 RANGE_ALL(context->kextArray), theKext)) 1904 { 1905 CFArrayAppendValue(context->kextArray, theKext); 1906 } 1907 1908finish: 1909 return; 1910} 1911 1912 1913/******************************************************************************* 1914*******************************************************************************/ 1915ExitStatus filterKextsForCache( 1916 KextcacheArgs * toolArgs, 1917 CFMutableArrayRef kextArray, 1918 const NXArchInfo * arch, 1919 Boolean * fatalOut) 1920{ 1921 ExitStatus result = EX_SOFTWARE; 1922 CFMutableArrayRef firstPassArray = NULL; 1923 OSKextRequiredFlags requiredFlags; 1924 CFIndex count, i; 1925 1926 if (!createCFMutableArray(&firstPassArray, &kCFTypeArrayCallBacks)) { 1927 OSKextLogMemError(); 1928 goto finish; 1929 } 1930 1931 /***** 1932 * Apply filters to select the kexts. 1933 * 1934 * If kexts have been specified by identifier, those are the only kexts we are going to use. 1935 * Otherwise run through the repository and named kexts and see which ones match the filter. 1936 */ 1937 if (CFSetGetCount(toolArgs->kextIDs)) { 1938 FilterIDContext context; 1939 1940 context.toolArgs = toolArgs; 1941 context.kextArray = firstPassArray; 1942 context.error = FALSE; 1943 CFSetApplyFunction(toolArgs->kextIDs, filterKextID, &context); 1944 1945 if (context.error) { 1946 goto finish; 1947 } 1948 1949 } else { 1950 1951 /* Set up the required flags for repository kexts. If any are set from 1952 * the command line, toss in "Root" and "Console" too. 1953 */ 1954 requiredFlags = toolArgs->requiredFlagsRepositoriesOnly | 1955 toolArgs->requiredFlagsAll; 1956 if (requiredFlags) { 1957 requiredFlags |= kOSKextOSBundleRequiredRootFlag | 1958 kOSKextOSBundleRequiredConsoleFlag; 1959 } 1960 1961 count = CFArrayGetCount(toolArgs->repositoryKexts); 1962 for (i = 0; i < count; i++) { 1963 1964 OSKextRef theKext = (OSKextRef)CFArrayGetValueAtIndex( 1965 toolArgs->repositoryKexts, i); 1966 1967 if (!kextMatchesFilter(toolArgs, theKext, requiredFlags)) { 1968 1969 char kextPath[PATH_MAX]; 1970 1971 if (!CFURLGetFileSystemRepresentation(OSKextGetURL(theKext), 1972 /* resolveToBase */ false, (UInt8 *)kextPath, sizeof(kextPath))) 1973 { 1974 strlcpy(kextPath, "(unknown)", sizeof(kextPath)); 1975 } 1976 1977 if (toolArgs->mkextPath) { 1978 OSKextLog(/* kext */ NULL, 1979 kOSKextLogStepLevel | kOSKextLogArchiveFlag, 1980 "%s does not match OSBundleRequired conditions; omitting.", 1981 kextPath); 1982 } else if (toolArgs->prelinkedKernelPath) { 1983 OSKextLog(/* kext */ NULL, 1984 kOSKextLogStepLevel | kOSKextLogArchiveFlag, 1985 "%s is not demanded by OSBundleRequired conditions.", 1986 kextPath); 1987 } 1988 continue; 1989 } 1990 1991 if (!CFArrayContainsValue(firstPassArray, RANGE_ALL(firstPassArray), theKext)) { 1992 CFArrayAppendValue(firstPassArray, theKext); 1993 } 1994 } 1995 1996 /* Set up the required flags for named kexts. If any are set from 1997 * the command line, toss in "Root" and "Console" too. 1998 */ 1999 requiredFlags = toolArgs->requiredFlagsAll; 2000 if (requiredFlags) { 2001 requiredFlags |= kOSKextOSBundleRequiredRootFlag | 2002 kOSKextOSBundleRequiredConsoleFlag; 2003 } 2004 2005 count = CFArrayGetCount(toolArgs->namedKexts); 2006 for (i = 0; i < count; i++) { 2007 OSKextRef theKext = (OSKextRef)CFArrayGetValueAtIndex( 2008 toolArgs->namedKexts, i); 2009 2010 if (!kextMatchesFilter(toolArgs, theKext, requiredFlags)) { 2011 2012 char kextPath[PATH_MAX]; 2013 2014 if (!CFURLGetFileSystemRepresentation(OSKextGetURL(theKext), 2015 /* resolveToBase */ false, (UInt8 *)kextPath, sizeof(kextPath))) 2016 { 2017 strlcpy(kextPath, "(unknown)", sizeof(kextPath)); 2018 } 2019 2020 if (toolArgs->mkextPath) { 2021 OSKextLog(/* kext */ NULL, 2022 kOSKextLogStepLevel | kOSKextLogArchiveFlag, 2023 "%s does not match OSBundleRequired conditions; omitting.", 2024 kextPath); 2025 } else if (toolArgs->prelinkedKernelPath) { 2026 OSKextLog(/* kext */ NULL, 2027 kOSKextLogStepLevel | kOSKextLogArchiveFlag, 2028 "%s is not demanded by OSBundleRequired conditions.", 2029 kextPath); 2030 } 2031 continue; 2032 } 2033 2034 if (!CFArrayContainsValue(firstPassArray, RANGE_ALL(firstPassArray), theKext)) { 2035 CFArrayAppendValue(firstPassArray, theKext); 2036 } 2037 } 2038 } 2039 2040 /***** 2041 * Take all the kexts that matched the filters above and check them for problems. 2042 */ 2043 CFArrayRemoveAllValues(kextArray); 2044 2045 count = CFArrayGetCount(firstPassArray); 2046 if (count) { 2047 OSKextIsInExcludeList(NULL, false); // prime the exclude list cache 2048 isInExceptionList(NULL, false); // prime the exception list cache 2049 for (i = count - 1; i >= 0; i--) { 2050 OSStatus sigResult; 2051 char kextPath[PATH_MAX]; 2052 OSKextRef theKext = (OSKextRef)CFArrayGetValueAtIndex( 2053 firstPassArray, i); 2054 2055 if (!CFURLGetFileSystemRepresentation(OSKextGetURL(theKext), 2056 /* resolveToBase */ false, (UInt8 *)kextPath, sizeof(kextPath))) 2057 { 2058 strlcpy(kextPath, "(unknown)", sizeof(kextPath)); 2059 } 2060 2061 /* Skip kexts we have no interest in for the current arch. 2062 */ 2063 if (!OSKextSupportsArchitecture(theKext, arch)) { 2064 OSKextLog(/* kext */ NULL, 2065 kOSKextLogStepLevel | kOSKextLogArchiveFlag, 2066 "%s doesn't support architecture '%s'; skipping.", kextPath, 2067 arch->name); 2068 continue; 2069 } 2070 2071 if (!OSKextIsValid(theKext)) { 2072 // xxx - should also use kOSKextLogArchiveFlag? 2073 OSKextLog(/* kext */ NULL, 2074 kOSKextLogErrorLevel | kOSKextLogArchiveFlag | 2075 kOSKextLogValidationFlag | kOSKextLogGeneralFlag, 2076 "%s is not valid; omitting.", kextPath); 2077 if (toolArgs->printTestResults) { 2078 OSKextLogDiagnostics(theKext, kOSKextDiagnosticsFlagAll); 2079 } 2080 continue; 2081 } 2082 2083 if (!toolArgs->skipAuthentication && !OSKextIsAuthentic(theKext)) { 2084 OSKextLog(/* kext */ NULL, 2085 kOSKextLogErrorLevel | kOSKextLogArchiveFlag | 2086 kOSKextLogAuthenticationFlag | kOSKextLogGeneralFlag, 2087 "%s is not authentic; omitting.", kextPath); 2088 if (toolArgs->printTestResults) { 2089 OSKextLogDiagnostics(theKext, kOSKextDiagnosticsFlagAll); 2090 } 2091 continue; 2092 } 2093 2094 if (OSKextIsInExcludeList(theKext, true)) { 2095 /* send alert about kext and message trace it 2096 */ 2097 addKextToAlertDict(&sExcludedKextAlertDict, theKext); 2098 messageTraceExcludedKext(theKext); 2099 OSKextLog(/* kext */ NULL, 2100 kOSKextLogErrorLevel | kOSKextLogArchiveFlag | 2101 kOSKextLogValidationFlag | kOSKextLogGeneralFlag, 2102 "%s is in exclude list; omitting.", kextPath); 2103 if (toolArgs->printTestResults) { 2104 OSKextLogDiagnostics(theKext, kOSKextDiagnosticsFlagAll); 2105 } 2106 continue; 2107 } 2108 2109 if (!OSKextResolveDependencies(theKext)) { 2110 OSKextLog(/* kext */ NULL, 2111 kOSKextLogWarningLevel | kOSKextLogArchiveFlag | 2112 kOSKextLogDependenciesFlag | kOSKextLogGeneralFlag, 2113 "%s is missing dependencies (including anyway; " 2114 "dependencies may be available from elsewhere)", kextPath); 2115 if (toolArgs->printTestResults) { 2116 OSKextLogDiagnostics(theKext, kOSKextDiagnosticsFlagAll); 2117 } 2118 } 2119 2120 if (isValidKextSigningTargetVolume(toolArgs->volumeRootURL) 2121 && (sigResult = checkKextSignature(theKext, true)) != 0 ) { 2122 2123 /* skip this invalid signed kext if we have a newer version 2124 * in our array 2125 */ 2126 if (_isNewerVersionInArray(theKext, firstPassArray)) { 2127 continue; 2128 } 2129 2130 Boolean inLibExtFolder = isInLibraryExtensionsFolder(theKext); 2131 if (inLibExtFolder) { 2132 addKextToAlertDict(&sNoLoadKextAlertDict, theKext); 2133 } 2134 else if (sigResult == CSSMERR_TP_CERT_REVOKED) { 2135 addKextToAlertDict(&sRevokedKextAlertDict, theKext); 2136 } 2137#if 0 // not yet 2138 else if (sigResult == errSecCSUnsigned) { 2139 addKextToAlertDict(&sUnsignedKextAlertDict, theKext); 2140 } 2141#endif 2142 else { 2143 addKextToAlertDict(&sInvalidSignedKextAlertDict, theKext); 2144 } 2145 2146 /* Do not load if kext has bad signature and comes from 2147 * /Library/Extensions/ 2148 */ 2149 if ( inLibExtFolder || sigResult == CSSMERR_TP_CERT_REVOKED ) { 2150 OSKextLog(/* kext */ NULL, 2151 kOSKextLogErrorLevel | kOSKextLogArchiveFlag | 2152 kOSKextLogAuthenticationFlag | kOSKextLogGeneralFlag, 2153 "%s has invalid signature; omitting.", 2154 kextPath); 2155 if (toolArgs->printTestResults) { 2156 OSKextLogDiagnostics(theKext, kOSKextDiagnosticsFlagAll); 2157 } 2158 continue; 2159 } 2160 CFStringRef myKextPath = NULL; // must release 2161 myKextPath = copyKextPath(theKext); 2162 if ( myKextPath ) { 2163 OSKextLogCFString(NULL, 2164 kOSKextLogErrorLevel | kOSKextLogLoadFlag, 2165 CFSTR("WARNING - Invalid signature %ld 0x%02lX for kext \"%@\""), 2166 (long)sigResult, (long)sigResult, myKextPath); 2167 SAFE_RELEASE(myKextPath); 2168 } 2169 } // isValidKextSigningTargetVolume... 2170 2171 if (!CFArrayContainsValue(kextArray, RANGE_ALL(kextArray), theKext)) { 2172 CFArrayAppendValue(kextArray, theKext); 2173 } 2174 } 2175 } 2176 2177 if (CFArrayGetCount(kextArray)) { 2178 recordKextLoadListForMT(kextArray); 2179 } 2180 2181 result = EX_OK; 2182 2183finish: 2184 return result; 2185} 2186 2187/* Scan the given array to see if we have a newer version of the given 2188 * kext (bundle ID). 2189 */ 2190static Boolean _isNewerVersionInArray( 2191 OSKextRef theKext, 2192 CFArrayRef theArray ) 2193{ 2194 CFStringRef theBundleID; // do not release 2195 CFStringRef theBundleVersion; // do not release 2196 OSKextVersion theKextVersion = -1; 2197 CFIndex myCount, i; 2198 Boolean myResult = false; 2199 2200 theBundleID = OSKextGetIdentifier(theKext); 2201 theBundleVersion = OSKextGetValueForInfoDictionaryKey( 2202 theKext, 2203 kCFBundleVersionKey ); 2204 if (theBundleVersion == NULL) { 2205 return(myResult); 2206 } 2207 theKextVersion = OSKextParseVersionCFString(theBundleVersion); 2208 if (theKextVersion == -1) { 2209 return(myResult); 2210 } 2211 2212 myCount = CFArrayGetCount(theArray); 2213 for (i = 0; i < myCount; i++) { 2214 OSKextRef myKext; // do not release 2215 CFStringRef myBundleID; // do not release 2216 CFStringRef myBundleVersion; // do not release 2217 OSKextVersion myKextVersion = -1; 2218 2219 2220 myKext = (OSKextRef) CFArrayGetValueAtIndex(theArray, i); 2221 myBundleID = OSKextGetIdentifier(myKext); 2222 2223 if ( CFStringCompare(myBundleID, theBundleID, 0) == kCFCompareEqualTo ) { 2224 myBundleVersion = OSKextGetValueForInfoDictionaryKey( 2225 myKext, 2226 kCFBundleVersionKey ); 2227 if (myBundleVersion == NULL) continue; 2228 myKextVersion = OSKextParseVersionCFString(myBundleVersion); 2229 if (myKextVersion > 0 && myKextVersion > theKextVersion ) { 2230 myResult = true; 2231 break; 2232 } 2233 } 2234 } 2235 return(myResult); 2236} 2237 2238/* We only want to check code signatures for volumes running 10.9 or 2239 * later version of OS (which means a Kernelcache v1.3 or later) 2240 */ 2241static Boolean isValidKextSigningTargetVolume(CFURLRef theVolRootURL) 2242{ 2243 static Boolean useCachedResult = false; 2244 static Boolean myResult = true; 2245 CFStringRef myVolRoot = NULL; // must release 2246 CFStringRef myPath = NULL; // must release 2247 CFURLRef myURL = NULL; // must release 2248 CFDictionaryRef myPlist = NULL; // must release 2249 2250 if (useCachedResult) { 2251 return(myResult); 2252 } 2253 else { 2254 useCachedResult = true; 2255 } 2256 2257 if (theVolRootURL) { 2258 myVolRoot = CFURLCopyFileSystemPath(theVolRootURL, 2259 kCFURLPOSIXPathStyle); 2260 if (myVolRoot == NULL) { 2261 goto finish; 2262 } 2263 myPath = CFStringCreateWithFormat( 2264 kCFAllocatorDefault, 2265 /* formatOptions */ NULL, 2266 CFSTR("%@%s"), 2267 myVolRoot, 2268 "/usr/standalone/bootcaches.plist" ); 2269 } 2270 else { 2271 myPath = CFStringCreateWithCString( 2272 kCFAllocatorDefault, 2273 "/usr/standalone/bootcaches.plist", 2274 kCFStringEncodingUTF8 ); 2275 } 2276 if (myPath == NULL) { 2277 goto finish; 2278 } 2279 2280 myURL = CFURLCreateWithFileSystemPath( kCFAllocatorDefault, 2281 myPath, 2282 kCFURLPOSIXPathStyle, 2283 false ); 2284 if (myURL && CFURLResourceIsReachable(myURL, NULL)) { 2285 CFReadStreamRef readStream = NULL; // must release 2286 2287 readStream = CFReadStreamCreateWithFile(kCFAllocatorDefault, myURL); 2288 if (readStream) { 2289 if (CFReadStreamOpen(readStream)) { 2290 /* read in contents of bootcaches.plist */ 2291 myPlist = CFPropertyListCreateWithStream( 2292 kCFAllocatorDefault, 2293 readStream, 2294 0, 2295 kCFPropertyListMutableContainersAndLeaves, 2296 NULL, NULL); 2297 CFReadStreamClose(readStream); 2298 } 2299 SAFE_RELEASE(readStream); 2300 } 2301 } 2302 if (myPlist) { 2303 CFDictionaryRef myDict = NULL; // do not release 2304 myDict = (CFDictionaryRef) CFDictionaryGetValue(myPlist, kBCPostBootKey); 2305 if (myDict) { 2306 if (!CFDictionaryContainsKey(myDict, kBCKernelcacheV3Key)) { 2307 myResult = false; 2308 } 2309 } 2310 } 2311finish: 2312 SAFE_RELEASE(myPlist); 2313 SAFE_RELEASE(myURL); 2314 SAFE_RELEASE(myPath); 2315 SAFE_RELEASE(myVolRoot); 2316 2317 return(myResult); 2318} 2319 2320/******************************************************************************* 2321*******************************************************************************/ 2322Boolean 2323kextMatchesFilter( 2324 KextcacheArgs * toolArgs, 2325 OSKextRef theKext, 2326 OSKextRequiredFlags requiredFlags) 2327{ 2328 Boolean result = false; 2329 Boolean needLoadedKextInfo = toolArgs->needLoadedKextInfo && 2330 (OSKextGetArchitecture() == OSKextGetRunningKernelArchitecture()); 2331 2332 if (needLoadedKextInfo) { 2333 result = (requiredFlags && OSKextMatchesRequiredFlags(theKext, requiredFlags)) || 2334 CFArrayContainsValue(toolArgs->loadedKexts, RANGE_ALL(toolArgs->loadedKexts), theKext); 2335 } else { 2336 result = OSKextMatchesRequiredFlags(theKext, requiredFlags); 2337 } 2338 2339 return result; 2340} 2341 2342/******************************************************************************* 2343 * Creates a list of architectures to generate prelinked kernel slices for by 2344 * selecting the requested architectures for which the kernel has a slice. 2345 * Warns when a requested architecture does not have a corresponding kernel 2346 * slice. 2347 *******************************************************************************/ 2348ExitStatus 2349createPrelinkedKernelArchs( 2350 KextcacheArgs * toolArgs, 2351 CFMutableArrayRef * prelinkArchsOut) 2352{ 2353 ExitStatus result = EX_OSERR; 2354 CFMutableArrayRef kernelArchs = NULL; // must release 2355 CFMutableArrayRef prelinkArchs = NULL; // must release 2356 const NXArchInfo * targetArch = NULL; // do not free 2357 u_int i = 0; 2358 2359 result = readFatFileArchsWithPath(toolArgs->kernelPath, &kernelArchs); 2360 if (result != EX_OK) { 2361 goto finish; 2362 } 2363 2364 prelinkArchs = CFArrayCreateMutableCopy(kCFAllocatorDefault, 2365 /* capacity */ 0, toolArgs->targetArchs); 2366 if (!prelinkArchs) { 2367 OSKextLogMemError(); 2368 result = EX_OSERR; 2369 goto finish; 2370 } 2371 2372 for (i = 0; i < CFArrayGetCount(prelinkArchs); ++i) { 2373 targetArch = CFArrayGetValueAtIndex(prelinkArchs, i); 2374 if (!CFArrayContainsValue(kernelArchs, 2375 RANGE_ALL(kernelArchs), targetArch)) 2376 { 2377 OSKextLog(/* kext */ NULL, 2378 kOSKextLogWarningLevel | kOSKextLogArchiveFlag, 2379 "Kernel file %s does not contain requested arch: %s", 2380 toolArgs->kernelPath, targetArch->name); 2381 CFArrayRemoveValueAtIndex(prelinkArchs, i); 2382 i--; 2383 continue; 2384 } 2385 } 2386 2387 *prelinkArchsOut = (CFMutableArrayRef) CFRetain(prelinkArchs); 2388 result = EX_OK; 2389 2390finish: 2391 SAFE_RELEASE(kernelArchs); 2392 SAFE_RELEASE(prelinkArchs); 2393 2394 return result; 2395} 2396 2397/******************************************************************************* 2398 * If the existing prelinked kernel has a valid timestamp, this reads the slices 2399 * out of that prelinked kernel so we don't have to regenerate them. 2400 *******************************************************************************/ 2401ExitStatus 2402createExistingPrelinkedSlices( 2403 KextcacheArgs * toolArgs, 2404 CFMutableArrayRef * existingSlicesOut, 2405 CFMutableArrayRef * existingArchsOut) 2406{ 2407 struct timeval existingFileTimes[2]; 2408 struct timeval prelinkFileTimes[2]; 2409 ExitStatus result = EX_SOFTWARE; 2410 2411 /* If we aren't updating the system prelinked kernel, then we don't want 2412 * to reuse any existing slices. 2413 */ 2414 if (!toolArgs->needDefaultPrelinkedKernelInfo) { 2415 result = EX_OK; 2416 goto finish; 2417 } 2418 2419 bzero(&existingFileTimes, sizeof(existingFileTimes)); 2420 bzero(&prelinkFileTimes, sizeof(prelinkFileTimes)); 2421 2422 result = getFilePathTimes(toolArgs->prelinkedKernelPath, 2423 existingFileTimes); 2424 if (result != EX_OK) { 2425 goto finish; 2426 } 2427 2428 result = getExpectedPrelinkedKernelModTime(toolArgs, 2429 prelinkFileTimes, NULL); 2430 if (result != EX_OK) { 2431 goto finish; 2432 } 2433 2434 /* We are testing that the existing prelinked kernel still has a valid 2435 * timestamp by comparing it to the timestamp we are going to use for 2436 * the new prelinked kernel. If they are equal, we can reuse slices 2437 * from the existing prelinked kernel. 2438 */ 2439 if (!timevalcmp(&existingFileTimes[1], &prelinkFileTimes[1], ==)) { 2440 result = EX_SOFTWARE; 2441 goto finish; 2442 } 2443 2444 result = readMachOSlices(toolArgs->prelinkedKernelPath, 2445 existingSlicesOut, existingArchsOut, NULL, NULL); 2446 if (result != EX_OK) { 2447 existingSlicesOut = NULL; 2448 existingArchsOut = NULL; 2449 goto finish; 2450 } 2451 2452 result = EX_OK; 2453 2454finish: 2455 return result; 2456} 2457 2458static void setVolumeRootInAlertDict(CFURLRef theURL, 2459 CFMutableDictionaryRef theDict); 2460 2461/******************************************************************************* 2462*******************************************************************************/ 2463ExitStatus 2464createPrelinkedKernel( 2465 KextcacheArgs * toolArgs) 2466{ 2467 ExitStatus result = EX_OSERR; 2468 struct timeval prelinkFileTimes[2]; 2469 CFMutableArrayRef generatedArchs = NULL; // must release 2470 CFMutableArrayRef generatedSymbols = NULL; // must release 2471 CFMutableArrayRef existingArchs = NULL; // must release 2472 CFMutableArrayRef existingSlices = NULL; // must release 2473 CFMutableArrayRef prelinkArchs = NULL; // must release 2474 CFMutableArrayRef prelinkSlices = NULL; // must release 2475 CFDataRef prelinkSlice = NULL; // must release 2476 CFDictionaryRef sliceSymbols = NULL; // must release 2477 const NXArchInfo * targetArch = NULL; // do not free 2478 Boolean updateModTime = false; 2479 u_int numArchs = 0; 2480 u_int i = 0; 2481 int j = 0; 2482 2483 bzero(&prelinkFileTimes, sizeof(prelinkFileTimes)); 2484 2485#if !NO_BOOT_ROOT 2486 /* Try a lock on the volume for the prelinked kernel being updated. 2487 * The lock prevents kextd from starting up a competing kextcache. 2488 */ 2489 if (!getenv("_com_apple_kextd_skiplocks")) { 2490 // xxx - updateBoots * related should return only sysexit-type values, not errno 2491 result = takeVolumeForPath(toolArgs->prelinkedKernelPath); 2492 if (result != EX_OK) { 2493 goto finish; 2494 } 2495 } 2496#endif /* !NO_BOOT_ROOT */ 2497 2498 result = createPrelinkedKernelArchs(toolArgs, &prelinkArchs); 2499 if (result != EX_OK) { 2500 goto finish; 2501 } 2502 numArchs = (u_int)CFArrayGetCount(prelinkArchs); 2503 2504 /* If we're generating symbols, we'll regenerate all slices. 2505 */ 2506 if (!toolArgs->symbolDirURL) { 2507 result = createExistingPrelinkedSlices(toolArgs, 2508 &existingSlices, &existingArchs); 2509 if (result != EX_OK) { 2510 SAFE_RELEASE_NULL(existingSlices); 2511 SAFE_RELEASE_NULL(existingArchs); 2512 } 2513 } 2514 2515 prelinkSlices = CFArrayCreateMutable(kCFAllocatorDefault, 2516 numArchs, &kCFTypeArrayCallBacks); 2517 generatedSymbols = CFArrayCreateMutable(kCFAllocatorDefault, 2518 numArchs, &kCFTypeArrayCallBacks); 2519 generatedArchs = CFArrayCreateMutable(kCFAllocatorDefault, 2520 numArchs, NULL); 2521 if (!prelinkSlices || !generatedSymbols || !generatedArchs) { 2522 OSKextLogMemError(); 2523 result = EX_OSERR; 2524 goto finish; 2525 } 2526 2527 for (i = 0; i < numArchs; i++) { 2528 targetArch = CFArrayGetValueAtIndex(prelinkArchs, i); 2529 2530 SAFE_RELEASE_NULL(prelinkSlice); 2531 SAFE_RELEASE_NULL(sliceSymbols); 2532 2533 /* We always create a new prelinked kernel for the current 2534 * running architecture if asked, but we'll reuse existing slices 2535 * for other architectures if possible. 2536 */ 2537 if (existingArchs && 2538 targetArch != OSKextGetRunningKernelArchitecture()) 2539 { 2540 j = (int)CFArrayGetFirstIndexOfValue(existingArchs, 2541 RANGE_ALL(existingArchs), targetArch); 2542 if (j != -1) { 2543 prelinkSlice = CFArrayGetValueAtIndex(existingSlices, j); 2544 CFArrayAppendValue(prelinkSlices, prelinkSlice); 2545 prelinkSlice = NULL; 2546 OSKextLog(/* kext */ NULL, 2547 kOSKextLogDebugLevel | kOSKextLogArchiveFlag, 2548 "Using existing prelinked slice for arch %s", 2549 targetArch->name); 2550 continue; 2551 } 2552 } 2553 2554 OSKextLog(/* kext */ NULL, 2555 kOSKextLogDebugLevel | kOSKextLogArchiveFlag, 2556 "Generating a new prelinked slice for arch %s", 2557 targetArch->name); 2558 2559 result = createPrelinkedKernelForArch(toolArgs, &prelinkSlice, 2560 &sliceSymbols, targetArch); 2561 if (result != EX_OK) { 2562 goto finish; 2563 } 2564 2565 CFArrayAppendValue(prelinkSlices, prelinkSlice); 2566 CFArrayAppendValue(generatedSymbols, sliceSymbols); 2567 CFArrayAppendValue(generatedArchs, targetArch); 2568 } 2569 2570 result = getExpectedPrelinkedKernelModTime(toolArgs, 2571 prelinkFileTimes, &updateModTime); 2572 if (result != EX_OK) { 2573 goto finish; 2574 } 2575 2576 result = writeFatFile(toolArgs->prelinkedKernelPath, prelinkSlices, 2577 prelinkArchs, MKEXT_PERMS, 2578 (updateModTime) ? prelinkFileTimes : NULL); 2579 if (result != EX_OK) { 2580 goto finish; 2581 } 2582 2583 if (toolArgs->symbolDirURL) { 2584 result = writePrelinkedSymbols(toolArgs->symbolDirURL, 2585 generatedSymbols, generatedArchs); 2586 if (result != EX_OK) { 2587 goto finish; 2588 } 2589 } 2590 2591 OSKextLog(/* kext */ NULL, 2592 kOSKextLogBasicLevel | kOSKextLogGeneralFlag | kOSKextLogArchiveFlag, 2593 "Created prelinked kernel %s.", 2594 toolArgs->prelinkedKernelPath); 2595 2596 result = EX_OK; 2597 2598finish: 2599 if (sNoLoadKextAlertDict) { 2600 /* notify kextd that we have some nonsigned kexts going into the 2601 * kernel cache. 2602 */ 2603 if (toolArgs->volumeRootURL) { 2604 setVolumeRootInAlertDict(toolArgs->volumeRootURL, 2605 sNoLoadKextAlertDict); 2606 } 2607 postNoteAboutKexts(CFSTR("No Load Kext Notification"), 2608 sNoLoadKextAlertDict ); 2609 } 2610 2611 if (sRevokedKextAlertDict) { 2612 /* notify kextd that we have some kexts with revoked certificate. 2613 */ 2614 if (toolArgs->volumeRootURL) { 2615 setVolumeRootInAlertDict(toolArgs->volumeRootURL, 2616 sRevokedKextAlertDict); 2617 } 2618 postNoteAboutKexts(CFSTR("Revoked Cert Kext Notification"), 2619 sRevokedKextAlertDict ); 2620 } 2621#if 0 // not yet 2622 if (sUnsignedKextAlertDict) { 2623 /* notify kextd that we have some unsigned kexts going into the 2624 * kernel cache. 2625 */ 2626 if (toolArgs->volumeRootURL) { 2627 setVolumeRootInAlertDict(toolArgs->volumeRootURL, 2628 sUnsignedKextAlertDict); 2629 } 2630 postNoteAboutKexts(CFSTR("Unsigned Kext Notification"), 2631 sUnsignedKextAlertDict); 2632 } 2633#endif 2634 2635 if (sInvalidSignedKextAlertDict) { 2636 /* notify kextd that we have some invalid signed kexts going into the 2637 * kernel cache. 2638 */ 2639 if (toolArgs->volumeRootURL) { 2640 setVolumeRootInAlertDict(toolArgs->volumeRootURL, 2641 sInvalidSignedKextAlertDict); 2642 } 2643 postNoteAboutKexts(CFSTR("Invalid Signature Kext Notification"), 2644 sInvalidSignedKextAlertDict); 2645 } 2646 2647 if (sExcludedKextAlertDict) { 2648 /* notify kextd that we have some excluded kexts going into the 2649 * kernel cache. 2650 */ 2651 if (toolArgs->volumeRootURL) { 2652 setVolumeRootInAlertDict(toolArgs->volumeRootURL, 2653 sExcludedKextAlertDict); 2654 } 2655 postNoteAboutKexts(CFSTR("Excluded Kext Notification"), 2656 sExcludedKextAlertDict); 2657 } 2658 2659 SAFE_RELEASE(generatedArchs); 2660 SAFE_RELEASE(generatedSymbols); 2661 SAFE_RELEASE(existingArchs); 2662 SAFE_RELEASE(existingSlices); 2663 SAFE_RELEASE(prelinkArchs); 2664 SAFE_RELEASE(prelinkSlices); 2665 SAFE_RELEASE(prelinkSlice); 2666 SAFE_RELEASE(sliceSymbols); 2667 2668#if !NO_BOOT_ROOT 2669 putVolumeForPath(toolArgs->prelinkedKernelPath, result); 2670#endif /* !NO_BOOT_ROOT */ 2671 2672 return result; 2673} 2674 2675static void setVolumeRootInAlertDict(CFURLRef theURL, 2676 CFMutableDictionaryRef theDict) 2677{ 2678 CFStringRef myVolRoot; 2679 2680 myVolRoot = (CFStringRef) 2681 CFDictionaryGetValue(theDict, 2682 CFSTR("VolRootKey")); 2683 if (myVolRoot == NULL) { 2684 myVolRoot = CFURLCopyFileSystemPath(theURL, 2685 kCFURLPOSIXPathStyle); 2686 if (myVolRoot) { 2687 CFDictionarySetValue(theDict, 2688 CFSTR("VolRootKey"), 2689 myVolRoot); 2690 SAFE_RELEASE(myVolRoot); 2691 } 2692 } 2693 return; 2694} 2695 2696/******************************************************************************* 2697*******************************************************************************/ 2698ExitStatus createPrelinkedKernelForArch( 2699 KextcacheArgs * toolArgs, 2700 CFDataRef * prelinkedKernelOut, 2701 CFDictionaryRef * prelinkedSymbolsOut, 2702 const NXArchInfo * archInfo) 2703{ 2704 ExitStatus result = EX_OSERR; 2705 CFMutableArrayRef prelinkKexts = NULL; 2706 CFDataRef kernelImage = NULL; 2707 CFDataRef prelinkedKernel = NULL; 2708 uint32_t flags = 0; 2709 Boolean fatalOut = false; 2710 Boolean kernelSupportsKASLR = false; 2711 macho_seek_result machoResult; 2712 const UInt8 * kernelStart; 2713 const UInt8 * kernelEnd; 2714 2715 /* Retrieve the kernel image for the requested architecture. 2716 */ 2717 kernelImage = readMachOSliceForArch(toolArgs->kernelPath, archInfo, /* checkArch */ TRUE); 2718 if (!kernelImage) { 2719 OSKextLog(/* kext */ NULL, 2720 kOSKextLogErrorLevel | kOSKextLogArchiveFlag | kOSKextLogFileAccessFlag, 2721 "Failed to read kernel file."); 2722 goto finish; 2723 } 2724 2725 /* Set the architecture in the OSKext library */ 2726 2727 if (!OSKextSetArchitecture(archInfo)) { 2728 OSKextLog(/* kext */ NULL, 2729 kOSKextLogErrorLevel | kOSKextLogGeneralFlag, 2730 "Can't set architecture %s to create prelinked kernel.", 2731 archInfo->name); 2732 result = EX_OSERR; 2733 goto finish; 2734 } 2735 2736 /***** 2737 * Figure out which kexts we're actually archiving. 2738 * This uses toolArgs->allKexts, which must already be created. 2739 */ 2740 prelinkKexts = CFArrayCreateMutable(kCFAllocatorDefault, 0, 2741 &kCFTypeArrayCallBacks); 2742 if (!prelinkKexts) { 2743 OSKextLogMemError(); 2744 result = EX_OSERR; 2745 goto finish; 2746 } 2747 2748 result = filterKextsForCache(toolArgs, prelinkKexts, 2749 archInfo, &fatalOut); 2750 if (result != EX_OK || fatalOut) { 2751 goto finish; 2752 } 2753 2754 result = EX_OSERR; 2755 2756 if (!CFArrayGetCount(prelinkKexts)) { 2757 OSKextLog(/* kext */ NULL, 2758 kOSKextLogErrorLevel | kOSKextLogArchiveFlag, 2759 "No kexts found for architecture %s.", 2760 archInfo->name); 2761 goto finish; 2762 } 2763 2764 /* Create the prelinked kernel from the given kernel and kexts */ 2765 2766 flags |= (toolArgs->noLinkFailures) ? kOSKextKernelcacheNeedAllFlag : 0; 2767 flags |= (toolArgs->skipAuthentication) ? kOSKextKernelcacheSkipAuthenticationFlag : 0; 2768 flags |= (toolArgs->printTestResults) ? kOSKextKernelcachePrintDiagnosticsFlag : 0; 2769 flags |= (toolArgs->includeAllPersonalities) ? kOSKextKernelcacheIncludeAllPersonalitiesFlag : 0; 2770 flags |= (toolArgs->stripSymbols) ? kOSKextKernelcacheStripSymbolsFlag : 0; 2771 2772 kernelStart = CFDataGetBytePtr(kernelImage); 2773 kernelEnd = kernelStart + CFDataGetLength(kernelImage) - 1; 2774 machoResult = macho_find_dysymtab(kernelStart, kernelEnd, NULL); 2775 /* this kernel supports KASLR if there is a LC_DYSYMTAB load command */ 2776 kernelSupportsKASLR = (machoResult == macho_seek_result_found); 2777 if (kernelSupportsKASLR) { 2778 flags |= kOSKextKernelcacheKASLRFlag; 2779 } 2780 2781 prelinkedKernel = OSKextCreatePrelinkedKernel(kernelImage, prelinkKexts, 2782 toolArgs->volumeRootURL, flags, prelinkedSymbolsOut); 2783 if (!prelinkedKernel) { 2784 OSKextLog(/* kext */ NULL, 2785 kOSKextLogErrorLevel | kOSKextLogArchiveFlag, 2786 "Failed to generate prelinked kernel."); 2787 result = EX_OSERR; 2788 goto finish; 2789 } 2790 2791 /* Compress the prelinked kernel if needed */ 2792 2793 if (toolArgs->compress) { 2794 *prelinkedKernelOut = compressPrelinkedSlice(prelinkedKernel, kernelSupportsKASLR); 2795 } else { 2796 *prelinkedKernelOut = CFRetain(prelinkedKernel); 2797 } 2798 2799 if (!*prelinkedKernelOut) { 2800 goto finish; 2801 } 2802 2803 result = EX_OK; 2804 2805finish: 2806 SAFE_RELEASE(kernelImage); 2807 SAFE_RELEASE(prelinkKexts); 2808 SAFE_RELEASE(prelinkedKernel); 2809 2810 return result; 2811} 2812 2813/***************************************************************************** 2814 *****************************************************************************/ 2815ExitStatus 2816getExpectedPrelinkedKernelModTime( 2817 KextcacheArgs * toolArgs, 2818 struct timeval cacheFileTimes[2], 2819 Boolean * updateModTimeOut) 2820{ 2821 struct timeval kextTimes[2]; 2822 struct timeval kernelTimes[2]; 2823 ExitStatus result = EX_SOFTWARE; 2824 Boolean updateModTime = false; 2825 2826 /* bail out if we don't have modtimes for extensions directory or kernel file 2827 */ 2828 if (toolArgs->extensionsDirTimes[1].tv_sec == 0 || 2829 toolArgs->kernelTimes[1].tv_sec == 0) { 2830 result = EX_OK; 2831 goto finish; 2832 } 2833 2834 result = getLatestTimesFromCFURLArray(toolArgs->repositoryURLs, 2835 kextTimes); 2836 if (result != EX_OK) { 2837 goto finish; 2838 } 2839 2840 /* bump kexts modtime by 1 second */ 2841 kextTimes[1].tv_sec++; 2842 2843 /* Check kernel mod time */ 2844 result = getFilePathModTimePlusOne(toolArgs->kernelPath, 2845 &toolArgs->kernelTimes[1], kernelTimes); 2846 if (result != EX_OK) { 2847 goto finish; 2848 } 2849 2850 /* Get the access and mod times of the later modified of the kernel 2851 * and kext repository. 2852 */ 2853 if (timercmp(&kextTimes[1], &kernelTimes[1], >)) { 2854 cacheFileTimes[0].tv_sec = kextTimes[0].tv_sec; // access time 2855 cacheFileTimes[0].tv_usec = kextTimes[0].tv_usec; 2856 cacheFileTimes[1].tv_sec = kextTimes[1].tv_sec; // mod time 2857 cacheFileTimes[1].tv_usec = kextTimes[1].tv_usec; 2858 } else { 2859 cacheFileTimes[0].tv_sec = kernelTimes[0].tv_sec; // access time 2860 cacheFileTimes[0].tv_usec = kernelTimes[0].tv_usec; 2861 cacheFileTimes[1].tv_sec = kernelTimes[1].tv_sec; // mod time 2862 cacheFileTimes[1].tv_usec = kernelTimes[1].tv_usec; 2863 } 2864 2865 /* Set the mod time of the kernelcache relative to the kernel */ 2866 updateModTime = true; 2867 result = EX_OK; 2868 2869finish: 2870 if (updateModTimeOut) *updateModTimeOut = updateModTime; 2871 2872 return result; 2873} 2874 2875/********************************************************************* 2876 *********************************************************************/ 2877ExitStatus 2878compressPrelinkedKernel( 2879 const char * prelinkPath, 2880 Boolean compress) 2881{ 2882 ExitStatus result = EX_SOFTWARE; 2883 struct timeval prelinkedKernelTimes[2]; 2884 CFMutableArrayRef prelinkedSlices = NULL; // must release 2885 CFMutableArrayRef prelinkedArchs = NULL; // must release 2886 CFDataRef prelinkedSlice = NULL; // must release 2887 const NXArchInfo * archInfo = NULL; // do not free 2888 const u_char * sliceBytes = NULL; // do not free 2889 mode_t fileMode = 0; 2890 int i = 0; 2891 2892 result = readMachOSlices(prelinkPath, &prelinkedSlices, 2893 &prelinkedArchs, &fileMode, prelinkedKernelTimes); 2894 if (result != EX_OK) { 2895 goto finish; 2896 } 2897 2898 /* Compress/uncompress each slice of the prelinked kernel. 2899 */ 2900 2901 for (i = 0; i < CFArrayGetCount(prelinkedSlices); ++i) { 2902 2903 SAFE_RELEASE_NULL(prelinkedSlice); 2904 prelinkedSlice = CFArrayGetValueAtIndex(prelinkedSlices, i); 2905 2906 if (compress) { 2907 const PrelinkedKernelHeader *header = (const PrelinkedKernelHeader *) 2908 CFDataGetBytePtr(prelinkedSlice); 2909 2910 prelinkedSlice = compressPrelinkedSlice(prelinkedSlice, 2911 (OSSwapHostToBigInt32(header->prelinkVersion) == 1)); 2912 if (!prelinkedSlice) { 2913 result = EX_DATAERR; 2914 goto finish; 2915 } 2916 } else { 2917 prelinkedSlice = uncompressPrelinkedSlice(prelinkedSlice); 2918 if (!prelinkedSlice) { 2919 result = EX_DATAERR; 2920 goto finish; 2921 } 2922 } 2923 2924 CFArraySetValueAtIndex(prelinkedSlices, i, prelinkedSlice); 2925 } 2926 SAFE_RELEASE_NULL(prelinkedSlice); 2927 2928 /* Snow Leopard prelinked kernels are not wrapped in a fat header, so we 2929 * have to decompress the prelinked kernel and look at the mach header 2930 * to get the architecture information. 2931 */ 2932 2933 if (!prelinkedArchs && CFArrayGetCount(prelinkedSlices) == 1) { 2934 if (!createCFMutableArray(&prelinkedArchs, NULL)) { 2935 OSKextLogMemError(); 2936 result = EX_OSERR; 2937 goto finish; 2938 } 2939 2940 sliceBytes = CFDataGetBytePtr( 2941 CFArrayGetValueAtIndex(prelinkedSlices, 0)); 2942 2943 archInfo = getThinHeaderPageArch(sliceBytes); 2944 if (archInfo) { 2945 CFArrayAppendValue(prelinkedArchs, archInfo); 2946 } else { 2947 SAFE_RELEASE_NULL(prelinkedArchs); 2948 } 2949 } 2950 2951 /* If we still don't have architecture information, then something 2952 * definitely went wrong. 2953 */ 2954 2955 if (!prelinkedArchs) { 2956 OSKextLog(/* kext */ NULL, 2957 kOSKextLogErrorLevel | kOSKextLogArchiveFlag, 2958 "Couldn't determine prelinked kernel's architecture"); 2959 result = EX_SOFTWARE; 2960 goto finish; 2961 } 2962 2963 result = writeFatFile(prelinkPath, prelinkedSlices, 2964 prelinkedArchs, fileMode, prelinkedKernelTimes); 2965 if (result != EX_OK) { 2966 goto finish; 2967 } 2968 2969 result = EX_OK; 2970 2971finish: 2972 SAFE_RELEASE(prelinkedSlices); 2973 SAFE_RELEASE(prelinkedArchs); 2974 SAFE_RELEASE(prelinkedSlice); 2975 2976 return result; 2977} 2978 2979#pragma mark Boot!=Root 2980 2981 2982/******************************************************************************* 2983* usage() 2984*******************************************************************************/ 2985void usage(UsageLevel usageLevel) 2986{ 2987 fprintf(stderr, 2988 "usage: %1$s <mkext_flag> [options] [--] [kext or directory] ...\n" 2989 " %1$s -prelinked-kernel <filename> [options] [--] [kext or directory]\n" 2990 " %1$s -system-prelinked-kernel\n" 2991 " %1$s [options] -prelinked-kernel\n" 2992#if !NO_BOOT_ROOT 2993 " %1$s -update-volume <volume> [options]\n" 2994#endif /* !NO_BOOT_ROOT */ 2995 " %1$s -system-caches [options]\n" 2996 "\n", 2997 progname); 2998 2999 if (usageLevel == kUsageLevelBrief) { 3000 fprintf(stderr, "use %s -%s for an explanation of each option\n", 3001 progname, kOptNameHelp); 3002 } 3003 3004 if (usageLevel == kUsageLevelBrief) { 3005 return; 3006 } 3007 3008 fprintf(stderr, "-%s <filename>: create an mkext (latest supported version)\n", 3009 kOptNameMkext); 3010 fprintf(stderr, "-%s <filename>: create an mkext (version 2)\n", 3011 kOptNameMkext2); 3012 fprintf(stderr, "-%s <filename> (-%c): create an mkext (version 1)\n", 3013 kOptNameMkext1, kOptMkext); 3014 fprintf(stderr, "-%s [<filename>] (-%c):\n" 3015 " create/update prelinked kernel (must be last if no filename given)\n", 3016 kOptNamePrelinkedKernel, kOptPrelinkedKernel); 3017 fprintf(stderr, "-%s:\n" 3018 " create/update system prelinked kernel\n", 3019 kOptNameSystemPrelinkedKernel); 3020#if !NO_BOOT_ROOT 3021 fprintf(stderr, "-%s <volume> (-%c): update system kext caches for <volume>\n", 3022 kOptNameUpdate, kOptUpdate); 3023 fprintf(stderr, "-%s called us, modify behavior appropriately\n", 3024 kOptNameInstaller); 3025 fprintf(stderr, "-%s skips updating any helper partitions even if they appear out of date\n", 3026 kOptNameCachesOnly); 3027#endif /* !NO_BOOT_ROOT */ 3028#if 0 3029// don't print this system-use option 3030 fprintf(stderr, "-%c <volume>:\n" 3031 " check system kext caches for <volume> (nonzero exit if out of date)\n", 3032 kOptCheckUpdate); 3033#endif 3034 fprintf(stderr, "-%s: update system kext info caches for the root volume\n", 3035 kOptNameSystemCaches); 3036 fprintf(stderr, "\n"); 3037 3038 fprintf(stderr, 3039 "kext or directory: Consider kext or all kexts in directory for inclusion\n"); 3040 fprintf(stderr, "-%s <bundle_id> (-%c):\n" 3041 " include the kext whose CFBundleIdentifier is <bundle_id>\n", 3042 kOptNameBundleIdentifier, kOptBundleIdentifier); 3043 fprintf(stderr, "-%s <volume>:\n" 3044 " Save kext paths in an mkext archive or prelinked kernel " 3045 " relative to <volume>\n", 3046 kOptNameVolumeRoot); 3047 fprintf(stderr, "-%s <kernel_filename> (-%c): Use kernel_filename for a prelinked kernel\n", 3048 kOptNameKernel, kOptKernel); 3049 fprintf(stderr, "-%s (-%c): Include all kexts ever loaded in prelinked kernel\n", 3050 kOptNameAllLoaded, kOptAllLoaded); 3051#if !NO_BOOT_ROOT 3052 fprintf(stderr, "-%s (-%c): Update volumes even if they look up to date\n", 3053 kOptNameForce, kOptForce); 3054 fprintf(stderr, "\n"); 3055#endif /* !NO_BOOT_ROOT */ 3056 3057 fprintf(stderr, "-%s (-%c): Add 'Local-Root' kexts from directories to an mkext file\n", 3058 kOptNameLocalRoot, kOptLocalRoot); 3059 fprintf(stderr, "-%s (-%c): Add 'Local-Root' kexts to an mkext file\n", 3060 kOptNameLocalRootAll, kOptLocalRootAll); 3061 fprintf(stderr, "-%s (-%c): Add 'Network-Root' kexts from directories to an mkext file\n", 3062 kOptNameNetworkRoot, kOptNetworkRoot); 3063 fprintf(stderr, "-%s (-%c): Add 'Network-Root' kexts to an mkext file\n", 3064 kOptNameNetworkRootAll, kOptNetworkRootAll); 3065 fprintf(stderr, "-%s (-%c): Add 'Safe Boot' kexts from directories to an mkext file\n", 3066 kOptNameSafeBoot, kOptSafeBoot); 3067 fprintf(stderr, "-%s (-%c): Add 'Safe Boot' kexts to an mkext file\n", 3068 kOptNameSafeBootAll, kOptSafeBootAll); 3069 fprintf(stderr, "\n"); 3070 3071 fprintf(stderr, "-%s <archname>:\n" 3072 " include architecture <archname> in created cache(s)\n", 3073 kOptNameArch); 3074 fprintf(stderr, "-%c: run at low priority\n", 3075 kOptLowPriorityFork); 3076 fprintf(stderr, "\n"); 3077 3078 fprintf(stderr, "-%s (-%c): quiet mode: print no informational or error messages\n", 3079 kOptNameQuiet, kOptQuiet); 3080 fprintf(stderr, "-%s [ 0-6 | 0x<flags> ] (-%c):\n" 3081 " verbose mode; print info about analysis & loading\n", 3082 kOptNameVerbose, kOptVerbose); 3083 fprintf(stderr, "\n"); 3084 3085 fprintf(stderr, "-%s (-%c):\n" 3086 " print diagnostics for kexts with problems\n", 3087 kOptNameTests, kOptTests); 3088 fprintf(stderr, "-%s (-%c): don't authenticate kexts (for use during development)\n", 3089 kOptNameNoAuthentication, kOptNoAuthentication); 3090 fprintf(stderr, "\n"); 3091 3092 fprintf(stderr, "-%s (-%c): print this message and exit\n", 3093 kOptNameHelp, kOptHelp); 3094 3095 return; 3096} 3097