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