1/*
2 * Copyright (c) 2000-2012 Apple Inc. All rights reserved.
3 *
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
14 *
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
17 *
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
25 *
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27 */
28#include <CoreFoundation/CoreFoundation.h>
29#include <CoreFoundation/CFPriv.h>  // for _CFRunLoopSetCurrent()
30#include <IOKit/IOKitLib.h>
31#include <IOKit/IOKitServer.h>
32#include <IOKit/IOCFURLAccess.h>
33#include <IOKit/IOCFUnserialize.h>
34#include <IOKit/storage/RAID/AppleRAIDUserLib.h>
35#include <IOKit/storage/CoreStorage/CoreStorageUserLib.h>
36#include <IOKit/kext/OSKext.h>
37#include <mach/mach.h>
38#include <mach/mach_host.h>
39#include <mach/mach_error.h>
40#include <mach-o/arch.h>
41#include <sys/types.h>
42#include <sys/sysctl.h>
43#include <libc.h>
44#include <servers/bootstrap.h>
45#include <signal.h>
46#include <sysexits.h>
47#include <sys/types.h>
48#include <sys/stat.h>
49#include <sys/sysctl.h>
50#include <sys/param.h>
51#include <sys/mount.h>
52#include <sys/resource.h>
53#include <unistd.h>
54#include <paths.h>
55
56#include <IOKit/kext/OSKext.h>
57#include <IOKit/kext/OSKextPrivate.h>
58#include <IOKit/kext/kextmanager_types.h>
59#include <bootfiles.h>
60
61#include "kextd_main.h"
62
63#include "kext_tools_util.h"
64#include "kextd_globals.h"
65#include "kextd_personalities.h"
66#include "kextd_mig_server.h"
67#include "kextd_request.h"
68#include "kextd_usernotification.h"
69#include "kextd_watchvol.h"
70
71#include "bootcaches.h"
72
73
74/*******************************************************************************
75* Globals set from invocation arguments (xxx - could use fewer globals).
76*******************************************************************************/
77const char * progname = "(unknown)";  // don't free
78
79/*******************************************************************************
80*******************************************************************************/
81struct option sOptInfo[] = {
82    { kOptNameHelp,             no_argument,       NULL, kOptHelp },
83    { kOptNameNoCaches,         no_argument,       NULL, kOptNoCaches },
84    { kOptNameDebug,            no_argument,       NULL, kOptDebug },
85
86    { kOptNameQuiet,            required_argument, NULL, kOptQuiet },
87    { kOptNameVerbose,          optional_argument, NULL, kOptVerbose },
88
89    { NULL, 0, NULL, 0 }  // sentinel to terminate list
90};
91
92/*******************************************************************************
93* Globals created at run time.
94*******************************************************************************/
95KextdArgs                 sToolArgs;
96CFArrayRef                gRepositoryURLs         = NULL;
97static CFArrayRef         sAllKexts               = NULL;
98
99Boolean                   gKernelRequestsPending            = false;
100
101// all the following are released in setUpServer()
102static CFRunLoopTimerRef  sReleaseKextsTimer                = NULL;
103static CFRunLoopSourceRef sClientRequestRunLoopSource       = NULL;
104static CFMachPortRef      sKextdSignalMachPort              = NULL;
105static mach_port_t        sKextSignalMachPortMachPort       = MACH_PORT_NULL;
106static CFRunLoopSourceRef sSignalRunLoopSource              = NULL;
107
108const NXArchInfo        * gKernelArchInfo                   = NULL;  // do not free
109
110ExitStatus                sKextdExitStatus                  = kKextdExitOK;
111
112/*******************************************************************************
113 * Static routines.
114 ******************************************************************************/
115static void NoLoadSigFailureKextCallback(
116                                         CFNotificationCenterRef center,
117                                         void *observer,
118                                         CFStringRef name,
119                                         const void *object,
120                                         CFDictionaryRef userInfo );
121static void InvalidSignedKextCallback(
122                                      CFNotificationCenterRef center,
123                                      void *observer,
124                                      CFStringRef name,
125                                      const void *object,
126                                      CFDictionaryRef userInfo );
127static void RevokedCertKextCallback(
128                                    CFNotificationCenterRef center,
129                                    void *observer,
130                                    CFStringRef name,
131                                    const void *object,
132                                    CFDictionaryRef userInfo );
133#if 0 // not yet
134static void UnsignedKextCallback(
135                                 CFNotificationCenterRef center,
136                                 void *observer,
137                                 CFStringRef name,
138                                 const void *object,
139                                 CFDictionaryRef userInfo );
140#endif
141static void ExcludedKextCallback(
142                                 CFNotificationCenterRef center,
143                                 void *observer,
144                                 CFStringRef name,
145                                 const void *object,
146                                 CFDictionaryRef userInfo );
147static void LoadedKextCallback(
148                               CFNotificationCenterRef center,
149                               void *observer,
150                               CFStringRef name,
151                               const void *object,
152                               CFDictionaryRef userInfo );
153
154/*******************************************************************************
155*******************************************************************************/
156
157int main(int argc, char * const * argv)
158{
159    char       logSpecBuffer[16];  // enough for a 64-bit hex value
160
161   /*****
162    * Find out what my name is.
163    */
164    progname = rindex(argv[0], '/');
165    if (progname) {
166        progname++;   // go past the '/'
167    } else {
168        progname = (char *)argv[0];
169    }
170
171    OSKextSetLogOutputFunction(&tool_log);
172
173   /* Read command line and set a few other args that don't involve
174    * logging. (Syslog is opened up just below; we have to do that after
175    * parsing args, unfortunately.)
176    */
177    sKextdExitStatus = readArgs(argc, argv, &sToolArgs);
178    if (sKextdExitStatus != EX_OK) {
179        goto finish;
180    }
181
182   /*****
183    * If not running in debug mode, then hook up to syslog.
184    */
185    if (!sToolArgs.debugMode) {
186        tool_openlog("com.apple.kextd");
187    }
188
189   /*****
190    * Set an environment variable that children (such as kextcache)
191    * can use to alter behavior (logging to syslog etc.).
192    */
193    setenv("KEXTD_SPAWNED", "", /* overwrite */ 1);
194    snprintf(logSpecBuffer, sizeof(logSpecBuffer), "0x%x",
195        OSKextGetLogFilter(/* kernel? */ true));
196    setenv("KEXT_LOG_FILTER_KERNEL", logSpecBuffer, /* overwrite */ 1);
197    snprintf(logSpecBuffer, sizeof(logSpecBuffer), "0x%x",
198        OSKextGetLogFilter(/* kernel? */ false));
199    setenv("KEXT_LOG_FILTER_USER", logSpecBuffer, /* overwrite */ 1);
200
201    gRepositoryURLs = OSKextGetSystemExtensionsFolderURLs();
202    if (!gRepositoryURLs) {
203        goto finish;
204    }
205
206   /* Get the running kernel arch for OSKext ops & cache creation.
207    */
208    if (!gKernelArchInfo) {
209        gKernelArchInfo = OSKextGetRunningKernelArchitecture();
210        if (!gKernelArchInfo) {
211            goto finish;
212        }
213    }
214
215   /* Check kernel first for safe boot, -x/-safe-boot is only for debugging.
216    */
217    if (OSKextGetActualSafeBoot()) {
218        sToolArgs.safeBootMode = true;
219    } else if (sToolArgs.safeBootMode) {
220        OSKextSetSimulatedSafeBoot(true);
221    }
222
223    OSKextSetUsesCaches(sToolArgs.useRepositoryCaches);
224
225    OSKextSetRecordsDiagnostics(kOSKextDiagnosticsFlagNone);
226    readExtensions();
227
228    sKextdExitStatus = setUpServer(&sToolArgs);
229    if (sKextdExitStatus != EX_OK) {
230        goto finish;
231    }
232
233   /* Tell the IOCatalogue that we are ready to service load requests.
234    */
235    sendActiveToKernel();
236
237   /*****
238    * Send the kext personalities to the kernel to trigger matching.
239    * Now that we have UUID dependency checks, the kernel shouldn't
240    * be able to hurt itself if kexts are out of date somewhere.
241    *
242    * sAllKexts gets cleaned up on the run loop so it's okay to use it here.
243    *
244    * We shouldn't need to reset the IOCatalogue here as it's startup time.
245    */
246    if (kOSReturnSuccess != sendSystemKextPersonalitiesToKernel(sAllKexts, /* reset? */ false)) {
247        sKextdExitStatus = EX_OSERR;
248        goto finish;
249    }
250
251   /* Note: We are not going to try to update the OSBunderHelpers cache
252    * this early as it isn't needed until login. It should normally be
253    * up to date anyhow so let's keep startup I/O to an absolute minimum.
254    */
255
256   /* Let IOCatalogue drop the artificial busy count on the registry
257    * now that it has personalities (which are sure to have naturally
258    * bumped the busy count).
259    */
260    sendFinishedToKernel();
261
262    // Start run loop
263    CFRunLoopRun();
264
265    // Runloop is done - for restart performance exit asap
266    _exit(sKextdExitStatus);
267
268finish:
269#ifndef NO_CFUserNotification
270    stopMonitoringConsoleUser();
271#endif /* ifndef NO_CFUserNotification */
272
273    kextd_stop_volwatch();    // no-op if watch_volumes not called
274
275    if (sKextdExitStatus == kKextdExitHelp) {
276        sKextdExitStatus = kKextdExitOK;
277    }
278
279    exit(sKextdExitStatus);
280
281    return sKextdExitStatus;
282}
283
284
285#pragma mark Major Subroutines
286/*******************************************************************************
287* Major Subroutines
288*******************************************************************************/
289ExitStatus
290readArgs(
291    int            argc,
292    char * const * argv,
293    KextdArgs * toolArgs)
294{
295    ExitStatus   result          = EX_USAGE;
296    ExitStatus   scratchResult   = EX_USAGE;
297    struct stat  stat_buf;
298    int          optchar;
299    int          longindex;
300    CFStringRef  scratchString   = NULL;  // must release
301    CFNumberRef  scratchNumber   = NULL;  // must release
302    CFURLRef     scratchURL      = NULL;  // must release
303
304   /* Kextd is not interested in log messages from the kernel, since
305    * kextd itself prints to the system log and this would duplicate
306    * things coming from the kernel. If you want more kernel kext
307    * logging, use the kextlog boot arg.
308    */
309    OSKextSetLogFilter(kDefaultServiceLogFilter, /* kernel? */ false);
310    OSKextSetLogFilter(kOSKextLogSilentFilter, /* kernel? */ true);
311
312    bzero(toolArgs, sizeof(*toolArgs));
313    toolArgs->debugMode           = false;
314    toolArgs->useRepositoryCaches = true;
315
316    if (stat(kAppleSetupDonePath, &stat_buf) == -1 && errno == ENOENT) {
317        toolArgs->firstBoot = true;
318    } else {
319        toolArgs->firstBoot = false;
320    }
321
322    while ((optchar = getopt_long_only(argc, (char * const *)argv,
323        kOptChars, sOptInfo, &longindex)) != -1) {
324
325        SAFE_RELEASE_NULL(scratchString);
326        SAFE_RELEASE_NULL(scratchNumber);
327        SAFE_RELEASE_NULL(scratchURL);
328
329        switch (optchar) {
330            case kOptHelp:
331                usage(kUsageLevelFull);
332                result = kKextdExitHelp;
333                goto finish;
334                break;
335
336            case kOptNoCaches:
337                toolArgs->useRepositoryCaches = false;
338                break;
339
340            case kOptDebug:
341                toolArgs->debugMode = true;
342                break;
343
344            case kOptQuiet:
345                beQuiet();
346                break;
347
348            case kOptVerbose:
349               /* Set the log flags by the command line, but then turn off
350                * the kernel bridge again.
351                */
352                scratchResult = setLogFilterForOpt(argc, argv, /* forceOnFlags */ 0);
353                if (scratchResult != EX_OK) {
354                    result = scratchResult;
355                    goto finish;
356                }
357                OSKextSetLogFilter(kOSKextLogSilentFilter, /* kernel? */ true);
358                break;
359
360            case kOptSafeBoot:
361                toolArgs->safeBootMode = true;
362                toolArgs->useRepositoryCaches = false;  // -x implies -c
363                break;
364
365            default:
366               /* getopt_long_only() prints an error message for us. */
367                goto finish;
368                break;
369
370        } /* switch (optchar) */
371    } /* while (optchar = getopt_long_only(...) */
372
373    argc -= optind;
374    argv += optind;
375
376    if (argc) {
377        OSKextLog(/* kext */ NULL,
378            kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
379            "Extra input on command line; %s....", argv[0]);
380        goto finish;
381    }
382
383    result = EX_OK;
384
385finish:
386    SAFE_RELEASE(scratchString);
387    SAFE_RELEASE(scratchNumber);
388    SAFE_RELEASE(scratchURL);
389
390    if (result == EX_USAGE) {
391        usage(kUsageLevelBrief);
392    }
393    return result;
394}
395
396/*******************************************************************************
397*******************************************************************************/
398Boolean isNetboot(void)
399{
400    Boolean result = false;
401    int     netboot_mib_name[] = { CTL_KERN, KERN_NETBOOT };
402    int     netboot = 0;
403    size_t  netboot_len = sizeof(netboot);
404
405    if (sysctl(netboot_mib_name, sizeof(netboot_mib_name) / sizeof(int),
406        &netboot, &netboot_len, NULL, 0) != 0) {
407
408        OSKextLog(/* kext */ NULL,
409            kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
410            "Failed to detect netboot - %s.", strerror(errno));
411        goto finish;
412    }
413
414    result = netboot ? true : false;
415
416finish:
417    return result;
418}
419
420/*******************************************************************************
421* Tell the IOCatalogue that kextd is ready to service kernel requests.
422*******************************************************************************/
423void sendActiveToKernel(void)
424{
425    kern_return_t kernelResult;
426    kernelResult = IOCatalogueSendData(kIOMasterPortDefault,
427        kIOCatalogKextdActive, 0, 0);
428    if (kernelResult != KERN_SUCCESS) {
429        OSKextLog(/* kext */ NULL,
430            kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
431            "Failed to notify kernel that kextd is active - %s.",
432            safe_mach_error_string(kernelResult));
433    }
434
435    return;
436}
437
438/*******************************************************************************
439* sendFinishedToKernel()
440*
441* Tell the IOCatalogue that kextd is about to enter its run loop to service
442* requests just generated by sending down all the personalities.
443* The IOCatalogue then drops its artificial busy count on the registry.
444*******************************************************************************/
445void sendFinishedToKernel(void)
446{
447    kern_return_t kernelResult;
448    kernelResult = IOCatalogueSendData(kIOMasterPortDefault,
449        kIOCatalogKextdFinishedLaunching, 0, 0);
450    if (kernelResult != KERN_SUCCESS) {
451        OSKextLog(/* kext */ NULL,
452            kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
453            "Failed to notify kernel that kextd is finished launching - %s.",
454            safe_mach_error_string(kernelResult));
455    }
456
457    return;
458}
459
460/*******************************************************************************
461* setUpServer()
462*******************************************************************************/
463ExitStatus setUpServer(KextdArgs * toolArgs)
464{
465    ExitStatus             result         = EX_OSERR;
466    kern_return_t          kernelResult   = KERN_SUCCESS;
467    unsigned int           sourcePriority = 1;
468    CFMachPortRef          kextdMachPort  = NULL;  // must release
469    mach_port_limits_t     limits;  // queue limit for signal-handler port
470    mach_port_t            servicePort;
471
472   /* Check in with the bootstrap services.
473    */
474    kernelResult = bootstrap_check_in(bootstrap_port,
475            KEXTD_SERVER_NAME, &servicePort);
476
477    if (kernelResult != BOOTSTRAP_SUCCESS) {
478        OSKextLog(/* kext */ NULL,
479            kOSKextLogErrorLevel | kOSKextLogGeneralFlag | kOSKextLogIPCFlag,
480            "Failed server check-in - %s", bootstrap_strerror(kernelResult));
481        exit(EX_OSERR);
482    }
483
484   /* Initialize the run loop so we can start adding sources.
485    */
486    if (!CFRunLoopGetCurrent()) {
487       OSKextLog(/* kext */ NULL,
488           kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
489           "Failed to create run loop.");
490        goto finish;
491    }
492
493   /*****
494    * Add the runloop sources in decreasing priority (increasing "order").
495    * Signals are handled first, followed by kernel requests, and then by
496    * client requests. It's important that each source have a distinct
497    * priority; sharing them causes unpredictable behavior with the runloop.
498    * Note: CFRunLoop.h, however, says 'order' should generally be 0 for all.
499    */
500
501    kextdMachPort = CFMachPortCreateWithPort(kCFAllocatorDefault,
502        servicePort, kextd_mach_port_callback, /* CFMachPortContext */ NULL,
503        /* shouldFreeInfo? */ NULL);
504    sClientRequestRunLoopSource = CFMachPortCreateRunLoopSource(
505        kCFAllocatorDefault, kextdMachPort, sourcePriority++);
506    if (!sClientRequestRunLoopSource) {
507       OSKextLog(/* kext */ NULL,
508           kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
509           "Failed to create client request run loop source.");
510        goto finish;
511    }
512    CFRunLoopAddSource(CFRunLoopGetCurrent(), sClientRequestRunLoopSource,
513        kCFRunLoopDefaultMode);
514
515    // 5519500: kextd_watch_volumes now holds off on updates on its own
516    if (kextd_watch_volumes(sourcePriority++)) {
517        goto finish;
518    }
519
520    sKextdSignalMachPort = CFMachPortCreate(kCFAllocatorDefault,
521        handleSignalInRunloop, NULL, NULL);
522    if (!sKextdSignalMachPort) {
523        OSKextLog(/* kext */ NULL,
524            kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
525            "Failed to create signal-handling Mach port.");
526        goto finish;
527    }
528    sKextSignalMachPortMachPort = CFMachPortGetPort(sKextdSignalMachPort);
529    limits.mpl_qlimit = 1;
530    kernelResult = mach_port_set_attributes(mach_task_self(),
531        sKextSignalMachPortMachPort,
532        MACH_PORT_LIMITS_INFO,
533        (mach_port_info_t)&limits,
534        MACH_PORT_LIMITS_INFO_COUNT);
535    if (kernelResult != KERN_SUCCESS) {
536        OSKextLog(/* kext */ NULL, kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
537            "Failed to set signal-handling port limits.");
538    }
539    sSignalRunLoopSource = CFMachPortCreateRunLoopSource(
540        kCFAllocatorDefault, sKextdSignalMachPort, sourcePriority++);
541    if (!sSignalRunLoopSource) {
542        OSKextLog(/* kext */ NULL, kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
543            "Failed to create signal-handling run loop source.");
544        goto finish;
545    }
546    CFRunLoopAddSource(CFRunLoopGetCurrent(), sSignalRunLoopSource,
547        kCFRunLoopDefaultMode);
548
549   /* Watch for RAID changes so we can forcibly update their boot partitions.
550    */
551    CFNotificationCenterAddObserver(CFNotificationCenterGetLocalCenter(),
552        NULL, // const void *observer
553        updateRAIDSet,
554        CFSTR(kAppleRAIDNotificationSetChanged),
555        NULL, // const void *object
556        CFNotificationSuspensionBehaviorHold);
557    kernelResult = AppleRAIDEnableNotifications();
558    if (kernelResult != KERN_SUCCESS) {
559        OSKextLog(/* kext */ NULL, kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
560            "Failed to register for RAID notifications.");
561    }
562
563  /* Watch for CoreStorage changes so we can update their boot partitions.
564   */
565   CFNotificationCenterAddObserver(CFNotificationCenterGetLocalCenter(),
566       NULL, // const void *observer
567       updateCoreStorageVolume,
568       CFSTR(kCoreStorageNotificationLVGChanged),
569       NULL, // const void *object
570       CFNotificationSuspensionBehaviorHold);
571   CFNotificationCenterAddObserver(CFNotificationCenterGetLocalCenter(),
572       NULL, // const void *observer
573       updateCoreStorageVolume,
574       CFSTR(kCoreStorageNotificationVolumeChanged),
575       NULL, // const void *object
576       CFNotificationSuspensionBehaviorHold);
577   kernelResult = CoreStorageEnableNotifications();
578   if (kernelResult != KERN_SUCCESS) {
579       OSKextLog(/* kext */ NULL, kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
580           "Failed to register for CoreStorage Volume notifications.");
581   }
582
583    /* Sign up to receive notifications when nonsigned kexts are found.  We
584     * currently get messages from kextcache, kextload and kextutil.
585     */
586    CFNotificationCenterAddObserver(CFNotificationCenterGetDistributedCenter(),
587                                    NULL,
588                                    NoLoadSigFailureKextCallback,
589                                    CFSTR("No Load Kext Notification"),
590                                    NULL,
591                                    CFNotificationSuspensionBehaviorDeliverImmediately);
592
593    /* Sign up to receive notifications when invalid signed kexts are found.  We
594     * currently get messages from kextcache, kextload and kextutil.
595     */
596    CFNotificationCenterAddObserver(CFNotificationCenterGetDistributedCenter(),
597                                    NULL,
598                                    InvalidSignedKextCallback,
599                                    CFSTR("Invalid Signature Kext Notification"),
600                                    NULL,
601                                    CFNotificationSuspensionBehaviorDeliverImmediately);
602
603    /* Sign up to receive notifications when kexts are found on the exclude
604     * list.  We currently get messages from kextcache, kextload and kextutil.
605     */
606    CFNotificationCenterAddObserver(CFNotificationCenterGetDistributedCenter(),
607                                    NULL,
608                                    ExcludedKextCallback,
609                                    CFSTR("Excluded Kext Notification"),
610                                    NULL,
611                                    CFNotificationSuspensionBehaviorDeliverImmediately);
612
613    /* Sign up to receive notifications when kexts with revoked certs are
614     * found.  We currently get messages from kextcache, kextload and kextutil.
615     */
616    CFNotificationCenterAddObserver(CFNotificationCenterGetDistributedCenter(),
617                                    NULL,
618                                    RevokedCertKextCallback,
619                                    CFSTR("Revoked Cert Kext Notification"),
620                                    NULL,
621                                    CFNotificationSuspensionBehaviorDeliverImmediately);
622
623#if 0 // not yet
624   /* Sign up to receive notifications when unisgned kexts are found.  We
625     * currently get messages from kextcache, kextload and kextutil.
626     */
627    CFNotificationCenterAddObserver(CFNotificationCenterGetDistributedCenter(),
628                                    NULL,
629                                    UnsignedKextCallback,
630                                    CFSTR("Unsigned Kext Notification"),
631                                    NULL,
632                                    CFNotificationSuspensionBehaviorDeliverImmediately);
633#endif
634
635    /* Sign up to receive notifications when kexts are loaded.
636     * We currently get messages from kextcache, kextload and kextutil.
637     */
638    CFNotificationCenterAddObserver(CFNotificationCenterGetDistributedCenter(),
639                                    NULL,
640                                    LoadedKextCallback,
641                                    CFSTR("Loaded Kext Notification"),
642                                    NULL,
643                                    CFNotificationSuspensionBehaviorDeliverImmediately);
644
645
646#ifndef NO_CFUserNotification
647    result = startMonitoringConsoleUser(toolArgs, &sourcePriority);
648    if (result != EX_OK) {
649        goto finish;
650    }
651#endif /* ifndef NO_CFUserNotification */
652
653    signal(SIGHUP,  handleSignal);
654    signal(SIGTERM, handleSignal);
655    signal(SIGCHLD, handleSignal);
656
657    result = EX_OK;
658
659finish:
660    SAFE_RELEASE(sClientRequestRunLoopSource);
661    SAFE_RELEASE(sKextdSignalMachPort);
662    SAFE_RELEASE(sSignalRunLoopSource);
663    SAFE_RELEASE(kextdMachPort);
664
665    return result;
666}
667
668#include "security.h"
669
670/******************************************************************************
671 * NoLoadSigFailureKextCallback() CFNotificationCenter posts from kextcache,
672 * kextload and kextutil enter here.
673 ******************************************************************************/
674void NoLoadSigFailureKextCallback(CFNotificationCenterRef center,
675                                  void *observer,
676                                  CFStringRef name,
677                                  const void *object,
678                                  CFDictionaryRef userInfo)
679{
680    if (userInfo) {
681        /* synchronize access to our plist file */
682        CFRetain(userInfo);
683        dispatch_async(dispatch_get_main_queue(), ^ {
684            writeKextAlertPlist(userInfo, NO_LOAD_KEXT_ALERT);
685        });
686    }
687
688    return;
689}
690
691/******************************************************************************
692 * RevokedCertKextCallback() CFNotificationCenter posts from kextcache,
693 * kextload and kextutil enter here.
694 ******************************************************************************/
695void RevokedCertKextCallback(CFNotificationCenterRef center,
696                             void *observer,
697                             CFStringRef name,
698                             const void *object,
699                             CFDictionaryRef userInfo)
700{
701    if (userInfo) {
702        /* synchronize access to our plist file */
703        CFRetain(userInfo);
704        dispatch_async(dispatch_get_main_queue(), ^ {
705            sendRevokedCertAlert(userInfo);
706        });
707    }
708
709    return;
710}
711
712#if 0 // not yet
713/******************************************************************************
714 * UnsignedKextCallback() CFNotificationCenter posts from kextcache,
715 * kextload and kextutil enter here.
716 ******************************************************************************/
717void UnsignedKextCallback(CFNotificationCenterRef center,
718                          void *observer,
719                          CFStringRef name,
720                          const void *object,
721                          CFDictionaryRef userInfo)
722{
723    if (userInfo) {
724        /* synchronize access to our plist file */
725        CFRetain(userInfo);
726        dispatch_async(dispatch_get_main_queue(), ^ {
727            writeKextAlertPlist(userInfo, UNSIGNED_KEXT_ALERT);
728        });
729    }
730
731    return;
732}
733#endif
734
735/******************************************************************************
736 * InvalidSignedKextCallback() CFNotificationCenter posts from kextcache,
737 * kextload and kextutil enter here.
738 ******************************************************************************/
739void InvalidSignedKextCallback(CFNotificationCenterRef center,
740                               void *observer,
741                               CFStringRef name,
742                               const void *object,
743                               CFDictionaryRef userInfo)
744{
745    if (userInfo) {
746        /* synchronize access to our plist file */
747        CFRetain(userInfo);
748        dispatch_async(dispatch_get_main_queue(), ^ {
749            writeKextAlertPlist(userInfo, INVALID_SIGNATURE_KEXT_ALERT);
750        });
751    }
752
753    return;
754}
755
756/******************************************************************************
757 * ExcludedKextCallback() CFNotificationCenter posts from kextcache, kextload
758 * and kextutil enter here.
759 ******************************************************************************/
760void ExcludedKextCallback(CFNotificationCenterRef center,
761                          void *observer,
762                          CFStringRef name,
763                          const void *object,
764                          CFDictionaryRef userInfo)
765{
766   if (userInfo) {
767        /* synchronize access to our plist file */
768        CFRetain(userInfo);
769        dispatch_async(dispatch_get_main_queue(), ^ {
770            writeKextAlertPlist(userInfo, EXCLUDED_KEXT_ALERT);
771        });
772    }
773
774    return;
775}
776
777/******************************************************************************
778 * LoadedKextCallback() CFNotificationCenter posts from kextcache, kextload
779 * and kextutil enter here.  Used for message tracing of kext loads.
780 ******************************************************************************/
781void LoadedKextCallback(CFNotificationCenterRef center,
782                          void *observer,
783                          CFStringRef name,
784                          const void *object,
785                          CFDictionaryRef userInfo)
786{
787   if (userInfo) {
788        CFArrayRef myValue;
789        myValue = CFDictionaryGetValue(userInfo, CFSTR("KextArrayKey"));
790
791       if (myValue && CFGetTypeID(myValue) == CFArrayGetTypeID()) {
792           /* synchronize access to our plist file */
793           CFRetain(myValue);
794           dispatch_async(dispatch_get_main_queue(), ^ {
795               writeKextLoadPlist(myValue);
796           });
797       }
798    }
799
800    return;
801}
802
803/******************************************************************************
804* isBootRootActive() checks for the booter hint
805******************************************************************************/
806bool isBootRootActive(void)
807{
808    int          result       = false;
809    io_service_t chosen       = 0;     // must IOObjectRelease()
810    CFTypeRef    bootrootProp = 0;  // must CFRelease()
811
812    chosen = IORegistryEntryFromPath(kIOMasterPortDefault,
813           "IODeviceTree:/chosen");
814    if (!chosen) {
815        goto finish;
816    }
817
818    bootrootProp = IORegistryEntryCreateCFProperty(
819        chosen, CFSTR(kBootRootActiveKey), kCFAllocatorDefault,
820        0 /* options */);
821
822   /* Mere presence of the property indicates that we are
823    * boot!=root, type and value are irrelevant.
824    */
825    if (bootrootProp) {
826        result = true;
827    }
828
829finish:
830    if (chosen)       IOObjectRelease(chosen);
831    SAFE_RELEASE(bootrootProp);
832    return result;
833}
834
835
836/*******************************************************************************
837* On receiving a SIGHUP or SIGTERM, the daemon sends a Mach message to the
838* signal port, causing the run loop handler function rescanExtensions() to be
839* called on the main thread.
840*
841* IMPORTANT: This is a UNIX signal handler, so no allocating or any other unsafe
842* calls. Sending a hand-rolled Mach message off the stack is okay.
843*******************************************************************************/
844void handleSignal(int signum)
845{
846    kextd_mach_msg_signal_t msg;
847
848    msg.signum                  = signum;
849
850    msg.header.msgh_bits        = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, 0);
851    msg.header.msgh_size        = sizeof(msg.header);
852    msg.header.msgh_remote_port = sKextSignalMachPortMachPort;
853    msg.header.msgh_local_port  = MACH_PORT_NULL;
854    msg.header.msgh_id          = 0;
855
856    (void) mach_msg(
857        &msg.header,                          /* msg */
858        MACH_SEND_MSG | MACH_SEND_TIMEOUT,    /* options */
859        sizeof(msg),                          /* send_size */
860        0,                          /* rcv_size */
861        MACH_PORT_NULL,             /* rcv_name */
862        0,                          /* timeout */
863        MACH_PORT_NULL);            /* notify */
864
865    return;
866}
867
868/*******************************************************************************
869*******************************************************************************/
870void handleSignalInRunloop(
871    CFMachPortRef   port,
872    void          * msg,
873    CFIndex         size,
874    void          * info)
875{
876    kextd_mach_msg_signal_t * signal_msg = (kextd_mach_msg_signal_t *)msg;
877    int                       signum = signal_msg->signum;
878
879    if (signum == SIGHUP) {
880        rescanExtensions();
881    } else if (signum == SIGTERM) {
882        CFRunLoopStop(CFRunLoopGetCurrent());
883        sKextdExitStatus = kKextdExitSigterm;
884    } else if (signum == SIGCHLD) {
885        pid_t child_pid    = -1;
886        int   child_status = 0;
887
888       /* Reap all spawned child processes that have exited.
889        */
890        do {
891            child_pid = waitpid(-1 /* any child */, &child_status, WNOHANG);
892            if (child_pid == -1) {
893                if (errno != ECHILD) {
894                    OSKextLog(/* kext */ NULL,
895                        kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
896                        "Error %s waiting on child processes.",
897                        strerror(errno));
898                }
899            } else if (child_pid > 0) {
900                OSKextLogSpec logSpec;
901
902                // EX_SOFTWARE generally means kextcache called
903                // _kextmanager_unlock_volume which logs the error
904                if (WEXITSTATUS(child_status)==0 || child_status==EX_SOFTWARE) {
905                    logSpec = kOSKextLogDetailLevel;
906                } else {
907                    logSpec = kOSKextLogErrorLevel;
908                }
909                OSKextLog(/* kext */ NULL, logSpec,
910                    "async child pid %d exited with status %d",
911                    child_pid, WEXITSTATUS(child_status));
912            }
913        } while (child_pid > 0);
914
915    }
916
917    return;
918}
919
920/*******************************************************************************
921 * This function reads the extensions if necessary.  Note support for multiple
922 * extensions directories.
923 *
924 * This function does not update personalities in the kernel or update any
925 * caches other than those the OSKext library updates in the course of reading
926 * the extensions. We have a notify thread on the extensions folders that does
927 * that, with a slight delay.
928 *******************************************************************************/
929void readExtensions(void)
930{
931    static struct timeval   lastModTime = {0,0};
932    static struct timeval   lastAccessTime = {0,0};
933    struct timeval          tempTimes[2];
934    ExitStatus              result = EX_SOFTWARE;
935
936   /* If getLatestTimesFromCFURLArray fails for any of the extensions
937    * directories we will force reading in all the kexts.  Otherwise we only
938    * read in all the kexts if any of the extensions directories have been
939    * modified.  The first time we're called we will save off the latest mod
940    * time and reread (which is fine since we have not read in any kexts yet).
941    */
942    result = getLatestTimesFromCFURLArray(gRepositoryURLs,
943                                          tempTimes);
944    if (result != EX_OK) {
945        OSKextLog(/* kext */ NULL,
946                  kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
947                  "Failed to stat extensions folders (%s); rereading.",
948                  strerror(errno));
949
950        /* force reading all extensions */
951        releaseExtensions(/* timer */ NULL, /* context */ NULL);
952    }
953
954    if (result == EX_OK && timercmp(&lastModTime, &tempTimes[1], !=)) {
955        lastAccessTime.tv_sec = tempTimes[0].tv_sec;
956        lastAccessTime.tv_usec = tempTimes[0].tv_usec;
957        lastModTime.tv_sec = tempTimes[1].tv_sec;
958        lastModTime.tv_usec = tempTimes[1].tv_usec;
959
960        releaseExtensions(/* timer */ NULL, /* context */ NULL);
961    }
962
963    if (!sAllKexts && gRepositoryURLs) {
964        OSKextLog(/* kext */ NULL,
965            kOSKextLogProgressLevel | kOSKextLogGeneralFlag,
966            "Reading extensions.");
967        sAllKexts = OSKextCreateKextsFromURLs(kCFAllocatorDefault,
968            gRepositoryURLs);
969    }
970    scheduleReleaseExtensions();
971    return;
972}
973
974/*******************************************************************************
975*******************************************************************************/
976void scheduleReleaseExtensions(void)
977{
978    OSKextLog(/* kext */ NULL,
979        kOSKextLogProgressLevel | kOSKextLogGeneralFlag,
980        "%scheduling release of all kexts.", sReleaseKextsTimer ? "Res" : "S");
981
982    if (sReleaseKextsTimer) {
983        CFRunLoopTimerInvalidate(sReleaseKextsTimer);
984        SAFE_RELEASE_NULL(sReleaseKextsTimer);
985    }
986    sReleaseKextsTimer = CFRunLoopTimerCreate(kCFAllocatorDefault,
987        CFAbsoluteTimeGetCurrent() + kReleaseKextsDelay, /* interval */ 0,
988        /* flags */ 0, /* order */ 0, &releaseExtensions, /* context */ NULL);
989    if (!sReleaseKextsTimer) {
990        OSKextLogMemError();
991        goto finish;
992    }
993    CFRunLoopAddTimer(CFRunLoopGetCurrent(), sReleaseKextsTimer,
994        kCFRunLoopDefaultMode);
995
996finish:
997    return;
998}
999
1000/*******************************************************************************
1001*******************************************************************************/
1002void releaseExtensions(
1003    CFRunLoopTimerRef   timer,
1004    void              * context __unused)
1005{
1006    OSKextLog(/* kext */ NULL,
1007        kOSKextLogProgressLevel | kOSKextLogGeneralFlag,
1008        "Releasing all kexts.");
1009
1010    if (timer == sReleaseKextsTimer) {
1011        SAFE_RELEASE_NULL(sReleaseKextsTimer);
1012    }
1013    SAFE_RELEASE_NULL(sAllKexts);
1014
1015    return;
1016}
1017
1018/*******************************************************************************
1019*******************************************************************************/
1020void rescanExtensions(void)
1021{
1022    OSKextLog(/* kext */ NULL,
1023        kOSKextLogBasicLevel | kOSKextLogGeneralFlag,
1024        "Rescanning kernel extensions.");
1025
1026#ifndef NO_CFUserNotification
1027    resetUserNotifications(/* dismissAlert */ false);
1028#endif /* ifndef NO_CFUserNotification */
1029
1030    releaseExtensions(/* timer */ NULL, /* context */ NULL);
1031    readExtensions();
1032
1033    // need to trigger check_rebuild (in watchvol.c) for mkext, etc
1034    // perhaps via mach message to the notification port
1035    // should we let it handle the ResetAllRepos?
1036
1037    // xxx Should we exit if this fails?
1038    sendSystemKextPersonalitiesToKernel(sAllKexts, /* reset? */ TRUE);
1039
1040   /* Update the loginwindow prop/value cache.
1041    * We just read it and don't use the values; this causes
1042    * the caches to be updated if necessary.
1043    */
1044    readSystemKextPropertyValues(CFSTR(kOSBundleHelperKey),
1045        gKernelArchInfo, /* forceUpdate? */ true, /* values */ NULL);
1046
1047    return;
1048}
1049
1050/*******************************************************************************
1051*******************************************************************************/
1052void usage(UsageLevel usageLevel)
1053{
1054    fprintf(stderr,
1055        "usage: %s [-c] [-d] [-f] [-h] [-j] [-r dir] ... [-v [1-6]] [-x]\n",
1056        progname);
1057
1058    if (usageLevel == kUsageLevelBrief) {
1059        goto finish;
1060    }
1061
1062    fprintf(stderr, "\n");
1063
1064    fprintf(stderr, "Arguments and options\n");
1065    fprintf(stderr, "\n");
1066
1067    fprintf(stderr, "-%s (-%c):\n"
1068        "        don't use repository caches; scan repository folders\n",
1069        kOptNameNoCaches, kOptNoCaches);
1070    fprintf(stderr, "-%s (-%c):\n"
1071        "        run in debug mode (log to stderr)\n",
1072        kOptNameDebug, kOptDebug);
1073    fprintf(stderr, "-%s (-%c):\n"
1074        "        run as if the system is in safe boot mode\n",
1075        kOptNameSafeBoot, kOptSafeBoot);
1076    fprintf(stderr, "\n");
1077
1078    fprintf(stderr, "-%s (-%c):\n"
1079        "        quiet mode: log/print no informational or error messages\n",
1080        kOptNameQuiet, kOptQuiet);
1081    fprintf(stderr, "-%s [ 0-6 | 0x<flags> ] (-%c):\n"
1082        "        verbose mode; log/print info about analysis & loading\n",
1083        kOptNameVerbose, kOptVerbose);
1084    fprintf(stderr, "\n");
1085
1086    fprintf(stderr, "-%s (-%c): print this message and exit\n",
1087        kOptNameHelp, kOptHelp);
1088
1089finish:
1090    return;
1091}
1092