1/*
2 * Copyright (c) 2000-2008 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23#include <CoreFoundation/CoreFoundation.h>
24#include <Security/Authorization.h>
25#include <IOKit/IOKitLib.h>
26#include <IOKit/IOKitServer.h>
27#include <libc.h>
28#include <mach/mach.h>
29#include <mach/mach_host.h>
30#include <mach/bootstrap.h>
31#include <mach/kmod.h>
32#include <notify.h>
33#include <sys/proc_info.h>
34#include <libproc.h>
35#include <libgen.h>
36#include <bsm/libbsm.h>
37#include <servers/bootstrap.h>  // bootstrap mach ports
38#include <sandbox.h>
39#include <esp.h>
40
41#include <IOKit/kext/kextmanager_types.h>
42#include <IOKit/kext/OSKext.h>
43#include <IOKit/kext/OSKextPrivate.h>
44#include <IOKit/kext/KextManagerPriv.h>
45#include <System/libkern/kext_request_keys.h>
46
47#include "kext_tools_util.h"
48#include <bootfiles.h>
49#include "fork_program.h"
50#include "kextd_globals.h"
51#include "paths.h"
52#include "kextd_request.h"
53#include "kextd_main.h"
54#include "kextd_usernotification.h"
55
56#include "kextd_mach.h"  // mig-generated, not in project
57
58#include "bootcaches.h"
59#include "security.h"
60
61
62#define setCrashLogMessage(m)
63
64
65#define CRASH_INFO_KERNEL_KEXT_LOAD      "kernel kext load request: id %s"
66#define CRASH_INFO_KERNEL_KEXT_RESOURCE  "kext resource request: %s from id %s"
67#define CRASH_INFO_USER_KEXT_LOAD        "user kext load request: %s"
68#define CRASH_INFO_USER_KEXT_PATH        "user kext path request: %s"
69#define CRASH_INFO_USER_PROPERTY         "user kext property request: %s"
70
71kern_return_t sendPropertyValueResponse(
72    CFArrayRef    propertyValues,
73    char       ** xml_data_out,
74    int         * xml_data_length);
75void kextdProcessKernelLoadRequest(
76    CFDictionaryRef   request);
77void kextdProcessKernelResourceRequest(
78    CFDictionaryRef   request);
79kern_return_t kextdProcessUserLoadRequest(
80    CFDictionaryRef request,
81    uid_t           remote_euid,
82    pid_t           remote_pid);
83static OSReturn checkNonrootLoadAllowed(
84    OSKextRef kext,
85    uid_t     remote_euid,
86    pid_t     remote_pid);
87
88#pragma mark KextManager RPC routines & support
89/*******************************************************************************
90*******************************************************************************/
91kern_return_t _kextmanager_path_for_bundle_id(
92    mach_port_t       server,
93    kext_bundle_id_t  bundle_id,
94    posix_path_t      path,        // PATH_MAX
95    OSReturn        * kext_result)
96{
97    kern_return_t result    = kOSReturnSuccess;
98    CFStringRef   kextID    = NULL;  // must release
99    OSKextRef     theKext   = NULL;  // must release
100    CFURLRef      kextURL   = NULL;  // do not release
101    CFURLRef      absURL    = NULL;  // must release
102    char          crashInfo[sizeof(CRASH_INFO_USER_KEXT_PATH) +
103                  KMOD_MAX_NAME + PATH_MAX];
104
105    snprintf(crashInfo, sizeof(crashInfo), CRASH_INFO_USER_KEXT_PATH,
106        bundle_id);
107
108    setCrashLogMessage(crashInfo);
109
110    *kext_result = kOSReturnError;
111    path[0] = '\0';
112
113    OSKextLog(/* kext */ NULL,
114        kOSKextLogDebugLevel | kOSKextLogIPCFlag,
115        "Received client request for path to bundle %s.",
116        bundle_id);
117
118    kextID = CFStringCreateWithCString(kCFAllocatorDefault, bundle_id,
119        kCFStringEncodingUTF8);
120    if (!kextID) {
121        OSKextLogMemError();
122        *kext_result = kOSKextReturnNoMemory;
123        goto finish;
124    }
125
126    theKext = OSKextCreateWithIdentifier(kCFAllocatorDefault, kextID);
127    if (!theKext) {
128        OSKextLog(/* kext */ NULL,
129            kOSKextLogErrorLevel | kOSKextLogIPCFlag,
130            "Kext %s not found for client path request.", bundle_id);
131        *kext_result = kOSKextReturnNotFound;
132        goto finish;
133    }
134    kextURL = OSKextGetURL(theKext);
135    if (!kextURL) {
136        OSKextLog(/* kext */ NULL,
137            kOSKextLogErrorLevel | kOSKextLogIPCFlag,
138            "Kext %s found for client path request, but has no URL.", bundle_id);
139        goto finish;
140    }
141    absURL = CFURLCopyAbsoluteURL(kextURL);
142    if (!absURL) {
143        OSKextLogMemError();
144        *kext_result = kOSKextReturnNoMemory;
145        goto finish;
146    }
147    if (!CFURLGetFileSystemRepresentation(absURL, /* resolveToBase */ true,
148        (UInt8 *)path, PATH_MAX)) {
149
150        *kext_result = kOSKextReturnSerialization;
151        goto finish;
152    }
153
154    *kext_result = kOSReturnSuccess;
155
156    OSKextLog(/* kext */ NULL,
157        kOSKextLogDebugLevel | kOSKextLogIPCFlag,
158        "Returning path %s for identifier %s.", path, bundle_id);
159
160finish:
161    SAFE_RELEASE(kextID);
162    SAFE_RELEASE(theKext);
163    SAFE_RELEASE(absURL);
164
165    setCrashLogMessage(NULL);
166
167    return result;
168}
169
170#pragma mark Loginwindow RPC routines & support
171/*******************************************************************************
172* This function is executed in the main thread after its run loop gets
173* kicked by a client request.
174*******************************************************************************/
175kern_return_t _kextmanager_create_property_value_array(
176    mach_port_t  server,
177    char        * property_key,
178    char       ** xml_data_out,
179    int         * xml_data_length)
180
181{
182    kern_return_t result         = kOSReturnError;
183
184    CFStringRef   propertyKey    = NULL;  // must release
185    CFArrayRef    propertyValues = NULL;  // must release
186    char          crashInfo[sizeof(CRASH_INFO_USER_PROPERTY) +
187                  KMOD_MAX_NAME + PATH_MAX];
188
189    OSKextLog(/* kext */ NULL,
190        kOSKextLogProgressLevel | kOSKextLogGeneralFlag,
191        "Received client request for property value array.");
192
193    if (!xml_data_out || !xml_data_length) {
194        result = kOSKextReturnInvalidArgument;
195        goto finish;
196    }
197
198    *xml_data_length = 0;
199    *xml_data_out    = NULL;
200
201    snprintf(crashInfo, sizeof(crashInfo), CRASH_INFO_USER_PROPERTY,
202        property_key);
203
204    setCrashLogMessage(crashInfo);
205
206    propertyKey = CFStringCreateWithCString(kCFAllocatorDefault, property_key,
207        kCFStringEncodingUTF8);
208    if (!propertyKey) {
209        OSKextLogMemError();
210        goto finish;
211    }
212
213    if (readSystemKextPropertyValues(propertyKey, gKernelArchInfo,
214        /* forceUpdate? */ FALSE, &propertyValues)) {
215
216            result = sendPropertyValueResponse(propertyValues,
217                xml_data_out, xml_data_length);
218            goto finish;
219    }
220
221finish:
222    SAFE_RELEASE(propertyKey);
223    SAFE_RELEASE(propertyValues);
224
225    setCrashLogMessage(NULL);
226
227    OSKextFlushInfoDictionary(NULL /* all kexts */);
228    OSKextFlushLoadInfo(NULL /* all kexts */, /* flushDependencies */ true);
229
230    return result;
231}
232
233/*******************************************************************************
234*******************************************************************************/
235kern_return_t sendPropertyValueResponse(
236    CFArrayRef    propertyValues,
237    char       ** xml_data_out,
238    int         * xml_data_length)
239{
240    kern_return_t result    = kOSReturnError;
241    CFDataRef     plistData = NULL;  // must release
242    CFErrorRef    error     = NULL;  // must relase
243
244    plistData = CFPropertyListCreateData(kCFAllocatorDefault,
245         propertyValues, kCFPropertyListBinaryFormat_v1_0,
246         /* options */ 0,
247         &error);
248    if (!plistData) {
249        OSKextLog(/* kext */ NULL,
250            kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
251            "Can't create plist data for property value response.");
252        log_CFError(/* kext */ NULL,
253            kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
254            error);
255        goto finish;
256    }
257
258    *xml_data_length = (int)CFDataGetLength(plistData);
259
260    if (*xml_data_length) {
261        result = vm_allocate(mach_task_self(), (vm_address_t *)xml_data_out,
262            *xml_data_length, VM_FLAGS_ANYWHERE);
263        if (result != kOSReturnSuccess) {
264            OSKextLog(/* kext */ NULL,
265                kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
266                "vm_allocate() failed.");
267            goto finish;
268        }
269        memcpy(*xml_data_out, CFDataGetBytePtr(plistData), *xml_data_length);
270    }
271
272finish:
273    SAFE_RELEASE(plistData);
274    SAFE_RELEASE(error);
275
276    return result;
277}
278
279#pragma mark Kernel Kext Requests
280
281#define KEXTD_LOCKED() (_gKextutilLock ? true:false)
282
283/*******************************************************************************
284* Incoming MIG message from kernel to let us know we should fetch requests from
285* it using kextd_process_kernel_requests().
286*******************************************************************************/
287kern_return_t svc_kextd_ping(mach_port_t mp __unused)
288{
289    bool shutdownRequested = false;
290
291    if (KEXTD_LOCKED()) {
292        gKernelRequestsPending = true;
293        return kOSReturnSuccess;
294    } else {
295        shutdownRequested = kextd_process_kernel_requests();
296        if (shutdownRequested) {
297            CFRunLoopStop(CFRunLoopGetCurrent());
298        }
299    }
300    return kOSReturnSuccess;
301}
302
303/*******************************************************************************
304*******************************************************************************/
305bool kextd_process_kernel_requests(void)
306{
307    CFArrayRef      kernelRequests             = NULL;  // must release
308    Boolean         loadNotificationReceived   = false;
309    Boolean         unloadNotificationReceived = false;
310    Boolean         prelinkedKernelRequested   = false;
311    Boolean         shutdownRequested          = false;
312    char          * scratchCString             = NULL;  // must free
313    CFIndex         count, i;
314
315   /* Stay in the while loop until _OSKextCopyKernelRequests() returns
316    * no more requests.
317    */
318    while (1) {
319        SAFE_RELEASE_NULL(kernelRequests);
320        kernelRequests = _OSKextCopyKernelRequests();
321        if (!kernelRequests || !CFArrayGetCount(kernelRequests)) {
322            break;
323        }
324
325        count = CFArrayGetCount(kernelRequests);
326        for (i = 0; i < count; i++) {
327            CFDictionaryRef request      = NULL; // do not release
328            CFStringRef     predicate    = NULL; // do not release
329
330            SAFE_FREE_NULL(scratchCString);
331
332            request = CFArrayGetValueAtIndex(kernelRequests, i);
333            predicate = request ? CFDictionaryGetValue(request,
334                CFSTR(kKextRequestPredicateKey)) : NULL;
335
336            if (!request) {
337                OSKextLog(/* kext */ NULL,
338                    kOSKextLogErrorLevel | kOSKextLogIPCFlag,
339                    "Empty kernel request.");
340                continue;
341            }
342            if (!predicate) {
343                OSKextLog(/* kext */ NULL,
344                    kOSKextLogErrorLevel | kOSKextLogIPCFlag,
345                    "No predicate in kernel request.");
346                continue;
347            }
348
349           /* Check the request predicate and process it or note it needs
350            * to be processed.
351            */
352            if (CFEqual(predicate, CFSTR(kKextRequestPredicateRequestPrelink))) {
353                OSKextLog(/* kext */ NULL,
354                    kOSKextLogProgressLevel | kOSKextLogIPCFlag,
355                    "Got prelink kernel request.");
356                prelinkedKernelRequested = true;
357            } else if (CFEqual(predicate, CFSTR(kKextRequestPredicateRequestKextdExit))) {
358                OSKextLog(/* kext */ NULL,
359                    kOSKextLogProgressLevel | kOSKextLogIPCFlag,
360                    "Got exit request from kernel.");
361                shutdownRequested = true;
362            } else if (CFEqual(predicate, CFSTR(kKextRequestPredicateRequestLoad))) {
363                OSKextLog(/* kext */ NULL,
364                    kOSKextLogProgressLevel | kOSKextLogIPCFlag,
365                    "Got load request from kernel.");
366                kextdProcessKernelLoadRequest(request);
367            } else if (CFEqual(predicate, CFSTR(kKextRequestPredicateLoadNotification))) {
368               /* We don't do anything with the kext identifier because notify(3)
369                * doesn't allow for an argument.
370                */
371                loadNotificationReceived = true;
372            } else if (CFEqual(predicate, CFSTR(kKextRequestPredicateUnloadNotification))) {
373               /* We don't do anything with the kext identifier because notify(3)
374                * doesn't allow for an argument.
375                */
376                unloadNotificationReceived = true;
377            } else if (CFEqual(predicate, CFSTR(kKextRequestPredicateRequestResource))) {
378                OSKextLog(/* kext */ NULL,
379                    kOSKextLogProgressLevel | kOSKextLogIPCFlag,
380                    "Got resource file request from kernel.");
381                kextdProcessKernelResourceRequest(request);
382            } else {
383                scratchCString = createUTF8CStringForCFString(predicate);
384                OSKextLog(/* kext */ NULL,
385                    kOSKextLogErrorLevel | kOSKextLogIPCFlag,
386                    "Unknown predicate%s%s in kernel request.",
387                    scratchCString ? " " : "",
388                    scratchCString ? scratchCString : "");
389            }
390        } /* for (i = 0; i < count; i++) */
391    } /* while (1) */
392
393// finish:
394
395    if (prelinkedKernelRequested) {
396        Boolean       readOnlyFS = FALSE;
397        struct statfs statfsBuffer;
398
399       /* If the statfs() fails we will forge ahead and try kextcache.
400        * Only if we know for sure it's read-only do we skip.
401        */
402        if (statfs("/System/Library/Caches", &statfsBuffer) == 0) {
403            if (statfsBuffer.f_flags & MNT_RDONLY) {
404                readOnlyFS = TRUE;
405
406                OSKextLog(/* kext */ NULL,
407                    kOSKextLogProgressLevel | kOSKextLogFileAccessFlag,
408                    "Skipping prelinked kernel build; read-only filesystem.");
409            }
410        }
411
412        if (!readOnlyFS) {
413            char * const kextcacheArgs[] = {
414                "/usr/sbin/kextcache",
415                "-F",
416                "-system-prelinked-kernel",
417                NULL };
418
419            OSKextLog(/* kext */ NULL,
420                kOSKextLogProgressLevel | kOSKextLogGeneralFlag,
421                "Building prelinked kernel.");
422
423            (void)fork_program("/usr/sbin/kextcache",
424                kextcacheArgs,
425                /* waitFlag */ false);
426        }
427    }
428
429    if (loadNotificationReceived) {
430        notify_post(kOSKextLoadNotification);
431    }
432    if (unloadNotificationReceived) {
433        notify_post(kOSKextUnloadNotification);
434    }
435
436    gKernelRequestsPending = false;
437
438    SAFE_FREE(scratchCString);
439    SAFE_RELEASE(kernelRequests);
440
441    OSKextFlushInfoDictionary(NULL /* all kexts */);
442    OSKextFlushLoadInfo(NULL /* all kexts */, /* flushDependencies */ true);
443
444    return shutdownRequested;
445}
446
447/*******************************************************************************
448* Kernel load request.
449*******************************************************************************/
450void
451kextdProcessKernelLoadRequest(CFDictionaryRef   request)
452{
453    CFDictionaryRef requestArgs     = NULL; // do not release
454    OSKextRef       osKext          = NULL; // do not release
455    OSReturn        osLoadResult    = kOSKextReturnNotFound;
456
457    CFArrayRef      loadList        = NULL;  // must release
458    CFStringRef     kextIdentifier  = NULL;  // do not release
459    char          * kext_id         = NULL;  // must free
460    char            crashInfo[sizeof(CRASH_INFO_KERNEL_KEXT_LOAD) + KMOD_MAX_NAME + PATH_MAX];
461
462    requestArgs = request ? CFDictionaryGetValue(request,
463        CFSTR(kKextRequestArgumentsKey)) : NULL;
464    kextIdentifier = requestArgs ? CFDictionaryGetValue(requestArgs,
465        CFSTR(kKextRequestArgumentBundleIdentifierKey)) : NULL;
466
467    if (!requestArgs) {
468        OSKextLog(/* kext */ NULL,
469            kOSKextLogErrorLevel | kOSKextLogLoadFlag | kOSKextLogIPCFlag,
470            "No arguments in kernel kext load request.");
471        goto finish;
472    }
473    if (!kextIdentifier) {
474        OSKextLog(/* kext */ NULL,
475            kOSKextLogErrorLevel | kOSKextLogLoadFlag | kOSKextLogIPCFlag,
476            "No kext ID in kernel kext load request.");
477        goto finish;
478    }
479    kext_id = createUTF8CStringForCFString(kextIdentifier);
480    if (!kext_id) {
481        // xxx - not much we can do here.
482        OSKextLogMemError();
483        goto finish;
484    }
485
486    snprintf(crashInfo, sizeof(crashInfo), CRASH_INFO_KERNEL_KEXT_LOAD,
487        kext_id);
488
489    setCrashLogMessage(crashInfo);
490
491   /* Read the extensions if necessary (also resets the release timer).
492    */
493    readExtensions();
494
495    OSKextLog(/* kext */ NULL,
496        kOSKextLogProgressLevel | kOSKextLogLoadFlag | kOSKextLogIPCFlag,
497        "Kernel requests kext with id %s.", kext_id);
498
499    osKext = OSKextGetKextWithIdentifier(kextIdentifier);
500    if (!osKext) {
501        OSKextLog(/* kext */ NULL,
502            kOSKextLogErrorLevel | kOSKextLogLoadFlag | kOSKextLogIPCFlag,
503            "Kext id %s not found; removing personalities from kernel.", kext_id);
504        OSKextRemovePersonalitiesForIdentifierFromKernel(kextIdentifier);
505        goto finish;
506    }
507
508   /* xxx - under what circumstances should we remove personalities?
509    * xxx - if the request gets into the kernel and fails, OSKext.cpp
510    * xxx - removes them, but there can be other failures on the way....
511    */
512    OSStatus  sigResult = checkKextSignature(osKext, true);
513    if ( sigResult != 0 ) {
514        CFMutableDictionaryRef      myAlertInfoDict = NULL; // must release
515        OSReturn                    myResult        = kOSReturnSuccess;
516
517        if ( isInLibraryExtensionsFolder(osKext) ||
518            sigResult == CSSMERR_TP_CERT_REVOKED ) {
519            /* Do not load if kext has invalid signature and comes from
520             *  /Library/Extensions/
521             */
522            CFStringRef     myBundleID;         // do not release
523
524            myBundleID = OSKextGetIdentifier(osKext);
525            myResult = kOSKextReturnNotLoadable; // see 13024670
526            OSKextLogCFString(NULL,
527                              kOSKextLogErrorLevel |
528                              kOSKextLogLoadFlag | kOSKextLogIPCFlag,
529                              CFSTR("ERROR: invalid signature for %@, will not load"),
530                              myBundleID ? myBundleID : CFSTR("Unknown"));
531        }
532
533        /* Put up alert if this is the first time we've seen this
534         * kext and it is not an Apple kext.
535         */
536        addKextToAlertDict(&myAlertInfoDict, osKext);
537        if (myAlertInfoDict) {
538            CFRetain(myAlertInfoDict); // writeKextAlertPlist or sendRevokedCertAlert will release
539            if (sigResult == CSSMERR_TP_CERT_REVOKED) {
540                dispatch_async(dispatch_get_main_queue(), ^ {
541                    sendRevokedCertAlert(myAlertInfoDict);
542                });
543            }
544            else if (myResult == kOSKextReturnNotLoadable) {
545                dispatch_async(dispatch_get_main_queue(), ^ {
546                    writeKextAlertPlist(myAlertInfoDict, NO_LOAD_KEXT_ALERT);
547                });
548            }
549#if 0 // not yet
550            else if (sigResult == errSecCSUnsigned) {
551                dispatch_async(dispatch_get_main_queue(), ^ {
552                    writeKextAlertPlist(myAlertInfoDict, UNSIGNED_KEXT_ALERT);
553                });
554            }
555#endif
556            else {
557                dispatch_async(dispatch_get_main_queue(), ^ {
558                    writeKextAlertPlist(myAlertInfoDict, INVALID_SIGNATURE_KEXT_ALERT);
559                });
560            }
561            SAFE_RELEASE(myAlertInfoDict);
562        }
563
564        if (myResult != kOSReturnSuccess) {
565            OSKextLog(/* kext */ NULL,
566                      kOSKextLogErrorLevel | kOSKextLogLoadFlag |
567                      kOSKextLogIPCFlag,
568                      "Load %s failed; removing personalities from kernel.",
569                      kext_id);
570            OSKextRemoveKextPersonalitiesFromKernel(osKext);
571            goto finish;
572        }
573        CFStringRef     myKextPath = NULL; // must release
574        myKextPath = copyKextPath(osKext);
575        if ( myKextPath ) {
576            OSKextLogCFString(NULL,
577                              kOSKextLogErrorLevel | kOSKextLogLoadFlag,
578                              CFSTR("WARNING - Invalid signature %ld 0x%02lX for kext \"%@\""),
579                              (long)sigResult, (long)sigResult, myKextPath);
580            SAFE_RELEASE(myKextPath);
581        }
582    }
583
584    osLoadResult = OSKextLoad(osKext);
585    if (osLoadResult != kOSReturnSuccess) {
586        OSKextLog(/* kext */ NULL,
587            kOSKextLogErrorLevel | kOSKextLogLoadFlag | kOSKextLogIPCFlag,
588            "Load %s failed; removing personalities from kernel.", kext_id);
589        OSKextRemoveKextPersonalitiesFromKernel(osKext);
590    } else {
591        if (kOSReturnSuccess != IOCatalogueModuleLoaded(
592            kIOMasterPortDefault, kext_id)) {
593
594            OSKextLog(/* kext */ NULL,
595                kOSKextLogErrorLevel | kOSKextLogLoadFlag | kOSKextLogIPCFlag,
596                "Failed to notify IOCatalogue that %s loaded.",
597                kext_id);
598        } else {
599            OSKextLog(/* kext */ NULL,
600                kOSKextLogProgressLevel | kOSKextLogLoadFlag | kOSKextLogIPCFlag,
601                "Loaded %s and notified IOCatalogue.",
602                kext_id);
603        }
604    }
605
606    if (osLoadResult == kOSKextReturnAuthentication) {
607        loadList = OSKextCopyLoadList(osKext, /* needAll? */ false);
608        recordNonsecureKexts(loadList);
609    }
610
611finish:
612    SAFE_RELEASE(loadList);
613    SAFE_FREE(kext_id);
614    setCrashLogMessage(NULL);
615
616    return;
617}
618
619/*******************************************************************************
620* Kernel resource file request.
621*******************************************************************************/
622#define kDSStoreFilename   ".DS_Store"
623
624void
625kextdProcessKernelResourceRequest(
626    CFDictionaryRef   request)
627{
628    CFDictionaryRef requestArgs            = NULL;  // do not release
629    OSKextRef       osKext                 = NULL;  // must release
630    CFDataRef       resource               = NULL;  // must release
631    OSReturn        requestResult          = kOSKextReturnInvalidArgument;
632
633    CFStringRef     kextIdentifier         = NULL;  // do not release
634    CFStringRef     resourceName           = NULL;  // do not release
635    CFURLRef        kextURL                = NULL;  // do not release
636    char          * kextIdentifierCString  = NULL;  // must free
637    char          * resourceNameCString    = NULL;  // must free
638    char            kextPathCString[PATH_MAX];
639    char            crashInfo[sizeof(CRASH_INFO_KERNEL_KEXT_RESOURCE) +
640                    KMOD_MAX_NAME + PATH_MAX];
641
642    requestArgs = request ? CFDictionaryGetValue(request,
643        CFSTR(kKextRequestArgumentsKey)) : NULL;
644    kextIdentifier = requestArgs ? CFDictionaryGetValue(requestArgs,
645        CFSTR(kKextRequestArgumentBundleIdentifierKey)) : NULL;
646    resourceName = requestArgs ? CFDictionaryGetValue(requestArgs,
647        CFSTR(kKextRequestArgumentNameKey)) : NULL;
648
649    OSKextLog(/* kext */ NULL,
650        kOSKextLogDebugLevel | kOSKextLogIPCFlag,
651        "Request for resource.");
652
653    if (!requestArgs) {
654        OSKextLog(/* kext */ NULL,
655            kOSKextLogProgressLevel | kOSKextLogIPCFlag,
656            "No arguments in kernel kext resource request.");
657        goto finish;
658    }
659    if (!kextIdentifier) {
660        OSKextLog(/* kext */ NULL,
661            kOSKextLogProgressLevel | kOSKextLogIPCFlag,
662            "No kext ID in kernel kext resource request.");
663        goto finish;
664    }
665    if (!resourceName) {
666        OSKextLog(/* kext */ NULL,
667            kOSKextLogProgressLevel | kOSKextLogIPCFlag,
668            "No resource name in kernel kext resource request.");
669        goto finish;
670    }
671
672    requestResult = kOSKextReturnNoMemory;
673
674    kextIdentifierCString = createUTF8CStringForCFString(kextIdentifier);
675    resourceNameCString = createUTF8CStringForCFString(resourceName);
676    if (!kextIdentifierCString || !resourceNameCString) {
677        // xxx - not much we can do here.
678        OSKextLogMemError();
679        goto finish;
680    }
681
682    snprintf(crashInfo, sizeof(crashInfo), CRASH_INFO_KERNEL_KEXT_RESOURCE,
683        resourceNameCString, kextIdentifierCString);
684
685    setCrashLogMessage(crashInfo);
686
687    if (CFEqual(resourceName, CFSTR(kDSStoreFilename))) {
688        requestResult = kOSKextReturnInvalidArgument;
689        OSKextLog(/* kext */ NULL,
690            kOSKextLogProgressLevel | kOSKextLogIPCFlag | kOSKextLogFileAccessFlag,
691            "Request for %s resource by %s - not allowed.",
692            kDSStoreFilename, kextIdentifierCString);
693        goto finish;
694    }
695
696   /* Read the extensions if necessary (also resets the release timer).
697    */
698    readExtensions();
699
700    OSKextLog(/* kext */ NULL,
701        kOSKextLogProgressLevel | kOSKextLogIPCFlag,
702        "Kernel requests resource %s from kext id %s.",
703        resourceNameCString, kextIdentifierCString);
704
705    requestResult = kOSKextReturnNotFound;
706
707    osKext = OSKextCreateWithIdentifier(kCFAllocatorDefault, kextIdentifier);
708    if (!osKext) {
709        OSKextLog(/* kext */ NULL,
710            kOSKextLogProgressLevel | kOSKextLogIPCFlag,
711            "Kext id %s not found; can't retrieve requested resource.",
712            kextIdentifierCString);
713        goto finish;
714    }
715
716    kextURL = OSKextGetURL(osKext);
717    if (!kextURL ||
718        !CFURLGetFileSystemRepresentation(kextURL, /* resolveToBase? */ TRUE,
719            (UInt8 *)kextPathCString, sizeof(kextPathCString))) {
720
721            strlcpy(kextPathCString, "(unknown)", sizeof("(unknown)"));
722    }
723
724    OSKextLog(/* kext */ NULL,
725        kOSKextLogProgressLevel | kOSKextLogIPCFlag,
726        "Seeking resource %s in %s.",
727        resourceNameCString, kextPathCString);
728
729    if (!OSKextIsValid(osKext)) {
730        OSKextLog(/* kext */ NULL,
731            kOSKextLogProgressLevel | kOSKextLogIPCFlag | kOSKextLogValidationFlag,
732            "%s is not valid; can't retrieve requested resource.",
733            kextPathCString);
734        requestResult = kOSKextReturnValidation;
735        goto finish;
736    }
737
738
739    if (!OSKextIsAuthentic(osKext)) {
740        OSKextLog(/* kext */ NULL,
741            kOSKextLogProgressLevel | kOSKextLogIPCFlag | kOSKextLogAuthenticationFlag,
742            "%s is not authentic; can't retrieve requested resource.",
743            kextPathCString);
744        requestResult = kOSKextReturnAuthentication;
745        goto finish;
746    }
747
748    resource = OSKextCopyResource(osKext, resourceName,
749        /* resourceType */ NULL);
750    if (!resource) {
751        OSKextLog(/* kext */ NULL,
752            kOSKextLogProgressLevel | kOSKextLogIPCFlag | kOSKextLogFileAccessFlag,
753            "Can't find resource %s in %s.",
754            resourceNameCString, kextPathCString);
755        requestResult = kOSKextReturnNotFound;
756        goto finish;
757    }
758
759    requestResult = kOSReturnSuccess;
760
761    OSKextLog(/* kext */ NULL,
762        kOSKextLogProgressLevel | kOSKextLogIPCFlag | kOSKextLogValidationFlag,
763        "Found resource %s in %s; sending to kernel.",
764        resourceNameCString, kextPathCString);
765
766finish:
767    // now we send it to the kernel
768    (void) _OSKextSendResource(request, requestResult, resource);
769
770    SAFE_RELEASE(resource);
771    SAFE_RELEASE(osKext);
772    SAFE_FREE(kextIdentifierCString);
773    SAFE_FREE(resourceNameCString);
774    setCrashLogMessage(NULL);
775    return;
776}
777
778#pragma mark User Space Kext Load Requests
779/*******************************************************************************
780* User space load request.
781*******************************************************************************/
782kern_return_t
783_kextmanager_load_kext(
784    mach_port_t   server,
785    audit_token_t audit_token,
786    char        * xml_data_in,
787    int           xml_data_length)
788{
789    OSReturn        result      = kOSReturnError;
790    CFDataRef       requestData = NULL;  // must release
791    CFDictionaryRef request     = NULL;  // must release
792    CFErrorRef      error       = NULL;  // must release
793    pid_t           remote_pid  = -1;
794    uid_t           remote_euid = -1;
795
796    requestData = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault,
797        (const UInt8 *)xml_data_in, xml_data_length,
798        /* deallocator */ kCFAllocatorNull);
799    if (!requestData) {
800        OSKextLogMemError();
801        result = kOSKextReturnNoMemory;
802        goto finish;
803    }
804    request = CFPropertyListCreateWithData(kCFAllocatorDefault,
805        requestData, /* options */ 0, /* format */ NULL,
806        &error);
807    if (!request) {
808        OSKextLog(/* kext */ NULL,
809            kOSKextLogErrorLevel | kOSKextLogIPCFlag,
810            "Can't read kext load request.");
811        log_CFError(/* kext */ NULL,
812            kOSKextLogErrorLevel | kOSKextLogIPCFlag,
813            error);
814        result = kOSKextReturnSerialization;
815        goto finish;
816    }
817    if (CFGetTypeID(request) != CFDictionaryGetTypeID()) {
818        result = kOSKextReturnBadData;
819        goto finish;
820    }
821
822    audit_token_to_au32(audit_token, /* audit UID */ NULL,
823            &remote_euid, /* egid */ NULL, /* ruid */ NULL, /* rgid */ NULL,
824            &remote_pid, /* asid */ NULL, /* au_tid_t */ NULL);
825
826    result = kextdProcessUserLoadRequest(request, remote_euid, remote_pid);
827
828finish:
829    SAFE_RELEASE(requestData);
830    SAFE_RELEASE(request);
831    SAFE_RELEASE(error);
832
833    OSKextFlushInfoDictionary(NULL /* all kexts */);
834    OSKextFlushLoadInfo(NULL /* all kexts */, /* flushDependencies */ true);
835
836   /* MIG is consume-on-success
837    * xxx - do we need separate result & op-result?
838    */
839    if (result == kOSReturnSuccess) {
840        vm_deallocate(mach_task_self(), (vm_address_t)xml_data_in,
841            (vm_size_t)xml_data_length);
842    }
843    return result;
844}
845
846/*******************************************************************************
847*******************************************************************************/
848const char * nameForPID(pid_t pid)
849{
850    char * result      = NULL;
851    int    path_length = 0;
852    char   path[PROC_PIDPATHINFO_MAXSIZE];
853
854    path_length = proc_pidpath(pid, path,
855        sizeof(path));
856    if (path_length > 0) {
857        result = basename(path);
858    }
859    if (!result) {
860        result = "(unknown)";
861    }
862    return result;
863}
864
865/*******************************************************************************
866*******************************************************************************/
867#define UNKNOWN_KEXT  "unknown kext"
868#define SYSTEM_FOLDER "/System/"
869
870#define _kSystemExtensionsDirSlash   (kSystemExtensionsDir "/")
871#define _kLibraryExtensionsDirSlash   (kLibraryExtensionsDir "/")
872#define _kSystemFilesystemsDirSlash  ("/System/Library/Filesystems/")
873
874/*******************************************************************************
875*******************************************************************************/
876static CFURLRef createAbsOrRealURLForURL(
877    CFURLRef   anURL,
878    uid_t      remote_euid,
879    pid_t      remote_pid,
880    OSReturn * error)
881{
882    CFURLRef result      = NULL;
883    OSReturn localError  = kOSReturnSuccess;
884    Boolean  inLE        = FALSE;
885    Boolean  inSLE       = FALSE;
886    Boolean  inSLF       = FALSE;
887    char     urlPathCString[PATH_MAX];
888    char     realpathCString[PATH_MAX];
889
890    if (!CFURLGetFileSystemRepresentation(anURL, /* resolveToBase? */ TRUE,
891        (UInt8 *)urlPathCString, sizeof(urlPathCString)))
892    {
893        OSKextLog(/* kext */ NULL,
894            kOSKextLogErrorLevel | kOSKextLogLoadFlag | kOSKextLogIPCFlag,
895            "Can't get path from URL for kext load request.");
896        localError = kOSKextReturnSerialization;
897        goto finish;
898    }
899
900    if (remote_euid == 0) {
901        result = CFURLCopyAbsoluteURL(anURL);
902        if (!result) {
903            OSKextLogMemError();
904            goto finish;
905        }
906        goto finish;
907    } else {
908
909        inSLE = (0 == strncmp(urlPathCString, _kSystemExtensionsDirSlash,
910                              strlen(_kSystemExtensionsDirSlash)));
911        inLE = (0 == strncmp(urlPathCString, _kLibraryExtensionsDirSlash,
912                             strlen(_kLibraryExtensionsDirSlash)));
913        inSLF = (0 == strncmp(urlPathCString, _kSystemFilesystemsDirSlash,
914                              strlen(_kSystemFilesystemsDirSlash)));
915
916       /*****
917        * May want to open these checks to use OSKextGetSystemExtensionsFolderURLs().
918        * For now, keep it tight and just do /System/Library/Extensions & Filesystems.
919        */
920        if (!inSLE && !inSLF && !inLE) {
921            localError = kOSKextReturnNotPrivileged;
922            if (!inSLE && !inSLF) {
923                OSKextLog(/* kext */ NULL,
924                    kOSKextLogErrorLevel | kOSKextLogLoadFlag | kOSKextLogIPCFlag,
925                    "Request from non-root process '%s' (euid %d) to load %s - "
926                          "not in extensions dirs or filesystems folder.",
927                    nameForPID(remote_pid), remote_euid, urlPathCString);
928            }
929            goto finish;
930        }
931
932        if (!realpath(urlPathCString, realpathCString)) {
933
934            localError = kOSReturnError; // xxx - should we have a filesystem error?
935            OSKextLog(/* kext */ NULL,
936                kOSKextLogErrorLevel | kOSKextLogLoadFlag | kOSKextLogIPCFlag,
937                "Unable to resolve raw path %s.", urlPathCString);
938            goto finish;
939        }
940
941       /*****
942        * Check the path once more now that we've resolved it with realpath().
943        */
944        inSLE = (0 == strncmp(realpathCString, _kSystemExtensionsDirSlash,
945                              strlen(_kSystemExtensionsDirSlash)));
946        inLE = (0 == strncmp(urlPathCString, _kLibraryExtensionsDirSlash,
947                             strlen(_kLibraryExtensionsDirSlash)));
948        inSLF = (0 == strncmp(realpathCString, _kSystemFilesystemsDirSlash,
949                              strlen(_kSystemFilesystemsDirSlash)));
950
951        if (!inSLE && !inSLF && !inLE) {
952
953            localError = kOSKextReturnNotPrivileged;
954            OSKextLog(/* kext */ NULL,
955                kOSKextLogErrorLevel | kOSKextLogLoadFlag | kOSKextLogIPCFlag,
956                "Request from non-root process '%s' (euid %d) to load %s - "
957                "(real path %s) - not in extensions dirs or filesystems folder.",
958                nameForPID(remote_pid), remote_euid, urlPathCString,
959                realpathCString);
960            goto finish;
961        }
962    }
963
964    result = CFURLCreateFromFileSystemRepresentation(kCFAllocatorDefault,
965        (UInt8 *)realpathCString, strlen(realpathCString), /* isDir */ TRUE);
966    if (!result) {
967        OSKextLogMemError();
968        goto finish;
969    }
970
971finish:
972    if (error) {
973        *error = localError;
974    }
975    return result;
976}
977
978/*******************************************************************************
979*******************************************************************************/
980static OSReturn
981checkNonrootLoadAllowed(
982    OSKextRef kext,
983    uid_t     remote_euid,
984    pid_t     remote_pid)
985{
986    OSReturn    result       = kOSKextReturnNotPrivileged;
987    CFArrayRef  loadList     = NULL;  // must release
988    CFStringRef kextPath     = NULL;  // must release
989    Boolean     kextAllows   = TRUE;
990    char        kextPathCString[PATH_MAX];
991    CFIndex     count, index;
992
993    loadList = OSKextCopyLoadList(kext, /* needAll?*/ TRUE);
994    if (!loadList) {
995        OSKextLog(/* kext */ NULL,
996            kOSKextLogErrorLevel | kOSKextLogLoadFlag |
997            kOSKextLogDependenciesFlag | kOSKextLogIPCFlag,
998            "Can't resolve dependencies for kext load request.");
999        result = kOSKextReturnDependencies;
1000        goto finish;
1001    }
1002
1003    count = CFArrayGetCount(loadList);
1004    for (index = count - 1; index >= 0; index--) {
1005        OSKextRef thisKext = (OSKextRef)CFArrayGetValueAtIndex(loadList, index);
1006        CFBooleanRef allowed = (CFBooleanRef)OSKextGetValueForInfoDictionaryKey(
1007            thisKext, CFSTR(kOSBundleAllowUserLoadKey));
1008        CFURLRef  kextURL = OSKextGetURL(thisKext);
1009
1010        SAFE_RELEASE_NULL(kextPath);
1011
1012        kextPath = CFURLCopyFileSystemPath(kextURL, kCFURLPOSIXPathStyle);
1013        if (!kextPath) {
1014            OSKextLogMemError();
1015            result = kOSKextReturnNoMemory;
1016        }
1017        if (!CFURLGetFileSystemRepresentation(kextURL, /* resolveToBase? */ TRUE,
1018            (UInt8 *)kextPathCString, sizeof(kextPathCString))) {
1019            strlcpy(kextPathCString, UNKNOWN_KEXT, sizeof(UNKNOWN_KEXT));
1020            OSKextLog(/* kext */ NULL,
1021                kOSKextLogErrorLevel | kOSKextLogLoadFlag | kOSKextLogIPCFlag,
1022                "Can't get path from URL for kext load request.");
1023            result = kOSKextReturnSerialization;
1024            goto finish;
1025        }
1026
1027        if (!allowed ||
1028            (CFGetTypeID(allowed) != CFBooleanGetTypeID()) ||
1029            !CFBooleanGetValue(allowed)) {
1030
1031            kextAllows = FALSE;
1032            goto finish;
1033        }
1034    }
1035
1036    result = kOSReturnSuccess;
1037
1038finish:
1039    SAFE_RELEASE(loadList);
1040    SAFE_RELEASE(kextPath);
1041
1042    if (!kextAllows) {
1043        result = kOSKextReturnNotPrivileged;
1044
1045        OSKextLog(/* kext */ NULL,
1046            kOSKextLogErrorLevel | kOSKextLogLoadFlag | kOSKextLogIPCFlag,
1047            "Request from non-root process '%s' (euid %d) to load %s - not allowed.",
1048            nameForPID(remote_pid), remote_euid, kextPathCString);
1049    }
1050
1051    return result;
1052}
1053
1054/*******************************************************************************
1055*******************************************************************************/
1056kern_return_t
1057kextdProcessUserLoadRequest(
1058    CFDictionaryRef request,
1059    uid_t           remote_euid,
1060    pid_t           remote_pid)
1061{
1062    OSReturn          result                   = kOSReturnSuccess;
1063    CFStringRef       kextID                   = NULL;  // do not release
1064    char *            kextIDString             = NULL;  // must free
1065    CFStringRef       kextPath                 = NULL;  // do not release
1066    CFArrayRef        dependencyPaths          = NULL;  // do not release
1067    CFURLRef          kextURL                  = NULL;  // must release
1068    CFURLRef          kextAbsURL               = NULL;  // must release
1069    OSKextRef         theKext                  = NULL;  // must release
1070    CFArrayRef        kexts                    = NULL;  // must release
1071    CFMutableArrayRef dependencyURLs           = NULL;  // must release
1072    CFURLRef          dependencyURL            = NULL;  // must release
1073    CFURLRef          dependencyAbsURL         = NULL;  // must release
1074    CFArrayRef        dependencyKexts          = NULL;  // must release
1075    CFArrayRef        loadList                 = NULL;  // must release
1076
1077    char              kextPathString[PATH_MAX] = "unknown";
1078    char              crashInfo[sizeof(CRASH_INFO_USER_KEXT_LOAD) +
1079                      KMOD_MAX_NAME + PATH_MAX];
1080    CFIndex           count, index;
1081
1082   /* First get the identifier or URL to load, and convert it to a C string
1083    * for logging.
1084    */
1085    kextID = (CFStringRef)CFDictionaryGetValue(request, kKextLoadIdentifierKey);
1086    if (kextID) {
1087        if (CFGetTypeID(kextID) != CFStringGetTypeID()) {
1088            result = kOSKextReturnInvalidArgument;
1089            goto finish;
1090        }
1091        kextIDString = createUTF8CStringForCFString(kextID);
1092        if (!kextIDString) {
1093            OSKextLogMemError();
1094            goto finish;
1095        }
1096    } else {
1097        kextPath = (CFStringRef)CFDictionaryGetValue(request, kKextLoadPathKey);
1098        if (!kextPath || CFGetTypeID(kextPath) != CFStringGetTypeID()) {
1099            result = kOSKextReturnInvalidArgument;
1100            goto finish;
1101        }
1102
1103        if (!CFStringHasPrefix(kextPath, CFSTR("/"))) {
1104            OSKextLog(/* kext */ NULL,
1105                kOSKextLogErrorLevel | kOSKextLogLoadFlag | kOSKextLogIPCFlag,
1106                "Error: Request from '%s' (euid %d) to load kext with relative path.",
1107                nameForPID(remote_pid), remote_euid);
1108            result = kOSKextReturnInvalidArgument;
1109            goto finish;
1110        }
1111
1112        kextURL = CFURLCreateWithFileSystemPath(kCFAllocatorDefault,
1113            kextPath, kCFURLPOSIXPathStyle, /* isDir? */ true);
1114        if (!kextURL) {
1115            result = kOSKextReturnSerialization;  // xxx - or other?
1116            goto finish;
1117        }
1118        result = kOSReturnError;
1119        kextAbsURL = createAbsOrRealURLForURL(kextURL,
1120            remote_euid, remote_pid, &result);
1121        if (!kextAbsURL) {
1122            goto finish;
1123        }
1124        CFURLGetFileSystemRepresentation(kextAbsURL, /* resolveToBase */ true,
1125                                         (UInt8 *)kextPathString,
1126                                         sizeof(kextPathString));
1127    }
1128
1129   /* Read the extensions if necessary (also resets the release timer).
1130    */
1131    readExtensions();
1132
1133   /* Now log before the attempt, then try to look up or create the kext.
1134    */
1135    if (remote_euid != 0) {
1136        OSKextLog(/* kext */ NULL,
1137            kOSKextLogProgressLevel | kOSKextLogLoadFlag | kOSKextLogIPCFlag,
1138                "Request from '%s' (euid %d) to load %s.",
1139                  nameForPID(remote_pid), remote_euid,
1140                  kextIDString ? kextIDString : kextPathString);
1141    }
1142
1143   /* Open any dependencies provided, *before* we create the kext, since
1144    * a request by identifier must be resolvable from the dependencies
1145    * as well as system extensions folders.
1146    */
1147    dependencyPaths = (CFArrayRef)CFDictionaryGetValue(request,
1148        kKextLoadDependenciesKey);
1149    if (dependencyPaths) {
1150        if (CFGetTypeID(dependencyPaths) != CFArrayGetTypeID()) {
1151            result = kOSKextReturnInvalidArgument;
1152            goto finish;
1153        }
1154
1155        count = CFArrayGetCount(dependencyPaths);
1156
1157        dependencyURLs = CFArrayCreateMutable(kCFAllocatorDefault,
1158            /* capacity */ count,
1159            &kCFTypeArrayCallBacks);
1160        if (!dependencyURLs) {
1161            result = kOSKextReturnNoMemory;
1162            goto finish;
1163        }
1164
1165        for (index = 0; index < count; index++) {
1166            CFStringRef thisPath = (CFStringRef)CFArrayGetValueAtIndex(
1167                dependencyPaths, index);
1168
1169            SAFE_RELEASE_NULL(dependencyURL);
1170            SAFE_RELEASE_NULL(dependencyAbsURL);
1171            if (CFGetTypeID(thisPath) != CFStringGetTypeID()) {
1172                result = kOSKextReturnInvalidArgument;
1173                goto finish;
1174            }
1175            if (!CFStringHasPrefix(thisPath, CFSTR("/"))) {
1176                OSKextLog(/* kext */ NULL,
1177                    kOSKextLogErrorLevel | kOSKextLogLoadFlag | kOSKextLogIPCFlag,
1178                    "Error: Request to load kext using dependency with relative path.");
1179                result = kOSKextReturnInvalidArgument;
1180                goto finish;
1181            }
1182
1183            dependencyURL = CFURLCreateWithFileSystemPath(kCFAllocatorDefault,
1184                thisPath, kCFURLPOSIXPathStyle, /* isDir? */ true);
1185            if (!dependencyURL) {
1186                result = kOSKextReturnSerialization;  // xxx - or other?
1187                goto finish;
1188            }
1189            result = kOSReturnError;
1190            dependencyAbsURL = createAbsOrRealURLForURL(dependencyURL,
1191                remote_euid, remote_pid, &result);
1192            if (!dependencyAbsURL) {
1193                goto finish;
1194            }
1195            CFArrayAppendValue(dependencyURLs, dependencyAbsURL);
1196        }
1197        dependencyKexts = OSKextCreateKextsFromURLs(kCFAllocatorDefault,
1198            dependencyURLs);
1199        if (!dependencyKexts) {
1200            result = kOSReturnError;
1201            goto finish;
1202        }
1203    }
1204
1205    snprintf(crashInfo, sizeof(crashInfo), CRASH_INFO_USER_KEXT_LOAD,
1206            kextIDString ? kextIDString : kextPathString);
1207
1208    setCrashLogMessage(crashInfo);
1209
1210
1211    if (kextID) {
1212        theKext = OSKextGetKextWithIdentifier(kextID);
1213        if (theKext) {
1214            CFRetain(theKext);  // we're going to release it
1215        }
1216    } else {
1217       /* Make sure we also read the plugins of the kext we're asked to load,
1218        * but only if we manage to open the kext itself (or we'll get too many
1219        * error messages).
1220        */
1221        theKext = OSKextCreate(kCFAllocatorDefault, kextAbsURL);
1222        if (theKext) {
1223            kexts = OSKextCreateKextsFromURL(kCFAllocatorDefault, kextAbsURL);
1224            kextIDString = createUTF8CStringForCFString(OSKextGetIdentifier(theKext));
1225            if (!kextIDString) {
1226                OSKextLogMemError();
1227                goto finish;
1228            }
1229        }
1230    }
1231
1232    if (!theKext) {
1233        OSKextLog(/* kext */ NULL,
1234            kOSKextLogErrorLevel | kOSKextLogLoadFlag | kOSKextLogIPCFlag,
1235            "Error: Kext %s - not found/unable to create.",
1236            kextIDString ? kextIDString : kextPathString);
1237        result = kOSKextReturnNotFound;
1238        goto finish;
1239    }
1240
1241    if (remote_euid != 0) {
1242        result = checkNonrootLoadAllowed(theKext, remote_euid, remote_pid);
1243        if (result != kOSReturnSuccess) {
1244            goto finish;
1245        }
1246    }
1247
1248    /* Get dictionary of all our excluded kexts */
1249    if (OSKextIsInExcludeList(theKext, false)) {
1250        CFMutableDictionaryRef myAlertInfoDict = NULL; // must release
1251        addKextToAlertDict(&myAlertInfoDict, theKext);
1252        if (myAlertInfoDict) {
1253            CFRetain(myAlertInfoDict); // writeKextAlertPlist will release
1254            dispatch_async(dispatch_get_main_queue(), ^ {
1255                writeKextAlertPlist(myAlertInfoDict, EXCLUDED_KEXT_ALERT);
1256            });
1257            SAFE_RELEASE(myAlertInfoDict);
1258        }
1259
1260        messageTraceExcludedKext(theKext);
1261        OSKextLog(NULL,
1262                  kOSKextLogErrorLevel | kOSKextLogArchiveFlag |
1263                  kOSKextLogValidationFlag | kOSKextLogGeneralFlag,
1264                  "%s is in exclude list; omitting.",
1265                  kextIDString ? kextIDString : kextPathString);
1266        result = kOSKextReturnNotLoadable;
1267        goto finish;
1268    }
1269
1270    if (__esp_enabled()) {
1271        xpc_object_t dict = xpc_dictionary_create(NULL, NULL, 0);
1272        if (dict == NULL) {
1273            result = kOSKextReturnNotLoadable;
1274            goto finish;
1275        }
1276        xpc_dictionary_set_int64(dict, "euid", remote_euid);
1277        xpc_dictionary_set_int64(dict, "pid", remote_pid);
1278        if (kextIDString != NULL) {
1279            xpc_dictionary_set_string(dict, "kextID", kextIDString);
1280        }
1281        if (kextPathString != NULL) {
1282            xpc_dictionary_set_string(dict, "kextPath", kextPathString);
1283        }
1284        int esp_result = __esp_check("kext-load", dict);
1285        xpc_release(dict);
1286        if (esp_result != 0) {
1287            result = kOSKextReturnNotLoadable;
1288            goto finish;
1289        }
1290    }
1291
1292    /* consult sandboxing system to make sure this is OK
1293     * <rdar://problem/11015459
1294     */
1295    if (sandbox_check(remote_pid, "system-kext-load",
1296                      SANDBOX_FILTER_KEXT_BUNDLE_ID,
1297                      kextIDString) != 0 )  {
1298        OSKextLog(NULL,
1299                  kOSKextLogErrorLevel | kOSKextLogArchiveFlag |
1300                  kOSKextLogValidationFlag | kOSKextLogGeneralFlag,
1301                  "%s failed sandbox check; omitting.", kextIDString);
1302        result = kOSKextReturnNotLoadable;
1303        goto finish;
1304    }
1305
1306    OSStatus  sigResult = checkKextSignature(theKext, true);
1307    if ( sigResult != 0 ) {
1308        if ( isInLibraryExtensionsFolder(theKext) ||
1309            sigResult == CSSMERR_TP_CERT_REVOKED ) {
1310            /* Do not load if kext has invalid signature and comes from
1311             *  /Library/Extensions/
1312             */
1313            CFStringRef         myBundleID;          // do not release
1314
1315            myBundleID = OSKextGetIdentifier(theKext);
1316            result = kOSKextReturnNotLoadable; // see 13024670
1317            OSKextLogCFString(NULL,
1318                              kOSKextLogErrorLevel |
1319                              kOSKextLogLoadFlag | kOSKextLogIPCFlag,
1320                              CFSTR("ERROR: invalid signature for %@, will not load"),
1321                              myBundleID ? myBundleID : CFSTR("Unknown"));
1322        }
1323
1324        /* Put up alert if this is the first time we've seen this
1325         * kext and it is not an Apple kext
1326         */
1327        CFMutableDictionaryRef myAlertInfoDict = NULL; // must release
1328
1329        addKextToAlertDict(&myAlertInfoDict, theKext);
1330        if (myAlertInfoDict) {
1331            CFRetain(myAlertInfoDict); // writeKextAlertPlist or sendRevokedCertAlert will release
1332            if (sigResult == CSSMERR_TP_CERT_REVOKED) {
1333                dispatch_async(dispatch_get_main_queue(), ^ {
1334                    sendRevokedCertAlert(myAlertInfoDict);
1335                });
1336            }
1337            else if (result == kOSKextReturnNotLoadable) {
1338                dispatch_async(dispatch_get_main_queue(), ^ {
1339                    writeKextAlertPlist(myAlertInfoDict, NO_LOAD_KEXT_ALERT);
1340                });
1341            }
1342#if 0 // not yet
1343            else if (sigResult == errSecCSUnsigned) {
1344                dispatch_async(dispatch_get_main_queue(), ^ {
1345                    writeKextAlertPlist(myAlertInfoDict, UNSIGNED_KEXT_ALERT);
1346                });
1347            }
1348#endif
1349            else {
1350                dispatch_async(dispatch_get_main_queue(), ^ {
1351                    writeKextAlertPlist(myAlertInfoDict, INVALID_SIGNATURE_KEXT_ALERT);
1352                });
1353            }
1354            SAFE_RELEASE(myAlertInfoDict);
1355        } // myAlertInfoDict
1356    }
1357    if (result != kOSReturnSuccess) {
1358        goto finish;
1359    }
1360    if (sigResult != 0) {
1361        CFStringRef     myKextPath = NULL; // must release
1362        myKextPath = copyKextPath(theKext);
1363        if ( myKextPath ) {
1364            OSKextLogCFString(NULL,
1365                              kOSKextLogErrorLevel | kOSKextLogLoadFlag,
1366                              CFSTR("WARNING - Invalid signature %ld 0x%02lX for kext \"%@\""),
1367                              (long)sigResult, (long)sigResult, myKextPath);
1368            SAFE_RELEASE(myKextPath);
1369        }
1370    }
1371
1372    /* <rdar://problem/12435992>
1373     */
1374    recordKextLoadForMT(theKext);
1375
1376   /* The codepath from this function will do any error logging
1377    * and cleanup needed.
1378    */
1379    result = OSKextLoadWithOptions(theKext,
1380        /* statExclusion */ kOSKextExcludeNone,
1381        /* addPersonalitiesExclusion */ kOSKextExcludeNone,
1382        /* personalityNames */ NULL,
1383        /* delayAutounloadFlag */ false);
1384
1385    if (result == kOSKextReturnAuthentication) {
1386        loadList = OSKextCopyLoadList(theKext, /* needAll? */ false);
1387        recordNonsecureKexts(loadList);
1388    }
1389
1390finish:
1391    SAFE_RELEASE(kextURL);
1392    SAFE_RELEASE(kextAbsURL);
1393    SAFE_RELEASE(kexts);
1394    SAFE_RELEASE(theKext);
1395    SAFE_RELEASE(dependencyURLs);
1396    SAFE_RELEASE(dependencyURL);
1397    SAFE_RELEASE(dependencyAbsURL);
1398    SAFE_RELEASE(dependencyKexts);
1399    SAFE_RELEASE(loadList);
1400    SAFE_FREE(kextIDString);
1401
1402    setCrashLogMessage(NULL);
1403
1404    return result;
1405}
1406