1/*
2 *  kextutil_main.c
3 *  kext_tools
4 *
5 *  Created by Nik Gervae on 4/24/08.
6 *  Copyright 2008 Apple Inc. All rights reserved.
7 *
8 */
9#include "kextutil_main.h"
10#include "kext_tools_util.h"
11#include "security.h"
12#include "bootcaches.h"
13
14#include <libc.h>
15#include <sysexits.h>
16
17#include <IOKit/kext/OSKext.h>
18#include <IOKit/kext/OSKextPrivate.h>
19
20#include <IOKit/kext/KextManager.h>
21#include <IOKit/kext/KextManagerPriv.h>
22#include <IOKit/kext/kextmanager_types.h>
23
24#include <servers/bootstrap.h>    // bootstrap mach ports
25#include <bootfiles.h>
26
27#pragma mark Constants
28/*******************************************************************************
29* Constants
30*******************************************************************************/
31#define BAD_ADDRESS_SPEC "Address format is <bundle-id@address>, " \
32                         "with nonzero hexadecimal address."
33
34#pragma mark Global/Static Variables
35/*******************************************************************************
36* Global/Static Variables
37*******************************************************************************/
38const char * progname = "(unknown)";
39
40#define LOCK_MAXTRIES 90
41#define LOCK_DELAY     1
42static mach_port_t sKextdPort = MACH_PORT_NULL;
43static mach_port_t sLockPort = MACH_PORT_NULL;     // kext loading lock
44static int sLockStatus = 0;
45static bool sLockTaken = false;
46
47#pragma mark Main Routine
48/*******************************************************************************
49* Global variables.
50*******************************************************************************/
51ExitStatus
52main(int argc, char * const * argv)
53{
54    ExitStatus result         = EX_SOFTWARE;
55    ExitStatus processResult  = EX_SOFTWARE;
56    Boolean    fatal          = false;
57    CFArrayRef allKexts       = NULL;  // must release
58    CFArrayRef kextsToProcess = NULL;  // must release
59    KextutilArgs   toolArgs;
60
61   /*****
62    * Find out what the program was invoked as.
63    */
64    progname = rindex(argv[0], '/');
65    if (progname) {
66        progname++;   // go past the '/'
67    } else {
68        progname = (char *)argv[0];
69    }
70
71   /* Set the OSKext log callback right away and hook up to get any
72    * warnings & errors out of the kernel.
73    */
74    OSKextSetLogOutputFunction(&tool_log);
75    OSKextSetLogFilter(kOSKextLogWarningLevel | kOSKextLogVerboseFlagsMask,
76        /* kernel? */ true);
77
78   /*****
79    * Process args & check for permission to load.
80    */
81    result = readArgs(argc, argv, &toolArgs);
82    if (result != EX_OK) {
83        if (result == kKextutilExitHelp) {
84            result = EX_OK;
85        }
86        goto finish;
87    }
88
89    result = checkArgs(&toolArgs);
90    if (result != EX_OK) {
91        goto finish;
92    }
93
94   /* From here on out the default exit status is ok.
95    */
96    result = EX_OK;
97
98   /*****
99    * Turn on recording of all diagnostics.
100    */
101    OSKextSetRecordsDiagnostics(kOSKextDiagnosticsFlagAll);
102
103   /*****
104    * Create the set of kexts we'll be working from.
105    */
106    allKexts = OSKextCreateKextsFromURLs(kCFAllocatorDefault, toolArgs.scanURLs);
107    if (!allKexts || !CFArrayGetCount(allKexts)) {
108        OSKextLog(/* kext */ NULL,
109            kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
110            "No kernel extensions found.");
111        result = EX_SOFTWARE;
112        goto finish;
113    }
114
115    if (result != EX_OK) {
116        goto finish;
117    }
118
119   /*****
120    * Figure out which kexts we're actually loading/generating symbols for.
121    * This must be done after the kext objects are created.
122    */
123    result = createKextsToProcess(&toolArgs, &kextsToProcess, &fatal);
124    if (fatal) {
125        goto finish;
126    }
127
128    if (!serializeLoad(&toolArgs, toolArgs.doLoad)) {
129        result = EX_OSERR;
130        goto finish;
131    }
132
133    processResult = processKexts(kextsToProcess, &toolArgs);
134    if (result == EX_OK) {
135        result = processResult;
136    }
137
138finish:
139   /*****
140    * Clean everything up. The mach stuff should be done even though we're
141    * exiting so we don't mess up other processes.
142    */
143    if (sLockTaken) {
144        kextmanager_unlock_kextload(sKextdPort, sLockPort);
145    }
146    if (sKextdPort != MACH_PORT_NULL) {
147        mach_port_deallocate(mach_task_self(), sKextdPort);
148    }
149    if (sLockPort != MACH_PORT_NULL) {
150        mach_port_mod_refs(mach_task_self(), sLockPort, MACH_PORT_RIGHT_RECEIVE, -1);
151    }
152
153   /* We're actually not going to free anything else because we're exiting!
154    */
155    exit(result);
156
157    SAFE_RELEASE(allKexts);
158    SAFE_RELEASE(toolArgs.loadAddresses);
159    SAFE_RELEASE(toolArgs.kextIDs);
160    SAFE_RELEASE(toolArgs.personalityNames);
161    SAFE_RELEASE(toolArgs.dependencyURLs);
162    SAFE_RELEASE(toolArgs.repositoryURLs);
163    SAFE_RELEASE(toolArgs.kextURLs);
164    SAFE_RELEASE(toolArgs.scanURLs);
165    SAFE_RELEASE(toolArgs.kernelURL);
166    SAFE_RELEASE(toolArgs.kernelFile);
167    SAFE_RELEASE(toolArgs.symbolDirURL);
168
169    return result;
170}
171
172#pragma mark Major Subroutines
173/*******************************************************************************
174* Major Subroutines
175*******************************************************************************/
176ExitStatus
177readArgs(
178    int            argc,
179    char * const * argv,
180    KextutilArgs * toolArgs)
181{
182    ExitStatus   result          = EX_USAGE;
183    ExitStatus   scratchResult   = EX_USAGE;
184    int          optchar;
185    int          longindex;
186    CFStringRef  scratchString   = NULL;  // must release
187    CFNumberRef  scratchNumber   = NULL;  // must release
188    CFNumberRef  existingAddress = NULL;  // do not release
189    CFURLRef     scratchURL      = NULL;  // must release
190    uint32_t     i;
191
192    bzero(toolArgs, sizeof(*toolArgs));
193
194   /* Set up default arg values.
195    */
196    toolArgs->useRepositoryCaches = true;
197    toolArgs->useSystemExtensions = true;
198    toolArgs->overwriteSymbols    = true;
199    toolArgs->interactiveLevel    = kOSKextExcludeNone;
200    toolArgs->doLoad              = true;
201    toolArgs->doStartMatching     = true;
202    // toolArgs->archInfo must remain NULL before checkArgs
203
204   /*****
205    * Allocate collection objects.
206    */
207    if (!createCFMutableDictionary(&toolArgs->loadAddresses)                       ||
208        !createCFMutableArray(&toolArgs->kextIDs, &kCFTypeArrayCallBacks)          ||
209        !createCFMutableArray(&toolArgs->personalityNames, &kCFTypeArrayCallBacks) ||
210        !createCFMutableArray(&toolArgs->dependencyURLs, &kCFTypeArrayCallBacks)   ||
211        !createCFMutableArray(&toolArgs->repositoryURLs, &kCFTypeArrayCallBacks)   ||
212        !createCFMutableArray(&toolArgs->kextURLs, &kCFTypeArrayCallBacks)         ||
213        !createCFMutableArray(&toolArgs->scanURLs, &kCFTypeArrayCallBacks)) {
214
215        result = EX_OSERR;
216        OSKextLogMemError();
217        exit(result);
218    }
219
220    while ((optchar = getopt_long_only(argc, (char * const *)argv,
221        kOptChars, sOptInfo, &longindex)) != -1) {
222
223        char * address_string = NULL;  // don't free
224        uint64_t address;
225
226        SAFE_RELEASE_NULL(scratchString);
227        SAFE_RELEASE_NULL(scratchNumber);
228        SAFE_RELEASE_NULL(scratchURL);
229
230        switch (optchar) {
231            case kOptHelp:
232                usage(kUsageLevelFull);
233                result = kKextutilExitHelp;
234                goto finish;
235                break;
236            case kOptBundleIdentifier:
237                scratchString = CFStringCreateWithCString(kCFAllocatorDefault,
238                    optarg, kCFStringEncodingUTF8);
239                if (!scratchString) {
240                    OSKextLogMemError();
241                    result = EX_OSERR;
242                    goto finish;
243                }
244                CFArrayAppendValue(toolArgs->kextIDs, scratchString);
245                break;
246
247            case kOptPersonality:
248                scratchString = CFStringCreateWithCString(kCFAllocatorDefault,
249                    optarg, kCFStringEncodingUTF8);
250                if (!scratchString) {
251                    OSKextLogMemError();
252                    result = EX_OSERR;
253                    goto finish;
254                }
255                CFArrayAppendValue(toolArgs->personalityNames, scratchString);
256                break;
257
258            case kOptKernel:
259                if (toolArgs->kernelURL) {
260                    OSKextLog(/* kext */ NULL,
261                        kOSKextLogWarningLevel | kOSKextLogGeneralFlag,
262                        "Warning: multiple use of -%s (-%c); using last.",
263                        kOptNameKernel, kOptKernel);
264                    SAFE_RELEASE_NULL(toolArgs->kernelURL);
265                }
266                scratchURL = CFURLCreateFromFileSystemRepresentation(
267                    kCFAllocatorDefault,
268                    (const UInt8 *)optarg, strlen(optarg), true);
269                if (!scratchURL) {
270                    OSKextLogStringError(/* kext */ NULL);
271                    result = EX_OSERR;
272                    goto finish;
273                }
274                toolArgs->kernelURL = CFRetain(scratchURL);
275                break;
276
277            case kOptDependency:
278                scratchURL = CFURLCreateFromFileSystemRepresentation(
279                    kCFAllocatorDefault,
280                    (const UInt8 *)optarg, strlen(optarg), true);
281                if (!scratchURL) {
282                    OSKextLogStringError(/* kext */ NULL);
283                    result = EX_OSERR;
284                    goto finish;
285                }
286                CFArrayAppendValue(toolArgs->dependencyURLs, scratchURL);
287                break;
288
289            case kOptRepository:
290                scratchResult = checkPath(optarg, /* suffix */ NULL,
291                    /* directoryRequired */ TRUE, /* writableRequired */ FALSE);
292                if (scratchResult != EX_OK) {
293                    result = scratchResult;
294                    goto finish;
295                }
296                scratchURL = CFURLCreateFromFileSystemRepresentation(
297                    kCFAllocatorDefault,
298                    (const UInt8 *)optarg, strlen(optarg), true);
299                if (!scratchURL) {
300                    OSKextLogStringError(/* kext */ NULL);
301                    result = EX_OSERR;
302                    goto finish;
303                }
304                CFArrayAppendValue(toolArgs->repositoryURLs, scratchURL);
305                break;
306
307            case kOptNoCaches:
308                toolArgs->useRepositoryCaches = false;
309                break;
310
311            case kOptNoLoadedCheck:
312                toolArgs->checkLoadedForDependencies = false;
313                break;
314
315            case kOptNoSystemExtensions:
316                toolArgs->useSystemExtensions = false;
317                break;
318
319            case kOptInteractive:
320                if (toolArgs->interactiveLevel) {
321                    OSKextLog(/* kext */ NULL,
322                        kOSKextLogWarningLevel | kOSKextLogGeneralFlag,
323                        "Warning: multiple use of -%s (-%c) or -%s (-%c); using last.",
324                        kOptNameInteractive, kOptInteractive,
325                        kOptNameInteractiveAll, kOptInteractiveAll);
326                }
327                toolArgs->overwriteSymbols = false;
328                toolArgs->interactiveLevel = kOSKextExcludeKext;
329                break;
330
331            case kOptInteractiveAll:
332                if (toolArgs->interactiveLevel) {
333                    OSKextLog(/* kext */ NULL,
334                        kOSKextLogWarningLevel | kOSKextLogGeneralFlag,
335                        "Warning: multiple use of -%s (-%c) or -%s (-%c); using last.",
336                        kOptNameInteractive, kOptInteractive,
337                        kOptNameInteractiveAll, kOptInteractiveAll);
338                }
339                toolArgs->overwriteSymbols = false;
340                toolArgs->interactiveLevel = kOSKextExcludeAll;
341                break;
342
343            case kOptLoadOnly:
344                toolArgs->flag_l = 1;
345                break;
346
347            case kOptMatchOnly:
348                toolArgs->flag_m = 1;
349                break;
350
351            case kOptNoLoad:
352                toolArgs->flag_n = 1;
353                break;
354
355            case kOptSymbolsDirectory:
356                if (toolArgs->symbolDirURL) {
357                    OSKextLog(/* kext */ NULL,
358                        kOSKextLogWarningLevel | kOSKextLogGeneralFlag,
359                        "Warning: multiple use of -%s (-%c); using last",
360                        kOptNameSymbolsDirectory, kOptSymbolsDirectory);
361                    SAFE_RELEASE_NULL(toolArgs->symbolDirURL);
362                }
363                scratchResult = checkPath(optarg, /* suffix */ NULL,
364                    /* directoryRequired? */ TRUE, /* writable? */ TRUE);
365                if (scratchResult != EX_OK) {
366                    result = scratchResult;
367                    goto finish;
368                }
369                scratchURL = CFURLCreateFromFileSystemRepresentation(
370                    kCFAllocatorDefault,
371                    (const UInt8 *)optarg, strlen(optarg), true);
372                if (!scratchURL) {
373                    OSKextLogStringError(/* kext */ NULL);
374                    result = EX_OSERR;
375                    goto finish;
376                }
377                toolArgs->symbolDirURL = CFRetain(scratchURL);
378                break;
379
380            case kOptAddress:
381                toolArgs->flag_n = 1;  // -a implies -n
382
383                address_string = index(optarg, '@');
384                if (!address_string) {
385                    OSKextLog(/* kext */ NULL,
386                        kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
387                        BAD_ADDRESS_SPEC);
388                    goto finish;
389                }
390                address_string[0] = '\0';
391                address_string++;
392
393               /* Read a 64-bit int here; we'll check at load time
394                * whether the address is too big.
395                */
396                address = strtoull(address_string, NULL, 16);
397                if (!address) {
398                    OSKextLog(/* kext */ NULL,
399                        kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
400                        BAD_ADDRESS_SPEC);
401                    goto finish;
402                }
403                scratchNumber = CFNumberCreate(kCFAllocatorDefault,
404                    kCFNumberSInt64Type, &address);
405                if (!scratchNumber) {
406                    OSKextLogMemError();
407                    result = EX_OSERR;
408                    goto finish;
409                }
410                scratchString = CFStringCreateWithCString(kCFAllocatorDefault,
411                   optarg, kCFStringEncodingUTF8);
412                if (!scratchString) {
413                    OSKextLogMemError();
414                    result = EX_OSERR;
415                    goto finish;
416                }
417                existingAddress = CFDictionaryGetValue(toolArgs->loadAddresses,
418                    scratchString);
419                if (existingAddress && !CFEqual(scratchNumber, existingAddress)) {
420                    OSKextLog(/* kext */ NULL, kOSKextLogWarningLevel,
421                        "Warning: multiple addresses specified for %s; using last.",
422                        optarg);
423                }
424                CFDictionarySetValue(toolArgs->loadAddresses, scratchString,
425                    scratchNumber);
426                break;
427
428            case kOptUseKernelAddresses:
429                toolArgs->flag_n = 1;   // -A implies -n
430                toolArgs->getAddressesFromKernel = true;
431                break;
432
433            case kOptQuiet:
434                beQuiet();
435                toolArgs->logFilterChanged = true;
436                break;
437
438            case kOptVerbose:
439                scratchResult = setLogFilterForOpt(argc, argv, /* forceOnFlags */ 0);
440                if (scratchResult != EX_OK) {
441                    result = scratchResult;
442                    goto finish;
443                }
444                toolArgs->logFilterChanged = true;
445                break;
446
447            case kOptTests:
448                toolArgs->printDiagnostics = true;
449                break;
450
451            case kOptSafeBoot:
452                toolArgs->safeBootMode = true;
453                toolArgs->useRepositoryCaches = false;  // -x implies -c
454                break;
455
456            case kOptNoAuthentication:
457                toolArgs->skipAuthentication = true;
458                break;
459
460            case kOptNoResolveDependencies:
461                toolArgs->skipDependencies = true;
462                break;
463
464            case 0:
465                switch (longopt) {
466                    case kLongOptArch:
467                        if (toolArgs->archInfo) {
468                            OSKextLog(/* kext */ NULL,
469                                kOSKextLogWarningLevel | kOSKextLogGeneralFlag,
470                                "Warning: multiple use of -%s; using last.",
471                                kOptNameArch);
472                        }
473                        toolArgs->archInfo = NXGetArchInfoFromName(optarg);
474                        if (!toolArgs->archInfo) {
475                            OSKextLog(/* kext */ NULL,
476                                kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
477                                "Unknown architecture %s.", optarg);
478                            goto finish;
479                        }
480                        break;
481
482                   default:
483                       /* getopt_long_only() prints an error message for us. */
484                        goto finish;
485                        break;
486                }
487                break;
488
489            default:
490               /* getopt_long_only() prints an error message for us. */
491                goto finish;
492                break;
493
494        } /* switch (optchar) */
495    } /* while (optchar = getopt_long_only(...) */
496
497   /*****
498    * Record the kext names from the command line.
499    */
500    for (i = optind; i < argc; i++) {
501        SAFE_RELEASE_NULL(scratchURL);
502
503        scratchResult = checkPath(argv[i], kOSKextBundleExtension,
504            /* directoryRequired */ TRUE, /* writableRequired */ FALSE);
505        if (scratchResult != EX_OK) {
506            result = scratchResult;
507            goto finish;
508        }
509
510        scratchURL = CFURLCreateFromFileSystemRepresentation(
511            kCFAllocatorDefault,
512            (const UInt8 *)argv[i], strlen(argv[i]), true);
513        if (!scratchURL) {
514            result = EX_OSERR;
515            OSKextLogMemError();
516            goto finish;
517        }
518        CFArrayAppendValue(toolArgs->kextURLs, scratchURL);
519    }
520
521    result = EX_OK;
522
523finish:
524    SAFE_RELEASE(scratchString);
525    SAFE_RELEASE(scratchNumber);
526    SAFE_RELEASE(scratchURL);
527
528    if (result == EX_USAGE) {
529        usage(kUsageLevelBrief);
530    }
531    return result;
532}
533
534/*******************************************************************************
535*******************************************************************************/
536ExitStatus
537checkArgs(KextutilArgs * toolArgs)
538{
539    ExitStatus         result         = EX_USAGE;
540    char               kernelPathCString[PATH_MAX];
541    const NXArchInfo * kernelArchInfo = OSKextGetRunningKernelArchitecture();
542
543   /* We don't need this for everything, but it's unlikely to fail.
544    */
545    if (!kernelArchInfo) {
546        result = EX_OSERR;
547        goto finish;
548    }
549
550   /*****
551    * Check for bad combinations of arguments and options.
552    */
553    if (toolArgs->flag_l + toolArgs->flag_m + toolArgs->flag_n > 1) {
554        OSKextLog(/* kext */ NULL,
555            kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
556            "Only one of -%s (-%c), -%s (-%c), or -%s (-%c) is allowed;\n"
557            "-%s (-%c) and -%s (-%c) imply -%s (-%c).",
558            kOptNameLoadOnly, kOptLoadOnly,
559            kOptNameMatchOnly, kOptMatchOnly,
560            kOptNameNoLoad, kOptNoLoad,
561            kOptNameAddress, kOptAddress,
562            kOptNameUseKernelAddresses, kOptUseKernelAddresses,
563            kOptNameNoLoad, kOptNoLoad);
564        goto finish;
565    } else if (toolArgs->flag_l) {
566        toolArgs->doLoad = true;
567        toolArgs->doStartMatching = false;
568    } else if (toolArgs->flag_m) {
569        toolArgs->doLoad = false;
570        toolArgs->doStartMatching = true;
571    } else if (toolArgs->flag_n) {
572        toolArgs->doLoad = false;
573        toolArgs->doStartMatching = false;
574    }
575
576    if ((toolArgs->interactiveLevel != kOSKextExcludeNone) &&
577        !toolArgs->doLoad && !toolArgs->doStartMatching) {
578
579        OSKextLog(/* kext */ NULL,
580            kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
581            "Use interactive modes only when loading or matching.");
582        goto finish;
583    }
584
585   /* If running in quiet mode and the invocation might require
586    * interaction, just exit with an error. Interaction required when:
587    *
588    * - Explicitly flagged with -i/-I.
589    * - Saving symbols w/o loading, no addresses specified,
590    *   not getting from kernel.
591    */
592    if (OSKextGetLogFilter(/* kernel? */ false) == kOSKextLogSilentFilter) {
593
594        Boolean interactive = (toolArgs->interactiveLevel != kOSKextExcludeNone);
595        Boolean needAddresses = (toolArgs->symbolDirURL &&
596            !toolArgs->doLoad &&
597            CFDictionaryGetCount(toolArgs->loadAddresses) == 0 &&
598            !toolArgs->getAddressesFromKernel);
599
600        if (interactive || needAddresses) {
601            result = kKextutilExitLoadFailed;
602            goto finish;
603        }
604    }
605
606   /* Disallow -address(-a) with any load or getting addresses from kernel.
607    */
608    if (CFDictionaryGetCount(toolArgs->loadAddresses) > 0 &&
609        (toolArgs->doLoad || toolArgs->getAddressesFromKernel)) {
610
611        OSKextLog(/* kext */ NULL,
612            kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
613            "Don't use -%s (-%c) when loading, or with -%s (-%c).",
614            kOptNameAddress, kOptAddress,
615            kOptNameUseKernelAddresses, kOptUseKernelAddresses);
616        goto finish;
617    }
618
619   /* If sending anything into the kernel, don't use a kernel file
620    * and refuse to skip authentication.
621    */
622    if (toolArgs->doLoad || toolArgs->doStartMatching) {
623        if (toolArgs->kernelURL) {
624            OSKextLog(/* kext */ NULL,
625                kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
626                "-%s (-%c) is allowed only when not loading "
627                "or sending personalities.",
628                kOptNameKernel, kOptKernel);
629            goto finish;
630        }
631
632        if (toolArgs->skipAuthentication) {
633            OSKextLog(/* kext */ NULL,
634                kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
635                "-%s (-%c) is allowed only when not loading "
636                "or sending personalities.",
637                kOptNameNoAuthentication, kOptNoAuthentication);
638            goto finish;
639        }
640    }
641
642   /* If we aren't sending anything to the kernel and not doing full
643    * tests, then don't bother authenticating. This lets developers
644    * generate symbols more conveniently & allows basic checks with -n alone.
645    */
646    if (!toolArgs->doLoad &&
647        !toolArgs->doStartMatching &&
648        !toolArgs->printDiagnostics) {
649
650        toolArgs-> skipAuthentication = true;
651    }
652
653   /****
654    * If we're getting addresses from the kernel we have to call
655    * down there, so we might as well check what's loaded before
656    * resolving dependencies too (-A overrides -D). If we're not
657    * performing a load, then don't check (-n/-m implies -D).
658    */
659    if (toolArgs->getAddressesFromKernel) {
660        toolArgs->checkLoadedForDependencies = true;
661    }
662
663   /*****
664    * -no-resolve-dependencies/-Z is only allowed
665    * if you don't need to resolve dependencies (duh).
666    * You need to resolve if loading or saving symbols.
667    */
668    if (toolArgs->skipDependencies) {
669        if (toolArgs->doLoad ||
670            toolArgs->symbolDirURL) {
671
672            OSKextLog(/* kext */ NULL,
673                kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
674                "Use -%s (-%c) only with -%s (-%c) or %s (-%c), "
675                "and not with -%s (-%c).",
676                kOptNameNoResolveDependencies, kOptNoResolveDependencies,
677                kOptNameNoLoad, kOptNoLoad,
678                kOptNameMatchOnly, kOptMatchOnly,
679                kOptNameSymbolsDirectory, kOptSymbolsDirectory);
680            goto finish;
681        }
682    }
683
684    if (!CFArrayGetCount(toolArgs->kextURLs) &&
685        !CFArrayGetCount(toolArgs->kextIDs) &&
686        !CFDictionaryGetCount(toolArgs->loadAddresses)) {
687
688        OSKextLog(/* kext */ NULL,
689            kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
690            "No kernel extensions specified; name kernel extension bundles\n"
691            "    following options, or use -%s (-%c) and -%s (-%c).",
692            kOptNameBundleIdentifier, kOptBundleIdentifier,
693            kOptNameAddress, kOptAddress);
694        goto finish;
695    }
696
697   /*****
698    * Check whether the user has permission to load into the kernel.
699    */
700    if ((toolArgs->doLoad || toolArgs->doStartMatching) && geteuid() != 0) {
701        OSKextLog(/* kext */ NULL,
702            kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
703            "You must be running as root to load kexts "
704            "or send personalities into the kernel.");
705        result = EX_NOPERM;
706        goto finish;
707    }
708
709   /*****
710    * Set up which arch to work with and sanity-check it.
711    */
712    if (toolArgs->archInfo) {
713
714       /* If loading into or getting addresses form the kernel,
715        * any specified architecture must match that of the running kernel.
716        */
717        if (toolArgs->doLoad || toolArgs->getAddressesFromKernel) {
718
719            if (kernelArchInfo != OSKextGetArchitecture()) {
720
721                OSKextLog(/* kext */ NULL,
722                    kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
723                    "Specified architecture %s does not match "
724                    "running kernel architecture %s.",
725                    toolArgs->archInfo->name, kernelArchInfo->name);
726                goto finish;
727            }
728        }
729
730       /* All good; set the default OSKext arch & safe boot mode.
731        */
732        OSKextSetArchitecture(toolArgs->archInfo);
733        OSKextSetSimulatedSafeBoot(toolArgs->safeBootMode);
734    }
735
736   /* Give a notice to the user in case they're doing cross-arch work.
737    */
738    if (toolArgs->symbolDirURL && !toolArgs->archInfo &&
739        !toolArgs->getAddressesFromKernel) {
740
741        OSKextLog(/* kext */ NULL,
742            kOSKextLogWarningLevel | kOSKextLogLinkFlag,
743            "Notice: Using running kernel architecture %s to generate symbols.",
744            kernelArchInfo->name);
745       /* not an error! */
746    }
747
748   /* Give a notice to the user if safe boot mode is actually on.
749    */
750    if (OSKextGetActualSafeBoot() && !toolArgs->safeBootMode) {
751
752        OSKextLog(/* kext */ NULL,
753            kOSKextLogWarningLevel | kOSKextLogLoadFlag,
754            "Notice: system is in safe boot mode; kernel may refuse loads.");
755       /* not an error! */
756    }
757
758   /*****
759    * Assemble the list of URLs to scan, in this order (the OSKext lib inverts it
760    * for last-opened-wins semantics):
761    * 1. System repository directories (-no-system-extensions/-e skips).
762    * 2. Named kexts (always given after -repository & -dependency on command line).
763    * 3. Named repository directories (-repository/-r).
764    * 4. Named dependencies get priority (-dependency/-d).
765    */
766    if (toolArgs->useSystemExtensions) {
767        CFArrayRef sysExtFolders = OSKextGetSystemExtensionsFolderURLs();
768        // xxx - check it
769        CFArrayAppendArray(toolArgs->scanURLs,
770            sysExtFolders, RANGE_ALL(sysExtFolders));
771    }
772    CFArrayAppendArray(toolArgs->scanURLs, toolArgs->kextURLs,
773        RANGE_ALL(toolArgs->kextURLs));
774    CFArrayAppendArray(toolArgs->scanURLs, toolArgs->repositoryURLs,
775        RANGE_ALL(toolArgs->repositoryURLs));
776    CFArrayAppendArray(toolArgs->scanURLs, toolArgs->dependencyURLs,
777        RANGE_ALL(toolArgs->dependencyURLs));
778
779    if ( (CFArrayGetCount(toolArgs->kextIDs) ||
780          CFDictionaryGetCount(toolArgs->loadAddresses)) &&
781        !CFArrayGetCount(toolArgs->scanURLs)) {
782
783        OSKextLog(/* kext */ NULL,
784            kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
785            "No kexts to search for bundle IDs; "
786            "-%s (-%c) requires kexts or repository directories "
787            "be named by path.",
788            kOptNameBundleIdentifier, kOptBundleIdentifier);
789        goto finish;
790    }
791
792    /* If no explicit kernel image was provided by the user, default it
793     * to KernelPath from /usr/standalone/bootcaches.plist
794     * <= 10.9 /mach_kernel
795     * > 10.9 /System/Library/Kernels/kernel
796     */
797    if (!toolArgs->kernelURL) {
798        CFURLRef        scratchURL = NULL;
799
800        // use KernelPath from /usr/standalone/bootcaches.plist
801        if (getKernelPathForURL(NULL,
802                                kernelPathCString,
803                                sizeof(kernelPathCString)) == FALSE) {
804            // no bootcaches.plist?  Forced to hardwire...
805            strlcpy(kernelPathCString, "/System/Library/Kernels/kernel",
806                    sizeof(kernelPathCString));
807        }
808
809        if (useDevelopmentKernel(kernelPathCString)) {
810            if (strlen(kernelPathCString) + strlen(kDefaultKernelSuffix) + 1 < sizeof(kernelPathCString)) {
811                strlcat(kernelPathCString,
812                        kDefaultKernelSuffix,
813                        sizeof(kernelPathCString));
814            }
815        }
816
817        scratchURL = CFURLCreateFromFileSystemRepresentation(
818                                                             kCFAllocatorDefault,
819                                                             (const UInt8 *)kernelPathCString,
820                                                             strlen(kernelPathCString),
821                                                             false );
822        if (!scratchURL) {
823            OSKextLogStringError(/* kext */ NULL);
824            result = EX_OSERR;
825            goto finish;
826        }
827        toolArgs->kernelURL = scratchURL;
828        OSKextLog(/* kext */ NULL,
829                  kOSKextLogBasicLevel | kOSKextLogLoadFlag,
830                  "Defaulting to kernel file '%s'",
831                  kernelPathCString);
832    }
833
834    if (toolArgs->kernelURL) {
835        /* create and fill our CFData object for toolArgs->kernelFile
836         */
837        if (!CFURLGetFileSystemRepresentation(toolArgs->kernelURL,
838                                              true,
839                                              (uint8_t *)kernelPathCString,
840                                              sizeof(kernelPathCString))) {
841            OSKextLogStringError(/* kext */ NULL);
842            result = EX_OSFILE;
843            goto finish;
844       }
845
846        if (!createCFDataFromFile(&toolArgs->kernelFile,
847                                  kernelPathCString)) {
848            OSKextLog(/* kext */ NULL,
849                      kOSKextLogErrorLevel | kOSKextLogFileAccessFlag,
850                      "Can't read kernel file '%s'",
851                      kernelPathCString);
852            result = EX_OSFILE;
853            goto finish;
854        }
855    }
856
857    if (!toolArgs->doLoad || (toolArgs->interactiveLevel != kOSKextExcludeNone)) {
858        adjustLogFilterForInteractive(toolArgs);
859    }
860
861    result = EX_OK;
862
863finish:
864    if (result == EX_USAGE) {
865        usage(kUsageLevelBrief);
866    }
867    return result;
868}
869
870/*******************************************************************************
871*******************************************************************************/
872void adjustLogFilterForInteractive(KextutilArgs * toolArgs)
873{
874    if (!toolArgs->logFilterChanged) {
875        OSKextSetLogFilter(kDefaultServiceLogFilter, /* kernel? */ false);
876        OSKextSetLogFilter(kDefaultServiceLogFilter, /* kernel? */ true);
877    }
878}
879
880/*******************************************************************************
881*******************************************************************************/
882ExitStatus
883createKextsToProcess(
884    KextutilArgs * toolArgs,
885    CFArrayRef   * outArray,
886    Boolean      * fatal)
887{
888    ExitStatus         result = EX_OK;
889    CFMutableArrayRef  kextsToProcess  = NULL;  // returned by ref.
890
891    CFURLRef           kextURL = NULL;          // do not release
892    char               kextPathCString[PATH_MAX];
893
894    CFStringRef      * addressKeys     = NULL;  // must free
895    char             * kextIDCString   = NULL;  // must free
896    OSKextRef          theKext         = NULL;  // do not release
897    CFIndex            kextCount, idCount, kextIndex, idIndex;
898
899    if (!createCFMutableArray(&kextsToProcess, &kCFTypeArrayCallBacks)) {
900        result = EX_OSERR;
901        goto finish;
902    }
903
904   /*****
905    * If we got kext bundle names, then create kexts for them. Kexts named
906    * by path always have priority so we're adding them first.
907    */
908    kextCount = CFArrayGetCount(toolArgs->kextURLs);
909    for (kextIndex = 0; kextIndex < kextCount; kextIndex++) {
910
911        kextURL = (CFURLRef)CFArrayGetValueAtIndex(
912            toolArgs->kextURLs, kextIndex);
913
914        if (!CFURLGetFileSystemRepresentation(kextURL,
915            /* resolveToBase */ false,
916            (u_char *)kextPathCString, sizeof(kextPathCString))) {
917
918            OSKextLogStringError(/* kext */ NULL);
919            result = EX_OSERR;
920            *fatal = true;
921            goto finish;
922        }
923
924        OSKextLog(/* kext */ NULL,
925            kOSKextLogStepLevel | kOSKextLogKextBookkeepingFlag,
926            "Looking up extension with URL %s.",
927            kextPathCString);
928
929       /* Use OSKextGetKextWithURL() to avoid double open error messages,
930        * because we already tried to open all kexts in main(). That means
931        * we don't log here if we don't find the kext.
932        */
933        theKext = OSKextGetKextWithURL(kextURL);
934        if (!theKext) {
935            result = kKextutilExitNotFound;
936            // keep going
937            continue;
938        }
939        addToArrayIfAbsent(kextsToProcess, theKext);
940
941       /* Since we're running a developer tool on this kext, let's go ahead
942        * and enable per-kext logging for it regardless of the in-bundle setting.
943        * Would be nice to do this for all dependencies but the user can set
944        * the verbose filter's 0x8 bit for that. It will log for all kexts
945        * but that's not so bad.
946        */
947        OSKextSetLoggingEnabled(theKext, true);
948    }
949
950   /*****
951    * If we got load addresses on the command line, add their bundle IDs
952    * to the list of kext IDs to load.
953    */
954    idCount = CFDictionaryGetCount(toolArgs->loadAddresses);
955    if (idCount) {
956        addressKeys = (CFStringRef *)malloc(idCount * sizeof(CFStringRef));
957        if (!addressKeys) {
958            OSKextLogMemError();
959            result = EX_OSERR;
960            *fatal = true;
961            goto finish;
962        }
963        CFDictionaryGetKeysAndValues(toolArgs->loadAddresses,
964            (void *)addressKeys, /* values */ NULL);
965
966        for (idIndex = 0; idIndex < idCount; idIndex++) {
967            if (kCFNotFound == CFArrayGetFirstIndexOfValue(toolArgs->kextIDs,
968                RANGE_ALL(toolArgs->kextIDs), addressKeys[idIndex])) {
969
970                CFArrayAppendValue(toolArgs->kextIDs, addressKeys[idIndex]);
971            }
972        }
973    }
974
975   /*****
976    * If we have CFBundleIdentifiers, then look them up. We just add to the
977    * kextsToProcess array here.
978    */
979    idCount = CFArrayGetCount(toolArgs->kextIDs);
980    for (idIndex = 0; idIndex < idCount; idIndex++) {
981        CFStringRef thisKextID = (CFStringRef)
982            CFArrayGetValueAtIndex(toolArgs->kextIDs, idIndex);
983
984        SAFE_FREE_NULL(kextIDCString);
985
986        kextIDCString = createUTF8CStringForCFString(thisKextID);
987        if (!kextIDCString) {
988            OSKextLogMemError();
989            result = EX_OSERR;
990            goto finish;
991        }
992
993       /* First see if we already have this kext in the list of kexts
994        * to process. Only if we don't have it by name do we look it up
995        * by identifier. This should allow kexts named by path on the command
996        * line to take precendence over any bundle ID lookups; for example,
997        * if the user is working with an older version of the kext, an
998        * identifier lookup would find the newer.
999        */
1000        kextCount = CFArrayGetCount(kextsToProcess);
1001        for (kextIndex = 0; kextIndex < kextCount; kextIndex++) {
1002            OSKextRef scanKext =  (OSKextRef)CFArrayGetValueAtIndex(
1003                kextsToProcess, kextIndex);
1004            CFStringRef scanKextID = OSKextGetIdentifier(scanKext);
1005
1006            if (CFEqual(thisKextID, scanKextID)) {
1007                theKext = scanKext;
1008                break;
1009            }
1010        }
1011
1012        if (!theKext) {
1013            OSKextLog(/* kext */ NULL,
1014                kOSKextLogStepLevel | kOSKextLogKextBookkeepingFlag,
1015                "Looking up extension with identifier %s.",
1016                kextIDCString);
1017
1018            theKext = OSKextGetKextWithIdentifier(thisKextID);
1019        }
1020
1021        if (!theKext) {
1022            OSKextLog(/* kext */ NULL,
1023                kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
1024                "Can't find extension with identifier %s.",
1025                kextIDCString);
1026            result = kKextutilExitNotFound;
1027            continue;  // not fatal, keep trying the others
1028        }
1029
1030        kextURL = OSKextGetURL(theKext);
1031        if (!CFURLGetFileSystemRepresentation(kextURL,
1032            /* resolveToBase */ false,
1033            (u_char *)kextPathCString, sizeof(kextPathCString))) {
1034
1035            OSKextLogStringError(theKext);
1036            result = EX_OSERR;
1037            *fatal = true;
1038            goto finish;
1039        }
1040
1041        OSKextLog(/* kext */ NULL,
1042            kOSKextLogStepLevel | kOSKextLogKextBookkeepingFlag,
1043            "Found %s for identifier %s.", kextPathCString, kextIDCString);
1044
1045        addToArrayIfAbsent(kextsToProcess, theKext);
1046
1047       /* As above, so below; enable logging for the kext we just looked up.
1048        */
1049        OSKextSetLoggingEnabled(theKext, true);
1050    }
1051
1052finish:
1053    SAFE_FREE(addressKeys);
1054    SAFE_FREE(kextIDCString);
1055
1056   /* Let go of these two tool args, we don't need them any more.
1057    */
1058    SAFE_RELEASE_NULL(toolArgs->kextURLs);
1059    SAFE_RELEASE_NULL(toolArgs->kextIDs);
1060
1061    if (*fatal) {
1062        SAFE_RELEASE(kextsToProcess);
1063        *outArray = NULL;
1064    } else {
1065        *outArray = kextsToProcess;
1066    }
1067    return result;
1068}
1069
1070/*******************************************************************************
1071*******************************************************************************/
1072typedef struct {
1073    Boolean fatal;
1074} SetAddressContext;
1075
1076void
1077setKextLoadAddress(
1078    const void * vKey,
1079    const void * vValue,
1080    void       * vContext)
1081{
1082    CFStringRef         bundleID          = (CFStringRef)vKey;
1083    CFNumberRef         loadAddressNumber = (CFNumberRef)vValue;
1084    SetAddressContext * context           = (SetAddressContext *)vContext;
1085    CFArrayRef          kexts             = NULL;  // must release
1086    OSKextRef           theKext           = NULL;  // do not release
1087    uint64_t            loadAddress       = 0;
1088    CFIndex             count, i;
1089
1090    if (context->fatal) {
1091        goto finish;
1092    }
1093
1094    if (!CFNumberGetValue(loadAddressNumber, kCFNumberSInt64Type,
1095        &loadAddress)) {
1096
1097        context->fatal = true;
1098        goto finish;
1099    }
1100
1101
1102    kexts = OSKextCopyKextsWithIdentifier(bundleID);
1103    if (!kexts) {
1104        goto finish;
1105    }
1106
1107    count = CFArrayGetCount(kexts);
1108    for (i = 0; i < count; i++) {
1109
1110        theKext = (OSKextRef)CFArrayGetValueAtIndex(kexts, i);
1111        if (!OSKextSetLoadAddress(theKext, loadAddress)) {
1112            context->fatal = true;
1113            goto finish;
1114        }
1115    }
1116
1117finish:
1118    SAFE_RELEASE(kexts);
1119    return;
1120}
1121
1122/*******************************************************************************
1123*******************************************************************************/
1124ExitStatus
1125processKexts(
1126    CFArrayRef     kextsToProcess,
1127    KextutilArgs * toolArgs)
1128{
1129    ExitStatus          result             = EX_OK;
1130    OSReturn            readLoadInfoResult = kOSReturnError;
1131    Boolean             fatal              = false;
1132    SetAddressContext   setLoadAddressContext;
1133    CFIndex             count, i;
1134
1135    if (toolArgs->getAddressesFromKernel) {
1136        readLoadInfoResult = OSKextReadLoadedKextInfo(
1137            /* kextIdentifiers */ NULL,
1138            /* flushDependencies */ true);
1139        if (readLoadInfoResult !=  kOSReturnSuccess) {
1140            OSKextLog(/* kext */ NULL,
1141                kOSKextLogErrorLevel | kOSKextLogLoadFlag,
1142                "Failed to read load info for kexts - %s.",
1143                safe_mach_error_string(readLoadInfoResult));
1144            result = EX_OSERR;
1145            goto finish;
1146        }
1147    } else if (toolArgs->loadAddresses) {
1148        setLoadAddressContext.fatal = false;
1149        CFDictionaryApplyFunction(toolArgs->loadAddresses, setKextLoadAddress,
1150            &setLoadAddressContext);
1151        if (setLoadAddressContext.fatal) {
1152            result = EX_OSERR;  // xxx - or may software
1153            goto finish;
1154        }
1155    }
1156
1157    /* Get busy loading kexts.
1158     */
1159    count = CFArrayGetCount(kextsToProcess);
1160    for (i = 0; i < count; i++) {
1161        OSKextRef theKext = (OSKextRef)CFArrayGetValueAtIndex(kextsToProcess, i);
1162
1163        int loadResult = processKext(theKext, toolArgs, &fatal);
1164
1165       /* Save the first non-OK loadResult as the return value.
1166        */
1167        if (result == EX_OK && loadResult != EX_OK) {
1168            result = loadResult;
1169        }
1170        if (fatal) {
1171            goto finish;
1172        }
1173    }
1174finish:
1175    return result;
1176}
1177
1178/*******************************************************************************
1179*******************************************************************************/
1180ExitStatus
1181processKext(
1182    OSKextRef      aKext,
1183    KextutilArgs * toolArgs,
1184    Boolean      * fatal)
1185{
1186    ExitStatus result   = EX_OK;
1187    char       kextPathCString[PATH_MAX];
1188
1189    if (!CFURLGetFileSystemRepresentation(OSKextGetURL(aKext),
1190        /* resolveToBase */ false,
1191        (u_char *)kextPathCString, sizeof(kextPathCString))) {
1192
1193        OSKextLogMemError();
1194        result = EX_OSERR;
1195        *fatal = true;
1196        goto finish;
1197    }
1198
1199    result = runTestsOnKext(aKext, kextPathCString, toolArgs, fatal);
1200    if (result != EX_OK) {
1201        goto finish;
1202    }
1203
1204   /* If there's no more work to do beyond printing test results,
1205    * skip right to the end.
1206    */
1207    if (!toolArgs->doLoad &&
1208        !toolArgs->doStartMatching &&
1209        !toolArgs->symbolDirURL) {
1210
1211        goto finish;
1212    }
1213
1214    if (toolArgs->doLoad) {
1215        OSStatus  sigResult = checkKextSignature(aKext, true, false);
1216        if ( sigResult != 0 ) {
1217            if ( isInvalidSignatureAllowed() ) {
1218                OSKextLogCFString(NULL,
1219                                  kOSKextLogErrorLevel | kOSKextLogLoadFlag,
1220                                  CFSTR("kext-dev-mode allowing invalid signature %ld 0x%02lX for kext \"%s\""),
1221                                  (long)sigResult, (long)sigResult, kextPathCString);
1222            }
1223            else {
1224                CFStringRef myBundleID;         // do not release
1225
1226                myBundleID = OSKextGetIdentifier(aKext);
1227                result = kOSKextReturnNotLoadable; // see 13024670
1228                OSKextLogCFString(NULL,
1229                                  kOSKextLogErrorLevel | kOSKextLogLoadFlag,
1230                                  CFSTR("ERROR: invalid signature for %@, will not load"),
1231                                  myBundleID ? myBundleID : CFSTR("Unknown"));
1232                goto finish;
1233            }
1234        }
1235
1236        result = loadKext(aKext, kextPathCString, toolArgs, fatal);
1237    }
1238    if (result != EX_OK) {
1239        goto finish;
1240    }
1241
1242    if (toolArgs->doLoad) {
1243        /* <rdar://problem/12435992> */
1244        recordKextLoadForMT(aKext);
1245    }
1246
1247   /* Reread loaded kext info to reflect newly-loaded kexts
1248    * if we need to save symbols (which requires load addresses)
1249    * or do interactive stuff.
1250    *
1251    * xxx - Could optimize OSKextReadLoadedKextInfo() with list
1252    * xxx - of kextIdentifiers from load list.
1253    */
1254    if (toolArgs->doLoad &&
1255        (toolArgs->symbolDirURL ||
1256        toolArgs->interactiveLevel != kOSKextExcludeNone)) {
1257
1258        OSReturn readLoadedResult =  OSKextReadLoadedKextInfo(
1259            /* kextIdentifiers */ NULL,
1260            /* flushDependencies */ true);
1261        if (kOSReturnSuccess != readLoadedResult) {
1262            OSKextLog(/* kext */ NULL,
1263                kOSKextLogErrorLevel | kOSKextLogLoadFlag,
1264                "Failed to reread load info after loading %s - %s.",
1265                kextPathCString,
1266                safe_mach_error_string(readLoadedResult));
1267            result = EX_OSERR;
1268            // xxx - fatal?
1269            goto finish;
1270        }
1271    }
1272
1273    if (toolArgs->symbolDirURL) {
1274        result = generateKextSymbols(aKext, kextPathCString, toolArgs,
1275            /* save? */ TRUE, fatal);
1276        if (result != EX_OK) {
1277            goto finish;
1278        }
1279    }
1280
1281    if (toolArgs->doLoad ||
1282        toolArgs->doStartMatching) {
1283
1284        result = startKextsAndSendPersonalities(aKext, toolArgs,
1285            fatal);
1286        if (result != EX_OK) {
1287            goto finish;
1288        }
1289    }
1290
1291finish:
1292
1293    return result;
1294}
1295
1296/*******************************************************************************
1297*******************************************************************************/
1298ExitStatus
1299runTestsOnKext(
1300    OSKextRef      aKext,
1301    char         * kextPathCString,
1302    KextutilArgs * toolArgs,
1303    Boolean      * fatal)
1304{
1305    ExitStatus     result        = EX_OK;
1306    ExitStatus     tempResult    = EX_OK;
1307    Boolean        kextLooksGood = true;
1308    Boolean        tryLink       = false;
1309    OSKextLogSpec  logFilter     = OSKextGetLogFilter(/* kernel? */ false);
1310    OSStatus        sigResult    = 0;
1311
1312   /* Print message if not loadable in safe boot, but keep going
1313    * for further test results.
1314    */
1315    if (toolArgs->safeBootMode &&
1316        !OSKextIsLoadableInSafeBoot(aKext)) {
1317
1318        Boolean        mustQualify = (toolArgs->doLoad || toolArgs->doStartMatching);
1319        OSKextLogSpec  msgLogLevel = kOSKextLogErrorLevel;
1320
1321        if (mustQualify) {
1322            msgLogLevel = kOSKextLogWarningLevel;
1323        }
1324
1325        OSKextLog(/* kext */ NULL,
1326            msgLogLevel | kOSKextLogLoadFlag,
1327            "%s%s is not eligible for loading during safe boot.",
1328            // label it just a notice if we won't be loading
1329            mustQualify ? "" : "Notice: ",
1330            kextPathCString);
1331
1332        if (mustQualify && result == EX_OK) {
1333            result = kKextutilExitSafeBoot;
1334        }
1335    }
1336
1337    if (OSKextHasLogOrDebugFlags(aKext)) {
1338        // xxx - check newline
1339        OSKextLog(/* kext */ NULL,
1340            kOSKextLogWarningLevel | kOSKextLogLoadFlag,
1341            "Notice: %s has debug properties set.", kextPathCString);
1342    }
1343
1344   /* Run the tests for this kext. These would normally be done during
1345    * a load anyhow, but we need the results up-front. *Always* call
1346    * the test function before the && so it actually runs; we want all
1347    * tests performed.
1348    */
1349    kextLooksGood = OSKextValidate(aKext) && kextLooksGood;
1350    if (!toolArgs->skipAuthentication) {
1351         kextLooksGood = OSKextAuthenticate(aKext) && kextLooksGood;
1352    }
1353    if (!toolArgs->skipDependencies) {
1354         kextLooksGood = OSKextResolveDependencies(aKext) && kextLooksGood;
1355         kextLooksGood = OSKextValidateDependencies(aKext) && kextLooksGood;
1356        if (!toolArgs->skipAuthentication) {
1357             kextLooksGood = OSKextAuthenticateDependencies(aKext) &&
1358                 kextLooksGood;
1359        }
1360    }
1361
1362   /* Check for safe boot loadability. Note we do each check separately so as
1363    * not so short-circuit the checks; we want all diagnostics available.
1364    */
1365    if (toolArgs->safeBootMode) {
1366        kextLooksGood = OSKextIsLoadableInSafeBoot(aKext) && kextLooksGood;
1367        kextLooksGood = OSKextDependenciesAreLoadableInSafeBoot(aKext) && kextLooksGood;
1368    }
1369
1370    /* Check code signature for diagnotic messages.
1371     */
1372    sigResult = checkKextSignature(aKext, false, false);
1373
1374   /*****
1375    * Print diagnostics/warnings as needed, set status if kext can't be used.
1376    */
1377    if (!kextLooksGood || sigResult != 0) {
1378        if ((logFilter & kOSKextLogLevelMask) >= kOSKextLogErrorLevel) {
1379            OSKextLog(aKext,
1380                kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
1381                "Diagnostics for %s:",
1382                kextPathCString);
1383
1384            OSKextLogDiagnostics(aKext, kOSKextDiagnosticsFlagAll);
1385            if (sigResult != 0) {
1386                OSKextLog(aKext,
1387                          kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
1388                          "Code Signing Failure: %s",
1389                          (sigResult == errSecCSUnsigned
1390                          ? "not code signed"
1391                          : "code signature is invalid") );
1392            }
1393        }
1394        if (!kextLooksGood) {
1395            result = kKextutilExitKextBad;
1396            goto finish;
1397        }
1398    }
1399
1400   /* Print diagnostics/warnings as needed, set status if kext can't be used.
1401    */
1402    if (result == EX_OK) {
1403        if ((logFilter & kOSKextLogLevelMask) >= kOSKextLogWarningLevel) {
1404            // xxx - used to print "%s has potential problems:", kextPathCString
1405            OSKextLogDiagnostics(aKext, kOSKextDiagnosticsFlagWarnings);
1406        }
1407    }
1408
1409   /* Do a trial link if we aren't otherwise linking, and replace a non-error
1410    * result with the tempResult of linking.
1411    */
1412    tryLink = !toolArgs->doLoad &&
1413        !toolArgs->symbolDirURL &&
1414        kextLooksGood;
1415    tryLink = tryLink &&
1416        ((OSKextGetArchitecture() == OSKextGetRunningKernelArchitecture()) ||
1417        toolArgs->kernelURL);
1418    if (tryLink) {
1419
1420        tempResult = generateKextSymbols(aKext,
1421            kextPathCString, toolArgs, /* save? */ false, fatal);
1422        tryLink = true;
1423        if (result == EX_OK) {
1424            result = tempResult;
1425        }
1426        // Do not goto finish from here! We want to print diagnostics first.
1427    }
1428
1429    if (result == EX_OK) {
1430        OSKextLog(/* kext */ NULL,
1431            kOSKextLogBasicLevel | kOSKextLogLoadFlag,
1432            "%s appears to be loadable (%sincluding linkage for on-disk libraries).",
1433            kextPathCString, tryLink ? "" : "not ");
1434    }
1435
1436#if 0
1437// just testing this
1438    OSKextLogDependencyGraph(aKext, /* bundleIDs */ true,
1439        /* linkGraph */ true);
1440#endif
1441
1442finish:
1443    return result;
1444}
1445
1446/*******************************************************************************
1447*******************************************************************************/
1448ExitStatus
1449loadKext(
1450    OSKextRef      aKext,
1451    char         * kextPathCString,
1452    KextutilArgs * toolArgs,
1453    Boolean      * fatal)
1454{
1455    ExitStatus         result           = EX_OK;
1456    OSKextExcludeLevel startExclude     = toolArgs->interactiveLevel;
1457    OSKextExcludeLevel matchExclude     = toolArgs->interactiveLevel;
1458    CFArrayRef         personalityNames = toolArgs->personalityNames;
1459    OSReturn           loadResult       = kOSReturnError;
1460
1461    if (OSKextIsInExcludeList(aKext, false)) {
1462#if 1 // <rdar://problem/12811081>
1463        /* notify kextd we are trying to load an excluded kext.
1464         */
1465        CFMutableDictionaryRef      myAlertInfoDict = NULL; // must release
1466
1467        addKextToAlertDict(&myAlertInfoDict, aKext);
1468        if (myAlertInfoDict) {
1469            postNoteAboutKexts(CFSTR("Excluded Kext Notification"),
1470                               myAlertInfoDict );
1471            SAFE_RELEASE(myAlertInfoDict);
1472        }
1473#endif
1474
1475        messageTraceExcludedKext(aKext);
1476        OSKextLog(NULL,
1477                  kOSKextLogErrorLevel | kOSKextLogArchiveFlag |
1478                  kOSKextLogValidationFlag | kOSKextLogGeneralFlag,
1479                  "%s is in exclude list; omitting.", kextPathCString);
1480        result = kOSKextReturnNotLoadable;
1481        *fatal = true;
1482        goto finish;
1483    }
1484
1485   /* INTERACTIVE: ask if ok to load kext and its dependencies
1486    */
1487    if (toolArgs->interactiveLevel != kOSKextExcludeNone) {
1488
1489        switch (user_approve(/* ask_all */ FALSE, /* default_answer */ REPLY_YES,
1490            "Load %s and its dependencies into the kernel",
1491            kextPathCString)) {
1492
1493            case REPLY_NO:
1494                fprintf(stderr, "Not loading %s.", kextPathCString);
1495                goto finish;  // result is EX_OK!
1496                break;
1497            case REPLY_YES:
1498                break;
1499            default:
1500                fprintf(stderr, "Failed to read response.");
1501                result = EX_SOFTWARE;
1502                *fatal = true;
1503                goto finish;
1504                break;
1505        }
1506    }
1507
1508    OSKextLog(/* kext */ NULL, kOSKextLogBasicLevel | kOSKextLogLoadFlag,
1509        "Loading %s.", kextPathCString);
1510
1511   /* Mask out the personality/matching args as required.
1512    */
1513    if (toolArgs->interactiveLevel != kOSKextExcludeNone) {
1514        personalityNames = NULL;
1515    }
1516    if (!toolArgs->doStartMatching) {
1517        matchExclude = kOSKextExcludeAll;
1518    }
1519
1520    loadResult = OSKextLoadWithOptions(aKext,
1521        startExclude, matchExclude, personalityNames,
1522        /* disableAutounload */ (startExclude != kOSKextExcludeNone));
1523
1524    if (loadResult == kOSReturnSuccess) {
1525        OSKextLog(/* kext */ NULL, kOSKextLogBasicLevel | kOSKextLogLoadFlag,
1526            "%s successfully loaded (or already loaded).",
1527            kextPathCString);
1528    } else {
1529        OSKextLog(/* kext */ NULL, kOSKextLogBasicLevel | kOSKextLogLoadFlag,
1530            "Failed to load %s - %s.",
1531            kextPathCString, safe_mach_error_string(loadResult));
1532    }
1533
1534    if (loadResult == kOSKextReturnLinkError) {
1535        OSKextLog(/* kext */ NULL,
1536            kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
1537            "Check library declarations for your kext with kextlibs(8).");
1538    }
1539
1540    if (loadResult != kOSReturnSuccess) {
1541        result = kKextutilExitLoadFailed;
1542    }
1543
1544finish:
1545    return result;
1546}
1547
1548/*******************************************************************************
1549*******************************************************************************/
1550// xxx - generally need to figure out when to flush what
1551
1552ExitStatus generateKextSymbols(
1553    OSKextRef      aKext,
1554    char         * kextPathCString,
1555    KextutilArgs * toolArgs,
1556    Boolean        saveFlag,
1557    Boolean      * fatal)
1558{
1559    ExitStatus         result      = EX_OK;
1560    CFArrayRef         loadList    = NULL;    // must release
1561    CFDictionaryRef    kextSymbols = NULL;    // must release
1562    const NXArchInfo * archInfo    = NULL;    // do not free
1563    CFIndex            count, i;
1564
1565    if (saveFlag && !toolArgs->symbolDirURL) {
1566        result = EX_USAGE;
1567        *fatal = TRUE;
1568        goto finish;
1569    }
1570
1571    // xxx - we might want to check these before processing any kexts
1572    if (OSKextIsKernelComponent(aKext)) {
1573        OSKextLog(/* kext */ NULL,
1574            kOSKextLogErrorLevel | kOSKextLogLoadFlag,
1575            "%s is a kernel component; no symbols to generate.",
1576            kextPathCString);
1577        result = EX_DATAERR;
1578        goto finish;
1579    }
1580
1581    if (OSKextIsInterface(aKext)) {
1582        OSKextLog(/* kext */ NULL,
1583            kOSKextLogErrorLevel | kOSKextLogLoadFlag,
1584            "%s is a an interface kext; no symbols to generate.",
1585            kextPathCString);
1586        result = EX_DATAERR;
1587        goto finish;
1588    }
1589
1590    archInfo = OSKextGetArchitecture();
1591    if (!OSKextSupportsArchitecture(aKext, archInfo)) {
1592        int native = (archInfo == NXGetLocalArchInfo());
1593        OSKextLog(/* kext */ NULL,
1594            kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
1595            "%s does not contain code for %sarchitecture%s%s.",
1596            kextPathCString,
1597            native ? "this computer's " : "",
1598            native ? "" : " ",
1599            native ? "" : archInfo->name);
1600        result = kKextutilExitArchNotFound;
1601        goto finish;
1602    }
1603
1604    /*****
1605     * If we don't have a load address for aKext, ask for load addresses for it
1606     * and any of its dependencies.
1607     * NOTE (9656777) - OSKextNeedsLoadAddressForDebugSymbols() needs to be
1608     * called even if we loaded the kext.  The reason is that loading the kext
1609     * does not necessarily mean the load address was set in the load info
1610     * kept in the kernel.  Calling OSKextNeedsLoadAddressForDebugSymbols()
1611     * will implicitly set the load address if the kernel has it thus
1612     * avoiding having to ask the user for it.  And without that load address
1613     * we silently give back a partial symbol file that gdb dislikes.
1614     */
1615    if (saveFlag &&
1616        OSKextNeedsLoadAddressForDebugSymbols(aKext)) {
1617
1618        loadList = OSKextCopyLoadList(aKext, /* needAll */ true);
1619        if (!loadList) {
1620            OSKextLog(/* kext */ NULL,
1621                kOSKextLogErrorLevel | kOSKextLogLoadFlag,
1622                "Can't resolve dependencies for %s.",
1623                kextPathCString);
1624            result = EX_SOFTWARE;
1625            *fatal = true;
1626            goto finish;
1627        }
1628
1629       /*****
1630        * For each kext w/o an address in loadAddresses, ask for an address.
1631        */
1632
1633        fprintf(stderr, "\nEnter the hexadecimal load addresses for these extensions\n"
1634            "(press Return to skip symbol generation for an extension):\n\n");
1635
1636        count = CFArrayGetCount(loadList);
1637        for (i = 0; i < count; i++) {
1638            OSKextRef thisKext = (OSKextRef)CFArrayGetValueAtIndex(loadList, i);
1639            Boolean   mainNeedsAddress = ((i + 1) == count);
1640
1641            do {
1642                switch (requestLoadAddress(thisKext)) {
1643                    case -1: // error
1644                        result = EX_SOFTWARE;
1645                        goto finish;
1646                        break;
1647                    case 0: // user cancel
1648                        fprintf(stderr, "\nuser canceled address input; exiting\n");
1649                        result = kKextutilExitUserAbort;
1650                        goto finish;
1651                        break;
1652                    case 1: // ok to continue
1653                        break;
1654                } /* switch */
1655
1656               /* If we didn't get a load address for the main kext, the user
1657                * probably hit Return too many times.
1658                */
1659                if (mainNeedsAddress && OSKextNeedsLoadAddressForDebugSymbols(aKext)) {
1660                    switch (user_approve(/* ask_all */ FALSE, /* default_anser */ REPLY_NO,
1661                        "\n%s is the main extension; really skip", kextPathCString)) {
1662                        case REPLY_NO:
1663                            break;
1664                        case REPLY_YES:
1665                            result = EX_OK;
1666                            goto finish;  // skip symbol gen.
1667                        default:
1668                            result = EX_SOFTWARE;
1669                            goto finish;
1670                    }
1671                }
1672            } while (mainNeedsAddress && OSKextNeedsLoadAddressForDebugSymbols(aKext));
1673        }
1674    }
1675
1676    kextSymbols = OSKextGenerateDebugSymbols(aKext, toolArgs->kernelFile);
1677    if (!kextSymbols) {
1678        OSKextLog(/* kext */ NULL,
1679            kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
1680            "Check library declarations for your kext with kextlibs(8).");
1681        result = kKextutilExitKextBad;  // more probably than an EX_OSERR
1682        *fatal = true;
1683        goto finish;
1684    }
1685
1686    if (saveFlag) {
1687        SaveFileContext saveFileContext;
1688        saveFileContext.saveDirURL = toolArgs->symbolDirURL;
1689        saveFileContext.overwrite = toolArgs->overwriteSymbols;
1690        saveFileContext.fatal = false;
1691        CFDictionaryApplyFunction(kextSymbols, &saveFile, &saveFileContext);
1692        if (saveFileContext.fatal) {
1693            *fatal = true;
1694            goto finish;
1695        }
1696    }
1697
1698finish:
1699    SAFE_RELEASE(loadList);
1700    SAFE_RELEASE(kextSymbols);
1701
1702    return result;
1703}
1704
1705
1706/*******************************************************************************
1707*******************************************************************************/
1708int
1709requestLoadAddress(
1710    OSKextRef aKext)
1711{
1712    int           result          = -1;
1713    char        * bundleIDCString = NULL;  // must free
1714    char        * user_response   = NULL;  // must free
1715    char        * scan_pointer    = NULL;  // do not free
1716    uint64_t      address = 0;
1717    Boolean       eof = false;
1718
1719    bundleIDCString = createUTF8CStringForCFString(
1720        OSKextGetIdentifier(aKext));
1721    if (!bundleIDCString) {
1722        goto finish;
1723    }
1724
1725    if (OSKextNeedsLoadAddressForDebugSymbols(aKext)) {
1726
1727        while (1) {
1728            SAFE_FREE(user_response);
1729
1730            user_response = (char *)user_input(&eof, "%s:", bundleIDCString);
1731            if (eof) {
1732                result = 0;
1733                goto finish;
1734            }
1735            if (!user_response) {
1736                goto finish;
1737            }
1738
1739           /* User wants to skip this one, don't set address & return success.
1740            */
1741            if (user_response[0] == '\0') {
1742                result = 1;
1743                goto finish;
1744                break;
1745            }
1746
1747            errno = 0;
1748            address = strtoull(user_response, &scan_pointer, 16);
1749
1750            if (address == ULONG_LONG_MAX && errno == ERANGE) {
1751                fprintf(stderr, "input address %s is too large; try again\n",
1752                    user_response);
1753                continue;
1754            } else if (address == 0 && errno == EINVAL) {
1755                fprintf(stderr, "no address found in input '%s'; try again\n",
1756                    user_response);
1757                continue;
1758            } else if (address == 0) {
1759                fprintf(stderr, "invalid address %s\n",
1760                    user_response);
1761                continue;
1762            } else if (*scan_pointer != '\0') {
1763                fprintf(stderr,
1764                    "input '%s' not a plain hexadecimal address; try again\n",
1765                    user_response);
1766                continue;
1767            } else {
1768                break;
1769            }
1770        }
1771
1772        OSKextSetLoadAddress(aKext, address);
1773    }
1774
1775    result = 1;
1776
1777finish:
1778    SAFE_FREE(bundleIDCString);
1779    SAFE_FREE(user_response);
1780    return result;
1781}
1782
1783/*******************************************************************************
1784xxx - should we be using paths or bundleids?
1785*******************************************************************************/
1786ExitStatus startKextsAndSendPersonalities(
1787    OSKextRef      aKext,
1788    KextutilArgs * toolArgs,
1789    Boolean      * fatal)
1790{
1791    ExitStatus          result                      = EX_OK;
1792    CFArrayRef          loadList                    = NULL;   // must release
1793    CFMutableArrayRef   kextIdentifiers             = NULL;  // must release
1794    char              * kextIDCString               = NULL;   // must free
1795    char              * thisKextIDCString           = NULL;   // must free
1796    Boolean             startedAndPersonalitiesSent = TRUE;   // loop breaks if ever false
1797    Boolean             yesToAllKexts               = FALSE;
1798    Boolean             yesToAllKextPersonalities   = FALSE;
1799    char                kextPath[PATH_MAX];
1800    CFIndex             count, i;  // used unothodoxically!
1801
1802    if (!CFURLGetFileSystemRepresentation(OSKextGetURL(aKext),
1803        /* resoveToBase */ false, (UInt8*)kextPath, sizeof(kextPath))) {
1804
1805        strlcpy(kextPath, "(unknown)", sizeof(kextPath));
1806    }
1807
1808    kextIDCString = createUTF8CStringForCFString(OSKextGetIdentifier(aKext));
1809    if (!kextIDCString) {
1810        OSKextLogMemError();
1811        result = EX_OSERR;
1812    }
1813
1814    loadList = OSKextCopyLoadList(aKext, /* needAll */ true);
1815    if (!loadList) {
1816        OSKextLog(/* kext */ NULL,
1817            kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
1818            "Can't get dependency list for %s.",
1819            kextIDCString);
1820        result = EX_OSERR;
1821        goto finish;
1822    }
1823
1824    count = CFArrayGetCount(loadList);
1825    if (!count) {
1826        OSKextLog(/* kext */ NULL,
1827            kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
1828            "Internal error - empty load list.");
1829        result = EX_SOFTWARE;
1830        *fatal = true;
1831        goto finish;
1832    }
1833
1834   /* We'll be using this in a call to OSKextReadLoadedKextInfo()
1835    * if something goes wrong.
1836    */
1837    kextIdentifiers = CFArrayCreateMutable(CFGetAllocator(aKext),
1838        count, &kCFTypeArrayCallBacks);
1839    if (!kextIdentifiers) {
1840        OSKextLogMemError();
1841        result = EX_OSERR;
1842        goto finish;
1843    }
1844
1845   /*****
1846    * For interactive loading to do gdb on start functions,
1847    * go through the load list and print the started status of each kext.
1848    */
1849    if (toolArgs->interactiveLevel != kOSKextExcludeNone && toolArgs->doLoad) {
1850
1851        fprintf(stderr, "\n"
1852            "%s and its dependencies are now loaded, and started as listed below. "
1853            "You can now return to the debugger to set breakpoints before starting "
1854            "any kexts that need to be started.\n\n",
1855            kextPath);
1856
1857       /* If we're only doing interactive for the main kext, bump the loop
1858        * index to the last one.
1859        */
1860        if (toolArgs->interactiveLevel == kOSKextExcludeKext) {
1861            i = count - 1;
1862        } else {
1863            i = 0;
1864        }
1865        for (/* see above */ ; i < count; i++) {
1866            OSKextRef thisKext = (OSKextRef)CFArrayGetValueAtIndex(
1867                loadList, i);
1868            const char * status = NULL;
1869
1870            SAFE_FREE_NULL(thisKextIDCString);
1871
1872            if (!CFURLGetFileSystemRepresentation(OSKextGetURL(thisKext),
1873                /* resoveToBase */ false, (UInt8*)kextPath, sizeof(kextPath))) {
1874
1875                strlcpy(kextPath, "(unknown)", sizeof(kextPath));
1876            }
1877
1878            thisKextIDCString = createUTF8CStringForCFString(OSKextGetIdentifier(thisKext));
1879            if (!thisKextIDCString) {
1880                OSKextLogMemError();
1881                result = EX_OSERR;
1882                goto finish;
1883            }
1884
1885            if (OSKextIsInterface(thisKext)) {
1886                status = "interface, not startable";
1887            } else if (!OSKextDeclaresExecutable(thisKext)) {
1888                status = "no executable, not startable";
1889            } else if (OSKextIsStarted(thisKext)) {
1890                status = "already started";
1891            } else {
1892                status = "not started";
1893            }
1894            fprintf(stderr, "    %s - %s\n", thisKextIDCString, status);
1895        }
1896
1897        fprintf(stderr, "\n");
1898    }
1899
1900   /*****
1901    * Now go through and actually process each kext.
1902    */
1903
1904   /* If we're only doing interactive for the main kext, bump the loop
1905    * index to the last one.
1906    */
1907    if (toolArgs->interactiveLevel == kOSKextExcludeKext) {
1908        i = count - 1;
1909    } else {
1910        i = 0;
1911    }
1912    for (/* see above */ ; i < count; i++) {
1913        OSKextRef thisKext = (OSKextRef)CFArrayGetValueAtIndex(
1914            loadList, i);
1915
1916        SAFE_FREE_NULL(thisKextIDCString);
1917
1918        if (!CFURLGetFileSystemRepresentation(OSKextGetURL(thisKext),
1919            /* resoveToBase */ false, (UInt8*)kextPath, sizeof(kextPath))) {
1920
1921            strlcpy(kextPath, "(unknown)", sizeof(kextPath));
1922        }
1923
1924        CFArrayAppendValue(kextIdentifiers, OSKextGetIdentifier(thisKext));
1925
1926        thisKextIDCString = createUTF8CStringForCFString(OSKextGetIdentifier(thisKext));
1927        if (!thisKextIDCString) {
1928            OSKextLogMemError();
1929            result = EX_OSERR;
1930            goto finish;
1931        }
1932
1933       /* Normally the kext is started when loaded, so only try to start it here
1934        * if we're in interactive mode and we loaded it.
1935        */
1936        if (toolArgs->interactiveLevel != kOSKextExcludeNone && toolArgs->doLoad) {
1937            if (!OSKextIsStarted(thisKext)) {
1938                result = startKext(thisKext, kextPath, toolArgs,
1939                    &startedAndPersonalitiesSent, &yesToAllKexts, fatal);
1940                if (result != EX_OK || !startedAndPersonalitiesSent) {
1941                    break;
1942                }
1943            }
1944        }
1945
1946       /* Normally the kext's personalities are sent when the kext is loaded,
1947        * so send them from here only if we are in interactive mode or
1948        * if we are only sending personalities and not loading.
1949        * It's ok to send personalities again if they're already in the kernel;
1950        * that just restarts matching.
1951        */
1952        if (toolArgs->interactiveLevel != kOSKextExcludeNone ||
1953            (toolArgs->doStartMatching && !toolArgs->doLoad)) {
1954
1955            result = sendPersonalities(thisKext, kextPath, toolArgs,
1956                /* isMain */ (i + 1 == count), &yesToAllKextPersonalities, fatal);
1957            if (result != EX_OK) {
1958                startedAndPersonalitiesSent = false;
1959                break;
1960            }
1961        }
1962    }
1963
1964   /* If the whole graph didn't start, go backward through it unloading
1965    * any kext that didn't start. We could optimize this a bit by providing
1966    * a list of the kext identifiers in the load list.
1967    */
1968    if (!startedAndPersonalitiesSent) {
1969        OSReturn readLoadedResult = OSKextReadLoadedKextInfo(
1970            kextIdentifiers, /* flushDependencies */ false);
1971
1972        if (kOSReturnSuccess != readLoadedResult) {
1973            OSKextLog(/* kext */ NULL,
1974                kOSKextLogErrorLevel | kOSKextLogLoadFlag | kOSKextLogIPCFlag,
1975                "Failed to read load info after starting - %s.",
1976                safe_mach_error_string(readLoadedResult));
1977            result = EX_OSERR;
1978            *fatal = true;
1979        }
1980
1981        for (i = count - 1; i >= 0; i--) {
1982            OSKextRef thisKext = (OSKextRef)CFArrayGetValueAtIndex(
1983                loadList, i);
1984
1985            if (!CFURLGetFileSystemRepresentation(OSKextGetURL(thisKext),
1986                /* resoveToBase */ false, (UInt8*)kextPath, sizeof(kextPath))) {
1987
1988                strlcpy(kextPath, "(unknown)", sizeof(kextPath));
1989            }
1990
1991            if (!OSKextIsStarted(thisKext)) {
1992                OSReturn unloadResult = OSKextUnload(thisKext,
1993                    /* terminateIOServicesAndRemovePersonalities */ true);
1994
1995                OSKextLog(/* kext */ NULL,
1996                    kOSKextLogStepLevel | kOSKextLogLoadFlag,
1997                    "Unloading kext %s after failing to start/send personalities.", kextPath);
1998
1999                if (kOSReturnSuccess != unloadResult) {
2000                    OSKextLog(/* kext */ NULL,
2001                        kOSKextLogErrorLevel | kOSKextLogLoadFlag,
2002                        "Failed to unload kext %s after failing to start/send personalities - %s.",
2003                        kextPath,
2004                        safe_mach_error_string(unloadResult));
2005                    result = EX_OSERR;
2006                } else {
2007                    OSKextLog(/* kext */ NULL,
2008                        kOSKextLogStepLevel | kOSKextLogLoadFlag,
2009                        "%s unloaded.", kextPath);
2010                }
2011            }
2012        }
2013    }
2014
2015finish:
2016    SAFE_RELEASE(loadList);
2017    SAFE_RELEASE(kextIdentifiers);
2018    SAFE_FREE(kextIDCString);
2019    SAFE_FREE(thisKextIDCString);
2020    return result;
2021}
2022
2023/*******************************************************************************
2024* Be explicit about setting started either way here.
2025*******************************************************************************/
2026ExitStatus startKext(
2027    OSKextRef      aKext,
2028    char         * kextPathCString,
2029    KextutilArgs * toolArgs,
2030    Boolean      * started,
2031    Boolean      * yesToAll,
2032    Boolean      * fatal)
2033{
2034    ExitStatus    result      = EX_OK;
2035    OSReturn      startResult = kOSReturnError;
2036
2037// xxx - check on the log calls in interactive mode; use fprintf instead?
2038
2039   /* Check if the dependency is still loaded. It should be, of course.
2040    * This error is not fatal for the overall program, but we leave
2041    * this function since we can't start any other dependencies
2042    * up to the main kext that was loaded.
2043    */
2044    if (!OSKextIsLoaded(aKext)) {
2045        OSKextLog(/* kext */ NULL,
2046            kOSKextLogErrorLevel | kOSKextLogLoadFlag,
2047            "%s is unexpectedly not loaded after loading!",
2048            kextPathCString);
2049        *started = false;
2050        result = EX_OSERR;
2051        goto finish;
2052    }
2053
2054    if (FALSE == *yesToAll) {
2055        switch (user_approve(/* ask_all */ TRUE,
2056            /* default_answer */ REPLY_YES,
2057            "Start %s",
2058            kextPathCString)) {
2059
2060            case REPLY_NO:
2061                OSKextLog(/* kext */ NULL,
2062                    kOSKextLogBasicLevel | kOSKextLogLoadFlag,
2063                    "Not starting %s.",
2064                    kextPathCString);
2065                *started = false;
2066                goto finish;  // result is EX_OK!
2067                break;
2068            case REPLY_YES:
2069                break;
2070            case REPLY_ALL:
2071                fprintf(stderr, "Starting all kexts just loaded.\n");
2072                *yesToAll = TRUE;
2073                break;
2074            default:
2075                OSKextLog(/* kext */ NULL,
2076                    kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
2077                    "Error: couldn't read response.");
2078                result = EX_SOFTWARE;
2079                *started = false;
2080                *fatal = true;
2081                goto finish;
2082                break;
2083        }
2084    }
2085
2086    startResult = OSKextStart(aKext);
2087    if (kOSReturnSuccess != startResult) {
2088        OSKextLog(/* kext */ NULL,
2089            kOSKextLogErrorLevel | kOSKextLogLoadFlag,
2090            "%s failed to start - %s.",
2091            kextPathCString,
2092            safe_mach_error_string(startResult));
2093
2094        *started = false;
2095        result = EX_OSERR;
2096        goto finish;
2097    } else {
2098        OSKextLog(/* kext */ NULL,
2099            kOSKextLogBasicLevel | kOSKextLogLoadFlag,
2100            "%s started.", kextPathCString);
2101        *started = true;
2102    }
2103
2104finish:
2105    return result;
2106}
2107
2108/*******************************************************************************
2109*******************************************************************************/
2110ExitStatus sendPersonalities(
2111    OSKextRef      aKext,
2112    char         * kextPathCString,
2113    KextutilArgs * toolArgs,
2114    Boolean        isMainFlag,
2115    Boolean      * yesToAllKextPersonalities,
2116    Boolean      * fatal)
2117{
2118    ExitStatus        result                  = EX_OK;
2119    CFDictionaryRef   kextPersonalities       = NULL;  // do not release
2120    CFMutableArrayRef namesToSend             = NULL;  // must release
2121    CFStringRef     * names                   = NULL;  // must free
2122    char            * nameCString             = NULL;  // must free
2123    OSReturn          sendPersonalitiesResult = kOSReturnError;
2124    Boolean           yesToAllPersonalities   = FALSE;
2125    CFIndex           count, i;
2126
2127    kextPersonalities = OSKextGetValueForInfoDictionaryKey(aKext,
2128        CFSTR(kIOKitPersonalitiesKey));
2129    if (!kextPersonalities || !CFDictionaryGetCount(kextPersonalities)) {
2130        OSKextLog(/* kext */ NULL,
2131            kOSKextLogStepLevel | kOSKextLogLoadFlag,
2132            "%s has no personalities to send.",
2133            kextPathCString);
2134        goto finish;
2135    }
2136
2137    if (toolArgs->interactiveLevel != kOSKextExcludeNone) {
2138        if (FALSE == *yesToAllKextPersonalities) {
2139            switch (user_approve(/* ask_all */ TRUE, /* default_answer */ REPLY_YES,
2140                "Send personalities for %s",
2141                kextPathCString)) {
2142
2143                case REPLY_NO:
2144                    fprintf(stderr, "Not sending personalities for %s.", kextPathCString);
2145                    goto finish;  // result is EX_OK!
2146                    break;
2147                case REPLY_YES:
2148                    break;
2149                case REPLY_ALL:
2150                    fprintf(stderr, "Sending personalities for all kexts just loaded.\n");
2151                    *yesToAllKextPersonalities = TRUE;
2152                    break;
2153                default:
2154                    fprintf(stderr, "Error: couldn't read response.");
2155                    result = EX_SOFTWARE;
2156                    *fatal = true;
2157                    goto finish;
2158                    break;
2159            }
2160        }
2161    }
2162
2163    namesToSend = CFArrayCreateMutable(kCFAllocatorDefault, 0,
2164        &kCFTypeArrayCallBacks);
2165    if (!namesToSend) {
2166        OSKextLogMemError();
2167        result = EX_OSERR;
2168        *fatal = true;
2169        goto finish;
2170    }
2171
2172    count = CFDictionaryGetCount(kextPersonalities);
2173    names = (CFStringRef *)malloc(count * sizeof(CFStringRef));
2174    if (!names) {
2175        OSKextLogMemError();
2176        result = EX_OSERR;
2177        *fatal = true;
2178        goto finish;
2179    }
2180    CFDictionaryGetKeysAndValues(kextPersonalities,
2181        (const void **)names, NULL);
2182
2183    for (i = 0; i < count; i++) {
2184        Boolean includeIt = TRUE;
2185
2186        SAFE_FREE_NULL(nameCString);
2187
2188        nameCString = createUTF8CStringForCFString(names[i]);
2189        if (!nameCString) {
2190            OSKextLogMemError();
2191            result = EX_OSERR;
2192            *fatal = true;
2193            goto finish;
2194        }
2195
2196       /* If -p was used on the command line, only consider those personalities.
2197        */
2198        if (isMainFlag && CFArrayGetCount(toolArgs->personalityNames) > 0) {
2199            if (kCFNotFound == CFArrayGetFirstIndexOfValue(
2200                toolArgs->personalityNames,
2201                RANGE_ALL(toolArgs->personalityNames), names[i])) {
2202
2203                continue;
2204            }
2205        }
2206
2207        if (toolArgs->interactiveLevel != kOSKextExcludeNone &&
2208            FALSE == *yesToAllKextPersonalities) {
2209
2210            if (FALSE == yesToAllPersonalities) {
2211                switch (user_approve(/* ask_all */ TRUE, /* default_answer */ REPLY_YES,
2212                    "Send personality %s", nameCString)) {
2213
2214                    case REPLY_NO:
2215                        includeIt = FALSE;
2216                        break;
2217                    case REPLY_YES:
2218                        includeIt = TRUE;
2219                        break;
2220                    case REPLY_ALL:
2221                        fprintf(stderr, "Sending all personalities for %s.\n",
2222                            kextPathCString);
2223                        includeIt = TRUE;
2224                        yesToAllPersonalities = TRUE;
2225                        break;
2226                    default:
2227                        fprintf(stderr, "Error: couldn't read response.");
2228                        result = EX_SOFTWARE;
2229                        *fatal = true;
2230                        goto finish;
2231                        break;
2232                } /* switch */
2233            } /* if (!*yesToAll) */
2234        } /* if (toolArgs->interactiveLevel ... ) */
2235
2236        if (includeIt) {
2237            CFArrayAppendValue(namesToSend, names[i]);
2238        }
2239    } /* for */
2240
2241    OSKextLog(/* kext */ NULL,
2242        kOSKextLogStepLevel | kOSKextLogLoadFlag | kOSKextLogIPCFlag,
2243        "Sending personalities of %s to the IOCatalogue.",
2244        kextPathCString);
2245
2246    sendPersonalitiesResult = OSKextSendKextPersonalitiesToKernel(aKext, namesToSend);
2247    if (kOSReturnSuccess != sendPersonalitiesResult) {
2248        OSKextLog(/* kext */ NULL,
2249            kOSKextLogErrorLevel | kOSKextLogLoadFlag | kOSKextLogIPCFlag,
2250            "Failed to send personalities for %s - %s.",
2251            kextPathCString,
2252            safe_mach_error_string(sendPersonalitiesResult));
2253
2254        result = EX_OSERR;
2255        goto finish;
2256    } else {
2257        OSKextLog(/* kext */ NULL,
2258            kOSKextLogStepLevel | kOSKextLogLoadFlag | kOSKextLogIPCFlag,
2259            "Personalities sent for %s.", kextPathCString);
2260    }
2261
2262finish:
2263    SAFE_RELEASE(namesToSend);
2264    SAFE_FREE(names);
2265    SAFE_FREE(nameCString);
2266    return result;
2267}
2268
2269/*******************************************************************************
2270*******************************************************************************/
2271Boolean serializeLoad(KextutilArgs * toolArgs, Boolean loadFlag)
2272{
2273    Boolean       result       = false;
2274    kern_return_t kern_result;
2275    int           lock_retries = LOCK_MAXTRIES;
2276
2277    if (!loadFlag) {
2278        result = true;
2279        goto finish;
2280    }
2281
2282    /*****
2283    * Serialize running kextload processes that are actually loading. Note
2284    * that we can't bail on failing to contact kextd, we can only print
2285    * warnings, since kextload may need to be run in single-user mode. We
2286    * do bail on hard OS errors though.
2287    */
2288    kern_result = bootstrap_look_up(bootstrap_port,
2289        KEXTD_SERVER_NAME, &sKextdPort);
2290    if (kern_result != KERN_SUCCESS) {
2291
2292        OSKextLog(/* kext */ NULL,
2293            kOSKextLogWarningLevel | kOSKextLogIPCFlag,
2294            "Can't contact kextd (continuing anyway) - %s.",
2295            bootstrap_strerror(kern_result));
2296    }
2297    if (sKextdPort != MACH_PORT_NULL) {
2298        kern_result = mach_port_allocate(mach_task_self(),
2299            MACH_PORT_RIGHT_RECEIVE, &sLockPort);
2300        if (kern_result != KERN_SUCCESS) {
2301            OSKextLog(/* kext */ NULL,
2302                kOSKextLogErrorLevel | kOSKextLogIPCFlag,
2303                "Can't allocate kext loading serialization mach port.");
2304            goto finish;
2305        }
2306        do {
2307            kern_result = kextmanager_lock_kextload(sKextdPort, sLockPort,
2308                &sLockStatus);
2309            if (kern_result != KERN_SUCCESS) {
2310                OSKextLog(/* kext */ NULL,
2311                    kOSKextLogErrorLevel | kOSKextLogIPCFlag,
2312                    "Can't acquire kextload serialization lock; aborting.");
2313                goto finish;
2314            }
2315
2316            if (sLockStatus == EBUSY) {
2317                --lock_retries;
2318                OSKextLog(/* kext */ NULL,
2319                    kOSKextLogWarningLevel | kOSKextLogIPCFlag,
2320                    "Kext loading serialization lock busy; "
2321                    "sleeping (%d retries left).",
2322                    lock_retries);
2323                sleep(LOCK_DELAY);
2324            }
2325        } while (sLockStatus == EBUSY && lock_retries > 0);
2326
2327        if (sLockStatus != 0) {
2328            OSKextLog(/* kext */ NULL,
2329                kOSKextLogErrorLevel | kOSKextLogIPCFlag,
2330                "Can't acquire kextload serialization lock; aborting.");
2331            goto finish;
2332        } else {
2333            sLockTaken = true;
2334        }
2335    }
2336
2337    result = true;
2338finish:
2339    return result;
2340}
2341
2342/*******************************************************************************
2343* usage()
2344*******************************************************************************/
2345void usage(UsageLevel usageLevel)
2346{
2347    fprintf(stderr, "usage: %s [options] [--] [kext] ...\n"
2348      "\n", progname);
2349
2350    if (usageLevel == kUsageLevelBrief) {
2351        fprintf(stderr, "use %s -%s for an explanation of each option\n",
2352            progname, kOptNameHelp);
2353        return;
2354    }
2355
2356    fprintf(stderr, "kext: a kext bundle to load or examine\n");
2357    fprintf(stderr, "\n");
2358
2359    fprintf(stderr, "-%s <bundle_id> (-%c):\n"
2360        "        load/use the kext whose CFBundleIdentifier is <bundle_id>\n",
2361        kOptNameBundleIdentifier, kOptBundleIdentifier);
2362    fprintf(stderr, "-%s <personality> (-%c):\n"
2363        "        send the named personality to the catalog\n",
2364        kOptNamePersonality, kOptPersonality);
2365    fprintf(stderr, "-%s <kext> (-%c):\n"
2366        "        consider <kext> as a candidate dependency\n",
2367        kOptNameDependency, kOptDependency);
2368    fprintf(stderr, "-%s <directory> (-%c):\n"
2369        "        look in <directory> for kexts\n",
2370        kOptNameRepository, kOptRepository);
2371    fprintf(stderr, "\n");
2372
2373    fprintf(stderr, "-%s (-%c):\n"
2374        "        don't use repository caches; scan repository folders\n",
2375        kOptNameNoCaches, kOptNoCaches);
2376    fprintf(stderr, "-%s (-%c):\n"
2377        "        don't check for loaded kexts when resolving dependencies "
2378        "(deprecated)\n",
2379        kOptNameNoLoadedCheck, kOptNoLoadedCheck);
2380    fprintf(stderr, "-%s (-%c):\n"
2381        "        don't use system extension folders\n",
2382        kOptNameNoSystemExtensions, kOptNoSystemExtensions);
2383    fprintf(stderr, "\n");
2384
2385    fprintf(stderr, "-%s (-%c):\n"
2386        "        interactive mode\n",
2387        kOptNameInteractive, kOptInteractive);
2388    fprintf(stderr, "-%s (-%c):\n"
2389        "        interactive mode for extension and all its dependencies\n",
2390        kOptNameInteractiveAll, kOptInteractiveAll);
2391    fprintf(stderr, "\n");
2392
2393    fprintf(stderr, "-%s (-%c):\n"
2394        "        load & start only; don't start matching\n",
2395        kOptNameLoadOnly, kOptLoadOnly);
2396    fprintf(stderr, "-%s (-%c):\n"
2397        "        start matching only, by sending personalities; "
2398        "don't load executable\n",
2399        kOptNameMatchOnly, kOptMatchOnly);
2400    fprintf(stderr, "-%s (-%c):\n"
2401        "        neither load nor start matching\n",
2402        kOptNameNoLoad, kOptNoLoad);
2403    fprintf(stderr, "-%s <directory> (-%c):\n"
2404        "        write symbol files into <directory>\n",
2405        kOptNameSymbolsDirectory, kOptSymbolsDirectory);
2406    fprintf(stderr, "-%s <archname>:\n"
2407        "        use architecture <archnaem>\n",
2408        kOptNameArch);
2409    fprintf(stderr, "-%s <kext_id@address> (-%c):\n"
2410        "        <kext_id> is loaded at address (for symbol generation)\n",
2411        kOptNameAddress, kOptAddress);
2412    fprintf(stderr, "-%s (-%c):\n"
2413        "        get load addresses for kexts from what's loaded "
2414        "(for symbol generation)\n",
2415        kOptNameUseKernelAddresses, kOptUseKernelAddresses);
2416    fprintf(stderr, "-%s <kernelFile> (-%c):\n"
2417        "        link against <kernelFile> (default is /System/Library/Kernels/kernel)\n",
2418        kOptNameKernel, kOptKernel);
2419    fprintf(stderr, "\n");
2420
2421    fprintf(stderr, "-%s (-%c):\n"
2422        "        quiet mode: print no informational or error messages\n",
2423        kOptNameQuiet, kOptQuiet);
2424    fprintf(stderr, "-%s [ 0-6 | 0x<flags> ] (-%c):\n"
2425        "        verbose mode; print info about analysis & loading\n",
2426        kOptNameVerbose, kOptVerbose);
2427    fprintf(stderr, "\n");
2428
2429    fprintf(stderr, "-%s (-%c):\n"
2430        "        perform all diagnostic tests and print a report on each kext\n",
2431        kOptNameTests, kOptTests);
2432    fprintf(stderr, "-%s (-%c):\n"
2433        "        simulate safe boot mode for diagnostic tests\n",
2434        kOptNameSafeBoot, kOptSafeBoot);
2435    fprintf(stderr, "-%s (-%c):\n"
2436        "        don't authenticate kexts (for use during development)\n",
2437        kOptNameNoAuthentication, kOptNoAuthentication);
2438    fprintf(stderr, "-%s (-%c):\n"
2439        "        don't check dependencies when diagnosing with\n"
2440        "        -%s & -%s (-%c%c)\n",
2441        kOptNameNoResolveDependencies, kOptNoResolveDependencies,
2442        kOptNameNoLoad, kOptNameTests,
2443        kOptNoLoad, kOptTests);
2444    fprintf(stderr, "\n");
2445
2446    fprintf(stderr, "-%s (-%c): print this message and exit\n",
2447        kOptNameHelp, kOptHelp);
2448    fprintf(stderr, "\n");
2449
2450    fprintf(stderr, "--: end of options\n");
2451    return;
2452}
2453