1/* 2 * kextload_main.c 3 * kext_tools 4 * 5 * Created by Nik Gervae on 11/08/08. 6 * Copyright 2008 Apple Inc. All rights reserved. 7 * 8 */ 9#include "kextload_main.h" 10#include "kext_tools_util.h" 11 12#include <libc.h> 13#include <servers/bootstrap.h> 14#include <sysexits.h> 15 16#include <IOKit/kext/KextManager.h> 17#include <IOKit/kext/KextManagerPriv.h> 18#include <IOKit/kext/kextmanager_types.h> 19#include <IOKit/kext/OSKextPrivate.h> 20 21#pragma mark Constants 22/******************************************************************************* 23* Constants 24*******************************************************************************/ 25 26#pragma mark Global/Static Variables 27/******************************************************************************* 28* Global/Static Variables 29*******************************************************************************/ 30const char * progname = "(unknown)"; 31static Boolean sKextdActive = FALSE; 32 33#pragma mark Main Routine 34/******************************************************************************* 35* Global variables. 36*******************************************************************************/ 37ExitStatus 38main(int argc, char * const * argv) 39{ 40 ExitStatus result = EX_SOFTWARE; 41 KextloadArgs toolArgs; 42 43 /***** 44 * Find out what the program was invoked as. 45 */ 46 progname = rindex(argv[0], '/'); 47 if (progname) { 48 progname++; // go past the '/' 49 } else { 50 progname = (char *)argv[0]; 51 } 52 53 /* Set the OSKext log callback right away. 54 */ 55 OSKextSetLogOutputFunction(&tool_log); 56 57 /***** 58 * Process args & check for permission to load. 59 */ 60 result = readArgs(argc, argv, &toolArgs); 61 if (result != EX_OK) { 62 if (result == kKextloadExitHelp) { 63 result = EX_OK; 64 } 65 goto finish; 66 } 67 68 result = checkArgs(&toolArgs); 69 if (result != EX_OK) { 70 goto finish; 71 } 72 73 result = checkAccess(); 74 if (result != EX_OK) { 75 goto finish; 76 } 77 78 /***** 79 * Assemble the list of URLs to scan, in this order (the OSKext lib inverts it 80 * for last-opened-wins semantics): 81 * 1. System repository directories (if not asking kextd to load). 82 * 2. Named kexts (always given after -repository & -dependency on command line). 83 * 3. Named repository directories (-repository/-r). 84 * 4. Named dependencies get priority (-dependency/-d). 85 * 86 * #2 is necessary since one might try to run kextload on two kexts, 87 * one of which depends on the other. 88 */ 89 if (!sKextdActive) { 90 CFArrayRef sysExtFolders = OSKextGetSystemExtensionsFolderURLs(); 91 if (!sysExtFolders) { 92 OSKextLog(/* kext */ NULL, 93 kOSKextLogErrorLevel | kOSKextLogGeneralFlag, 94 "Can't get system extensions folders."); 95 result = EX_OSERR; 96 goto finish; 97 } 98 CFArrayAppendArray(toolArgs.scanURLs, 99 sysExtFolders, RANGE_ALL(sysExtFolders)); 100 } 101 CFArrayAppendArray(toolArgs.scanURLs, toolArgs.kextURLs, 102 RANGE_ALL(toolArgs.kextURLs)); 103 CFArrayAppendArray(toolArgs.scanURLs, toolArgs.repositoryURLs, 104 RANGE_ALL(toolArgs.repositoryURLs)); 105 CFArrayAppendArray(toolArgs.scanURLs, toolArgs.dependencyURLs, 106 RANGE_ALL(toolArgs.dependencyURLs)); 107 108 if (sKextdActive) { 109 result = loadKextsViaKextd(&toolArgs); 110 } else { 111 result = loadKextsIntoKernel(&toolArgs); 112 } 113 114finish: 115 116 /* We're actually not going to free anything else because we're exiting! 117 */ 118 exit(result); 119 120 SAFE_RELEASE(toolArgs.kextIDs); 121 SAFE_RELEASE(toolArgs.dependencyURLs); 122 SAFE_RELEASE(toolArgs.repositoryURLs); 123 SAFE_RELEASE(toolArgs.kextURLs); 124 SAFE_RELEASE(toolArgs.scanURLs); 125 SAFE_RELEASE(toolArgs.allKexts); 126 127 return result; 128} 129 130#pragma mark Major Subroutines 131 132/******************************************************************************* 133* Major Subroutines 134*******************************************************************************/ 135ExitStatus 136readArgs( 137 int argc, 138 char * const * argv, 139 KextloadArgs * toolArgs) 140{ 141 ExitStatus result = EX_USAGE; 142 ExitStatus scratchResult = EX_USAGE; 143 int optchar; 144 int longindex; 145 CFStringRef scratchString = NULL; // must release 146 CFURLRef scratchURL = NULL; // must release 147 uint32_t i; 148 149 /* Set up default arg values. 150 */ 151 bzero(toolArgs, sizeof(*toolArgs)); 152 153 /***** 154 * Allocate collection objects needed for reading args. 155 */ 156 if (!createCFMutableArray(&toolArgs->kextIDs, &kCFTypeArrayCallBacks) || 157 !createCFMutableArray(&toolArgs->dependencyURLs, &kCFTypeArrayCallBacks) || 158 !createCFMutableArray(&toolArgs->repositoryURLs, &kCFTypeArrayCallBacks) || 159 !createCFMutableArray(&toolArgs->kextURLs, &kCFTypeArrayCallBacks) || 160 !createCFMutableArray(&toolArgs->scanURLs, &kCFTypeArrayCallBacks)) { 161 162 result = EX_OSERR; 163 OSKextLogMemError(); 164 exit(result); 165 } 166 167 while ((optchar = getopt_long_only(argc, (char * const *)argv, 168 kOptChars, sOptInfo, &longindex)) != -1) { 169 170 SAFE_RELEASE_NULL(scratchString); 171 SAFE_RELEASE_NULL(scratchURL); 172 173 switch (optchar) { 174 case kOptHelp: 175 usage(kUsageLevelFull); 176 result = kKextloadExitHelp; 177 goto finish; 178 break; 179 180 case kOptBundleIdentifier: 181 scratchString = CFStringCreateWithCString(kCFAllocatorDefault, 182 optarg, kCFStringEncodingUTF8); 183 if (!scratchString) { 184 OSKextLogMemError(); 185 result = EX_OSERR; 186 goto finish; 187 } 188 CFArrayAppendValue(toolArgs->kextIDs, scratchString); 189 break; 190 191 case kOptDependency: 192 case kOptRepository: 193 scratchURL = CFURLCreateFromFileSystemRepresentation( 194 kCFAllocatorDefault, 195 (const UInt8 *)optarg, strlen(optarg), true); 196 if (!scratchURL) { 197 OSKextLogStringError(/* kext */ NULL); 198 result = EX_OSERR; 199 goto finish; 200 } 201 CFArrayAppendValue((optchar == kOptDependency) ? 202 toolArgs->dependencyURLs : toolArgs->repositoryURLs, 203 scratchURL); 204 break; 205 206 case kOptQuiet: 207 beQuiet(); 208 break; 209 210 case kOptVerbose: 211 scratchResult = setLogFilterForOpt(argc, argv, /* forceOnFlags */ 0); 212 if (scratchResult != EX_OK) { 213 result = scratchResult; 214 goto finish; 215 } 216 break; 217 218 case kOptNoCaches: 219 OSKextLog(/* kext */ NULL, 220 kOSKextLogWarningLevel | kOSKextLogGeneralFlag, 221 "Notice: -%s (-%c) ignored; use kextutil(8) to test kexts.", 222 kOptNameNoCaches, kOptNoCaches); 223 break; 224 225 case kOptNoLoadedCheck: 226 OSKextLog(/* kext */ NULL, 227 kOSKextLogWarningLevel | kOSKextLogGeneralFlag, 228 "Notice: -%s (-%c) ignored.", 229 kOptNameNoLoadedCheck, kOptNoLoadedCheck); 230 break; 231 232 case kOptTests: 233 OSKextLog(/* kext */ NULL, 234 kOSKextLogWarningLevel | kOSKextLogGeneralFlag, 235 "Notice: -%s (-%c) ignored; use kextutil(8) to test kexts.", 236 kOptNameTests, kOptTests); 237 break; 238 239 case 0: 240 switch (longopt) { 241 default: 242 OSKextLog(/* kext */ NULL, 243 kOSKextLogWarningLevel | kOSKextLogGeneralFlag, 244 "Use kextutil(8) for development loading of kexts."); 245 goto finish; 246 break; 247 } 248 break; 249 250 default: 251 OSKextLog(/* kext */ NULL, 252 kOSKextLogWarningLevel | kOSKextLogGeneralFlag, 253 "Use kextutil(8) for development loading of kexts."); 254 goto finish; 255 break; 256 257 } /* switch (optchar) */ 258 } /* while (optchar = getopt_long_only(...) */ 259 260 /***** 261 * Record the kext names from the command line. 262 */ 263 for (i = optind; (int)i < argc; i++) { 264 SAFE_RELEASE_NULL(scratchURL); 265 scratchURL = CFURLCreateFromFileSystemRepresentation( 266 kCFAllocatorDefault, 267 (const UInt8 *)argv[i], strlen(argv[i]), true); 268 if (!scratchURL) { 269 result = EX_OSERR; 270 OSKextLogMemError(); 271 goto finish; 272 } 273 CFArrayAppendValue(toolArgs->kextURLs, scratchURL); 274 } 275 276 result = EX_OK; 277 278finish: 279 SAFE_RELEASE(scratchString); 280 SAFE_RELEASE(scratchURL); 281 282 if (result == EX_USAGE) { 283 usage(kUsageLevelBrief); 284 } 285 return result; 286} 287 288/******************************************************************************* 289*******************************************************************************/ 290ExitStatus 291checkArgs(KextloadArgs * toolArgs) 292{ 293 ExitStatus result = EX_USAGE; 294 295 if (!CFArrayGetCount(toolArgs->kextURLs) && 296 !CFArrayGetCount(toolArgs->kextIDs)) { 297 298 OSKextLog(/* kext */ NULL, 299 kOSKextLogErrorLevel | kOSKextLogGeneralFlag, 300 "No kernel extensions specified; name kernel extension bundles\n" 301 " following options, or use -%s (-%c).", 302 kOptNameBundleIdentifier, kOptBundleIdentifier); 303 goto finish; 304 } 305 306 result = EX_OK; 307 308finish: 309 if (result == EX_USAGE) { 310 usage(kUsageLevelBrief); 311 } 312 return result; 313} 314 315/******************************************************************************* 316*******************************************************************************/ 317ExitStatus checkAccess(void) 318{ 319 ExitStatus result = EX_OK; 320#if !TARGET_OS_EMBEDDED 321 kern_return_t kern_result = kOSReturnError; 322 mach_port_t kextd_port = MACH_PORT_NULL; 323 324 kern_result = bootstrap_look_up(bootstrap_port, 325 (char *)KEXTD_SERVER_NAME, &kextd_port); 326 327 if (kern_result == kOSReturnSuccess) { 328 sKextdActive = TRUE; 329 } else { 330 if (geteuid() == 0) { 331 OSKextLog(/* kext */ NULL, 332 kOSKextLogBasicLevel | kOSKextLogGeneralFlag | 333 kOSKextLogLoadFlag | kOSKextLogIPCFlag, 334 "Can't contact kextd; attempting to load directly into kernel."); 335 } else { 336 OSKextLog(/* kext */ NULL, 337 kOSKextLogErrorLevel | kOSKextLogGeneralFlag | 338 kOSKextLogLoadFlag | kOSKextLogIPCFlag, 339 "Can't contact kextd; must run as root to load kexts."); 340 result = EX_NOPERM; 341 goto finish; 342 } 343 } 344 345#else 346 347 if (geteuid() != 0) { 348 OSKextLog(/* kext */ NULL, 349 kOSKextLogErrorLevel | kOSKextLogGeneralFlag | 350 kOSKextLogLoadFlag | kOSKextLogIPCFlag, 351 "You must be running as root to load kexts."); 352 result = EX_NOPERM; 353 goto finish; 354 } 355 356#endif /* !TARGET_OS_EMBEDDED */ 357 358finish: 359 360#if !TARGET_OS_EMBEDDED 361 if (kextd_port != MACH_PORT_NULL) { 362 mach_port_deallocate(mach_task_self(), kextd_port); 363 } 364#endif /* !TARGET_OS_EMBEDDED */ 365 366 return result; 367} 368 369/******************************************************************************* 370*******************************************************************************/ 371ExitStatus loadKextsViaKextd(KextloadArgs * toolArgs) 372{ 373 ExitStatus result = EX_OK; 374 OSReturn loadResult = kOSReturnError; 375 char scratchCString[PATH_MAX]; 376 CFIndex count, index; 377 378 count = CFArrayGetCount(toolArgs->kextIDs); 379 for (index = 0; index < count; index++) { 380 CFStringRef kextID = CFArrayGetValueAtIndex(toolArgs->kextIDs, index); 381 382 if (!CFStringGetCString(kextID, scratchCString, sizeof(scratchCString), 383 kCFStringEncodingUTF8)) { 384 385 strlcpy(scratchCString, "unknown", sizeof(scratchCString)); 386 } 387 388 OSKextLog(/* kext */ NULL, 389 kOSKextLogBasicLevel | kOSKextLogGeneralFlag | 390 kOSKextLogLoadFlag | kOSKextLogIPCFlag, 391 "Requesting load of %s.", 392 scratchCString); 393 394 loadResult = KextManagerLoadKextWithIdentifier(kextID, 395 toolArgs->scanURLs); 396 if (loadResult != kOSReturnSuccess) { 397 OSKextLog(/* kext */ NULL, 398 kOSKextLogErrorLevel | kOSKextLogLoadFlag | kOSKextLogIPCFlag, 399 "%s failed to load - %s; " 400 "check the system/kernel logs for errors or try kextutil(8).", 401 scratchCString, safe_mach_error_string(loadResult)); 402 if (result == EX_OK) { 403 result = exitStatusForOSReturn(loadResult); 404 // keep trying other kexts though 405 } 406 } else { 407 OSKextLog(/* kext */ NULL, 408 kOSKextLogBasicLevel | kOSKextLogGeneralFlag | kOSKextLogLoadFlag, 409 "%s loaded successfully (or already loaded).", 410 scratchCString); 411 } 412 } 413 414 count = CFArrayGetCount(toolArgs->kextURLs); 415 for (index = 0; index < count; index++) { 416 CFURLRef kextURL = CFArrayGetValueAtIndex(toolArgs->kextURLs, index); 417 if (!CFURLGetFileSystemRepresentation(kextURL, /* resolveToBase */ true, 418 (UInt8 *)scratchCString, sizeof(scratchCString))) { 419 420 strlcpy(scratchCString, "unknown", sizeof(scratchCString)); 421 } 422 423 OSKextLog(/* kext */ NULL, 424 kOSKextLogBasicLevel | kOSKextLogGeneralFlag | 425 kOSKextLogLoadFlag | kOSKextLogIPCFlag, 426 "Requesting load of %s.", 427 scratchCString); 428 429 loadResult = KextManagerLoadKextWithURL(kextURL, 430 toolArgs->scanURLs); 431 if (loadResult != kOSReturnSuccess) { 432 OSKextLog(/* kext */ NULL, 433 kOSKextLogErrorLevel | kOSKextLogLoadFlag | kOSKextLogIPCFlag, 434 "%s failed to load - %s; " 435 "check the system/kernel logs for errors or try kextutil(8).", 436 scratchCString, safe_mach_error_string(loadResult)); 437 if (result == EX_OK) { 438 result = exitStatusForOSReturn(loadResult); 439 // keep trying other kexts though 440 } 441 } else { 442 OSKextLog(/* kext */ NULL, 443 kOSKextLogBasicLevel | kOSKextLogGeneralFlag | kOSKextLogLoadFlag, 444 "%s loaded successfully (or already loaded).", 445 scratchCString); 446 } 447 } 448 449 return result; 450} 451 452/******************************************************************************* 453*******************************************************************************/ 454ExitStatus loadKextsIntoKernel(KextloadArgs * toolArgs) 455{ 456 ExitStatus result = EX_OK; 457 OSReturn loadResult = kOSReturnError; 458 char scratchCString[PATH_MAX]; 459 CFIndex count, index; 460 461 OSKextLog(/* kext */ NULL, 462 kOSKextLogProgressLevel | kOSKextLogGeneralFlag, 463 "Reading extensions."); 464 toolArgs->allKexts = OSKextCreateKextsFromURLs(kCFAllocatorDefault, 465 toolArgs->scanURLs); 466 if (!toolArgs->allKexts) { 467 OSKextLog(/* kext */ NULL, 468 kOSKextLogErrorLevel | kOSKextLogGeneralFlag, 469 "Can't read kexts from disk."); 470 result = EX_OSERR; 471 goto finish; 472 } 473 474 count = CFArrayGetCount(toolArgs->kextIDs); 475 for (index = 0; index < count; index++) { 476 OSKextRef theKext = NULL; // do not release 477 CFStringRef kextID = CFArrayGetValueAtIndex( 478 toolArgs->kextIDs, 479 index); 480 481 if (!CFStringGetCString(kextID, scratchCString, sizeof(scratchCString), 482 kCFStringEncodingUTF8)) { 483 484 strlcpy(scratchCString, "unknown", sizeof(scratchCString)); 485 } 486 487 OSKextLog(/* kext */ NULL, 488 kOSKextLogBasicLevel | kOSKextLogGeneralFlag | 489 kOSKextLogLoadFlag | kOSKextLogIPCFlag, 490 "Loading %s.", 491 scratchCString); 492 493 theKext = OSKextGetKextWithIdentifier(kextID); 494 if (!theKext) { 495 OSKextLog(/* kext */ NULL, 496 kOSKextLogErrorLevel | kOSKextLogLoadFlag | kOSKextLogIPCFlag, 497 "Error: Kext %s - not found/unable to create.", scratchCString); 498 result = kOSKextReturnNotFound; 499 goto finish; 500 } 501 502 /* The codepath from this function will do any error logging 503 * and cleanup needed. 504 */ 505 loadResult = OSKextLoadWithOptions(theKext, 506 /* statExclusion */ kOSKextExcludeNone, 507 /* addPersonalitiesExclusion */ kOSKextExcludeNone, 508 /* personalityNames */ NULL, 509 /* delayAutounloadFlag */ false); 510 511 if (loadResult != kOSReturnSuccess) { 512 OSKextLog(/* kext */ NULL, 513 kOSKextLogErrorLevel | kOSKextLogLoadFlag | kOSKextLogIPCFlag, 514 "%s failed to load - %s.", 515 scratchCString, safe_mach_error_string(loadResult)); 516 if (result == EX_OK) { 517 result = exitStatusForOSReturn(loadResult); 518 // keep trying other kexts though 519 } 520 } else { 521 OSKextLog(/* kext */ NULL, 522 kOSKextLogBasicLevel | kOSKextLogGeneralFlag | kOSKextLogLoadFlag, 523 "%s loaded successfully (or already loaded).", 524 scratchCString); 525 } 526 } 527 528 count = CFArrayGetCount(toolArgs->kextURLs); 529 for (index = 0; index < count; index++) { 530 CFURLRef kextURL = CFArrayGetValueAtIndex( 531 toolArgs->kextURLs, 532 index); 533 if (!CFURLGetFileSystemRepresentation(kextURL, /* resolveToBase */ true, 534 (UInt8 *)scratchCString, sizeof(scratchCString))) { 535 536 strlcpy(scratchCString, "unknown", sizeof(scratchCString)); 537 } 538 539 OSKextLog(/* kext */ NULL, 540 kOSKextLogBasicLevel | kOSKextLogGeneralFlag | 541 kOSKextLogLoadFlag | kOSKextLogIPCFlag, 542 "Loading %s.", 543 scratchCString); 544 545 OSKextRef theKext = NULL; // do not release 546 547 /* Use OSKextGetKextWithURL() to avoid double open error messages, 548 * because we already tried to open all kexts above. 549 * That means we don't log here if we don't find the kext. 550 */ 551 theKext = OSKextGetKextWithURL(kextURL); 552 if (!theKext) { 553 loadResult = kOSKextReturnNotFound; 554 } else { 555 /* The codepath from this function will do any error logging 556 * and cleanup needed. 557 */ 558 loadResult = OSKextLoadWithOptions(theKext, 559 /* statExclusion */ kOSKextExcludeNone, 560 /* addPersonalitiesExclusion */ kOSKextExcludeNone, 561 /* personalityNames */ NULL, 562 /* delayAutounloadFlag */ false); 563 } 564 if (loadResult != kOSReturnSuccess) { 565 OSKextLog(/* kext */ NULL, 566 kOSKextLogErrorLevel | kOSKextLogLoadFlag | kOSKextLogIPCFlag, 567 "%s failed to load - %s.", 568 scratchCString, safe_mach_error_string(loadResult)); 569 if (result == EX_OK) { 570 result = exitStatusForOSReturn(loadResult); 571 // keep trying other kexts though 572 } 573 } else { 574 OSKextLog(/* kext */ NULL, 575 kOSKextLogBasicLevel | kOSKextLogGeneralFlag | kOSKextLogLoadFlag, 576 "%s loaded successfully (or already loaded).", 577 scratchCString); 578 } 579 } 580 581finish: 582 return result; 583} 584 585/******************************************************************************* 586*******************************************************************************/ 587ExitStatus exitStatusForOSReturn(OSReturn osReturn) 588{ 589 ExitStatus result = EX_OSERR; 590 591 switch (osReturn) { 592 case kOSKextReturnNotPrivileged: 593 result = EX_NOPERM; 594 break; 595 default: 596 result = EX_OSERR; 597 break; 598 } 599 return result; 600} 601 602/******************************************************************************* 603* usage() 604*******************************************************************************/ 605void usage(UsageLevel usageLevel) 606{ 607 fprintf(stderr, "usage: %s [options] [--] [kext] ...\n" 608 "\n", progname); 609 610 if (usageLevel == kUsageLevelBrief) { 611 fprintf(stderr, "use %s -%s for an explanation of each option\n", 612 progname, kOptNameHelp); 613 return; 614 } 615 616 fprintf(stderr, "kext: a kext bundle to load or examine\n"); 617 fprintf(stderr, "\n"); 618 619 fprintf(stderr, "-%s <bundle_id> (-%c):\n" 620 " load/use the kext whose CFBundleIdentifier is <bundle_id>\n", 621 kOptNameBundleIdentifier, kOptBundleIdentifier); 622 fprintf(stderr, "-%s <kext> (-%c):\n" 623 " consider <kext> as a candidate dependency\n", 624 kOptNameDependency, kOptDependency); 625 fprintf(stderr, "-%s <directory> (-%c):\n" 626 " look in <directory> for kexts\n", 627 kOptNameRepository, kOptRepository); 628 fprintf(stderr, "\n"); 629 630 fprintf(stderr, "-%s (-%c):\n" 631 " quiet mode: print no informational or error messages\n", 632 kOptNameQuiet, kOptQuiet); 633 fprintf(stderr, "-%s [ 0-6 | 0x<flags> ] (-%c):\n" 634 " verbose mode; print info about analysis & loading\n", 635 kOptNameVerbose, kOptVerbose); 636 fprintf(stderr, "\n"); 637 638 fprintf(stderr, "-%s (-%c): print this message and exit\n", 639 kOptNameHelp, kOptHelp); 640 fprintf(stderr, "\n"); 641 642 fprintf(stderr, "--: end of options\n"); 643 return; 644} 645