#include <CoreFoundation/CoreFoundation.h>
#include <Security/Authorization.h>
#include <IOKit/IOKitLib.h>
#include <IOKit/IOKitServer.h>
#include <libc.h>
#include <mach/mach.h>
#include <mach/mach_host.h>
#include <mach/bootstrap.h>
#include <mach/kmod.h>
#include <notify.h>
#include <sys/proc_info.h>
#include <libproc.h>
#include <libgen.h>
#include <bsm/libbsm.h>
#include <servers/bootstrap.h> // bootstrap mach ports
#include <sandbox.h>
#include <esp.h>
#include <IOKit/kext/kextmanager_types.h>
#include <IOKit/kext/OSKext.h>
#include <IOKit/kext/OSKextPrivate.h>
#include <IOKit/kext/KextManagerPriv.h>
#include <System/libkern/kext_request_keys.h>
#include "kext_tools_util.h"
#include <bootfiles.h>
#include "fork_program.h"
#include "kextd_globals.h"
#include "paths.h"
#include "kextd_request.h"
#include "kextd_main.h"
#include "kextd_usernotification.h"
#include "kextd_mach.h" // mig-generated, not in project
#include "bootcaches.h"
#include "security.h"
#include "pgo.h"
#define setCrashLogMessage(m)
#define CRASH_INFO_KERNEL_KEXT_LOAD "kernel kext load request: id %s"
#define CRASH_INFO_KERNEL_KEXT_RESOURCE "kext resource request: %s from id %s"
#define CRASH_INFO_USER_KEXT_LOAD "user kext load request: %s"
#define CRASH_INFO_USER_KEXT_PATH "user kext path request: %s"
#define CRASH_INFO_USER_PROPERTY "user kext property request: %s"
kern_return_t sendPropertyValueResponse(
CFArrayRef propertyValues,
char ** xml_data_out,
int * xml_data_length);
void kextdProcessKernelLoadRequest(
CFDictionaryRef request);
void kextdProcessKernelResourceRequest(
CFDictionaryRef request);
kern_return_t kextdProcessUserLoadRequest(
CFDictionaryRef request,
uid_t remote_euid,
pid_t remote_pid);
static OSReturn checkNonrootLoadAllowed(
OSKextRef kext,
uid_t remote_euid,
pid_t remote_pid);
#pragma mark KextManager RPC routines & support
kern_return_t _kextmanager_path_for_bundle_id(
mach_port_t server,
kext_bundle_id_t bundle_id,
posix_path_t path, OSReturn * kext_result)
{
kern_return_t result = kOSReturnSuccess;
CFStringRef kextID = NULL; OSKextRef theKext = NULL; CFURLRef kextURL = NULL; CFURLRef absURL = NULL; char crashInfo[sizeof(CRASH_INFO_USER_KEXT_PATH) +
KMOD_MAX_NAME + PATH_MAX];
snprintf(crashInfo, sizeof(crashInfo), CRASH_INFO_USER_KEXT_PATH,
bundle_id);
setCrashLogMessage(crashInfo);
*kext_result = kOSReturnError;
path[0] = '\0';
OSKextLog( NULL,
kOSKextLogDebugLevel | kOSKextLogIPCFlag,
"Received client request for path to bundle %s.",
bundle_id);
kextID = CFStringCreateWithCString(kCFAllocatorDefault, bundle_id,
kCFStringEncodingUTF8);
if (!kextID) {
OSKextLogMemError();
*kext_result = kOSKextReturnNoMemory;
goto finish;
}
theKext = OSKextCreateWithIdentifier(kCFAllocatorDefault, kextID);
if (!theKext) {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogIPCFlag,
"Kext %s not found for client path request.", bundle_id);
*kext_result = kOSKextReturnNotFound;
goto finish;
}
kextURL = OSKextGetURL(theKext);
if (!kextURL) {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogIPCFlag,
"Kext %s found for client path request, but has no URL.", bundle_id);
goto finish;
}
absURL = CFURLCopyAbsoluteURL(kextURL);
if (!absURL) {
OSKextLogMemError();
*kext_result = kOSKextReturnNoMemory;
goto finish;
}
if (!CFURLGetFileSystemRepresentation(absURL, true,
(UInt8 *)path, PATH_MAX)) {
*kext_result = kOSKextReturnSerialization;
goto finish;
}
*kext_result = kOSReturnSuccess;
OSKextLog( NULL,
kOSKextLogDebugLevel | kOSKextLogIPCFlag,
"Returning path %s for identifier %s.", path, bundle_id);
finish:
SAFE_RELEASE(kextID);
SAFE_RELEASE(theKext);
SAFE_RELEASE(absURL);
setCrashLogMessage(NULL);
return result;
}
#pragma mark Loginwindow RPC routines & support
kern_return_t _kextmanager_create_property_value_array(
mach_port_t server,
char * property_key,
char ** xml_data_out,
int * xml_data_length)
{
kern_return_t result = kOSReturnError;
CFStringRef propertyKey = NULL; CFArrayRef propertyValues = NULL; char crashInfo[sizeof(CRASH_INFO_USER_PROPERTY) +
KMOD_MAX_NAME + PATH_MAX];
OSKextLog( NULL,
kOSKextLogProgressLevel | kOSKextLogGeneralFlag,
"Received client request for property value array.");
if (!xml_data_out || !xml_data_length) {
result = kOSKextReturnInvalidArgument;
goto finish;
}
*xml_data_length = 0;
*xml_data_out = NULL;
snprintf(crashInfo, sizeof(crashInfo), CRASH_INFO_USER_PROPERTY,
property_key);
setCrashLogMessage(crashInfo);
propertyKey = CFStringCreateWithCString(kCFAllocatorDefault, property_key,
kCFStringEncodingUTF8);
if (!propertyKey) {
OSKextLogMemError();
goto finish;
}
if (readSystemKextPropertyValues(propertyKey, gKernelArchInfo,
FALSE, &propertyValues)) {
result = sendPropertyValueResponse(propertyValues,
xml_data_out, xml_data_length);
goto finish;
}
finish:
SAFE_RELEASE(propertyKey);
SAFE_RELEASE(propertyValues);
setCrashLogMessage(NULL);
OSKextFlushInfoDictionary(NULL );
OSKextFlushLoadInfo(NULL , true);
return result;
}
kern_return_t sendPropertyValueResponse(
CFArrayRef propertyValues,
char ** xml_data_out,
int * xml_data_length)
{
kern_return_t result = kOSReturnError;
CFDataRef plistData = NULL; CFErrorRef error = NULL;
plistData = CFPropertyListCreateData(kCFAllocatorDefault,
propertyValues, kCFPropertyListBinaryFormat_v1_0,
0,
&error);
if (!plistData) {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
"Can't create plist data for property value response.");
log_CFError( NULL,
kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
error);
goto finish;
}
*xml_data_length = (int)CFDataGetLength(plistData);
if (*xml_data_length) {
result = vm_allocate(mach_task_self(), (vm_address_t *)xml_data_out,
*xml_data_length, VM_FLAGS_ANYWHERE);
if (result != kOSReturnSuccess) {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
"vm_allocate() failed.");
goto finish;
}
memcpy(*xml_data_out, CFDataGetBytePtr(plistData), *xml_data_length);
}
finish:
SAFE_RELEASE(plistData);
SAFE_RELEASE(error);
return result;
}
#pragma mark Kernel Kext Requests
#define KEXTD_LOCKED() (_gKextutilLock ? true:false)
kern_return_t svc_kextd_ping(mach_port_t mp __unused)
{
bool shutdownRequested = false;
if (KEXTD_LOCKED()) {
gKernelRequestsPending = true;
return kOSReturnSuccess;
} else {
shutdownRequested = kextd_process_kernel_requests();
if (shutdownRequested) {
CFRunLoopStop(CFRunLoopGetCurrent());
}
}
return kOSReturnSuccess;
}
bool kextd_process_kernel_requests(void)
{
CFArrayRef kernelRequests = NULL; Boolean loadNotificationReceived = false;
Boolean unloadNotificationReceived = false;
Boolean prelinkedKernelRequested = false;
Boolean shutdownRequested = false;
char * scratchCString = NULL; CFIndex count, i;
while (1) {
SAFE_RELEASE_NULL(kernelRequests);
kernelRequests = _OSKextCopyKernelRequests();
if (!kernelRequests || !CFArrayGetCount(kernelRequests)) {
break;
}
count = CFArrayGetCount(kernelRequests);
for (i = 0; i < count; i++) {
CFDictionaryRef request = NULL; CFStringRef predicate = NULL;
SAFE_FREE_NULL(scratchCString);
request = CFArrayGetValueAtIndex(kernelRequests, i);
predicate = request ? CFDictionaryGetValue(request,
CFSTR(kKextRequestPredicateKey)) : NULL;
if (!request) {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogIPCFlag,
"Empty kernel request.");
continue;
}
if (!predicate) {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogIPCFlag,
"No predicate in kernel request.");
continue;
}
if (CFEqual(predicate, CFSTR(kKextRequestPredicateRequestPrelink))) {
OSKextLog( NULL,
kOSKextLogProgressLevel | kOSKextLogIPCFlag,
"Got prelink kernel request.");
prelinkedKernelRequested = true;
} else if (CFEqual(predicate, CFSTR(kKextRequestPredicateRequestKextdExit))) {
OSKextLog( NULL,
kOSKextLogProgressLevel | kOSKextLogIPCFlag,
"Got exit request from kernel.");
shutdownRequested = true;
} else if (CFEqual(predicate, CFSTR(kKextRequestPredicateRequestLoad))) {
OSKextLog( NULL,
kOSKextLogProgressLevel | kOSKextLogIPCFlag,
"Got load request from kernel.");
kextdProcessKernelLoadRequest(request);
} else if (CFEqual(predicate, CFSTR(kKextRequestPredicateLoadNotification))) {
loadNotificationReceived = true;
} else if (CFEqual(predicate, CFSTR(kKextRequestPredicateUnloadNotification))) {
unloadNotificationReceived = true;
} else if (CFEqual(predicate, CFSTR(kKextRequestPredicateRequestResource))) {
OSKextLog( NULL,
kOSKextLogProgressLevel | kOSKextLogIPCFlag,
"Got resource file request from kernel.");
kextdProcessKernelResourceRequest(request);
} else {
scratchCString = createUTF8CStringForCFString(predicate);
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogIPCFlag,
"Unknown predicate%s%s in kernel request.",
scratchCString ? " " : "",
scratchCString ? scratchCString : "");
}
}
}
if (prelinkedKernelRequested) {
Boolean skipRebuild = FALSE;
struct statfs statfsBuffer;
if (statfs("/System/Library/Caches", &statfsBuffer) == 0) {
if (statfsBuffer.f_flags & MNT_RDONLY) {
skipRebuild = TRUE;
OSKextLog( NULL,
kOSKextLogProgressLevel | kOSKextLogFileAccessFlag,
"Skipping prelinked kernel build; read-only filesystem.");
}
}
if (isPrelinkedKernelAutoRebuildDisabled()) {
OSKextLog( NULL,
kOSKextLogGeneralFlag | kOSKextLogBasicLevel,
"Skipping prelinked kernel rebuild request; kext-dev-mode setting.");
skipRebuild = TRUE;
}
if (!skipRebuild) {
char * const kextcacheArgs[] = {
"/usr/sbin/kextcache",
"-F",
"-system-prelinked-kernel",
NULL };
OSKextLog( NULL,
kOSKextLogProgressLevel | kOSKextLogGeneralFlag,
"Building prelinked kernel.");
(void)fork_program("/usr/sbin/kextcache",
kextcacheArgs,
false);
}
}
if (loadNotificationReceived) {
notify_post(kOSKextLoadNotification);
}
if (unloadNotificationReceived) {
notify_post(kOSKextUnloadNotification);
}
gKernelRequestsPending = false;
SAFE_FREE(scratchCString);
SAFE_RELEASE(kernelRequests);
OSKextFlushInfoDictionary(NULL );
OSKextFlushLoadInfo(NULL , true);
return shutdownRequested;
}
void
kextdProcessKernelLoadRequest(CFDictionaryRef request)
{
CFDictionaryRef requestArgs = NULL; OSKextRef osKext = NULL; OSReturn osLoadResult = kOSKextReturnNotFound;
CFArrayRef loadList = NULL; CFStringRef kextIdentifier = NULL; char * kext_id = NULL; char crashInfo[sizeof(CRASH_INFO_KERNEL_KEXT_LOAD) + KMOD_MAX_NAME + PATH_MAX];
requestArgs = request ? CFDictionaryGetValue(request,
CFSTR(kKextRequestArgumentsKey)) : NULL;
kextIdentifier = requestArgs ? CFDictionaryGetValue(requestArgs,
CFSTR(kKextRequestArgumentBundleIdentifierKey)) : NULL;
if (!requestArgs) {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogLoadFlag | kOSKextLogIPCFlag,
"No arguments in kernel kext load request.");
goto finish;
}
if (!kextIdentifier) {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogLoadFlag | kOSKextLogIPCFlag,
"No kext ID in kernel kext load request.");
goto finish;
}
kext_id = createUTF8CStringForCFString(kextIdentifier);
if (!kext_id) {
OSKextLogMemError();
goto finish;
}
snprintf(crashInfo, sizeof(crashInfo), CRASH_INFO_KERNEL_KEXT_LOAD,
kext_id);
setCrashLogMessage(crashInfo);
readExtensions();
OSKextLog( NULL,
kOSKextLogProgressLevel | kOSKextLogLoadFlag | kOSKextLogIPCFlag,
"Kernel requests kext with id %s.", kext_id);
osKext = OSKextGetKextWithIdentifier(kextIdentifier);
if (!osKext) {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogLoadFlag | kOSKextLogIPCFlag,
"Kext id %s not found; removing personalities from kernel.", kext_id);
OSKextRemovePersonalitiesForIdentifierFromKernel(kextIdentifier);
goto finish;
}
OSStatus sigResult = checkKextSignature(osKext, true, false);
if ( sigResult != 0 ) {
if ( isInvalidSignatureAllowed() ) {
CFStringRef myKextPath = NULL;
myKextPath = copyKextPath(osKext);
OSKextLogCFString(NULL,
kOSKextLogErrorLevel | kOSKextLogLoadFlag,
CFSTR("kext-dev-mode allowing invalid signature %ld 0x%02lX for kext \"%@\""),
(long)sigResult, (long)sigResult,
myKextPath ? myKextPath : CFSTR("Unknown"));
SAFE_RELEASE(myKextPath);
}
else {
CFStringRef myBundleID = NULL;
myBundleID = OSKextGetIdentifier(osKext);
OSKextLogCFString(NULL,
kOSKextLogErrorLevel |
kOSKextLogLoadFlag | kOSKextLogIPCFlag,
CFSTR("ERROR: invalid signature for %@, will not load"),
myBundleID ? myBundleID : CFSTR("Unknown"));
OSKextRemoveKextPersonalitiesFromKernel(osKext);
goto finish;
}
}
if (checkSignaturesOfDependents(osKext, true, false) != 0) {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogLoadFlag |
kOSKextLogDependenciesFlag | kOSKextLogIPCFlag,
"Signature failure in dependencies for kext load request.");
OSKextRemoveKextPersonalitiesFromKernel(osKext);
goto finish;
}
CFBooleanRef pgoref = (CFBooleanRef)
OSKextGetValueForInfoDictionaryKey(osKext, CFSTR("PGO"));
bool pgo = false;
if (pgoref &&
CFGetTypeID(pgoref) == CFBooleanGetTypeID())
{
pgo = CFBooleanGetValue(pgoref);
}
#if HAVE_DANGERZONE
dzRecordKextLoadKernel(osKext);
if ( ! dzAllowKextLoad(osKext) ) {
OSKextRemoveKextPersonalitiesFromKernel(osKext);
goto finish;
}
#endif // HAVE_DANGERZONE
osLoadResult = OSKextLoadWithOptions(osKext,
kOSKextExcludeNone,
kOSKextExcludeAll,
NULL,
pgo);
if (osLoadResult != kOSReturnSuccess) {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogLoadFlag | kOSKextLogIPCFlag,
"Load %s failed; removing personalities from kernel.", kext_id);
OSKextRemoveKextPersonalitiesFromKernel(osKext);
} else {
if (pgo) {
pgo_start_thread(osKext);
}
if (kOSReturnSuccess != IOCatalogueModuleLoaded(
kIOMasterPortDefault, kext_id)) {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogLoadFlag | kOSKextLogIPCFlag,
"Failed to notify IOCatalogue that %s loaded.",
kext_id);
} else {
OSKextLog( NULL,
kOSKextLogProgressLevel | kOSKextLogLoadFlag | kOSKextLogIPCFlag,
"Loaded %s and notified IOCatalogue.",
kext_id);
}
}
if (osLoadResult == kOSKextReturnAuthentication) {
loadList = OSKextCopyLoadList(osKext, false);
recordNonsecureKexts(loadList);
}
finish:
SAFE_RELEASE(loadList);
SAFE_FREE(kext_id);
setCrashLogMessage(NULL);
return;
}
#define kDSStoreFilename ".DS_Store"
void
kextdProcessKernelResourceRequest(
CFDictionaryRef request)
{
CFDictionaryRef requestArgs = NULL; OSKextRef osKext = NULL; CFDataRef resource = NULL; OSReturn requestResult = kOSKextReturnInvalidArgument;
CFStringRef kextIdentifier = NULL; CFStringRef resourceName = NULL; CFURLRef kextURL = NULL; char * kextIdentifierCString = NULL; char * resourceNameCString = NULL; char kextPathCString[PATH_MAX];
char crashInfo[sizeof(CRASH_INFO_KERNEL_KEXT_RESOURCE) +
KMOD_MAX_NAME + PATH_MAX];
requestArgs = request ? CFDictionaryGetValue(request,
CFSTR(kKextRequestArgumentsKey)) : NULL;
kextIdentifier = requestArgs ? CFDictionaryGetValue(requestArgs,
CFSTR(kKextRequestArgumentBundleIdentifierKey)) : NULL;
resourceName = requestArgs ? CFDictionaryGetValue(requestArgs,
CFSTR(kKextRequestArgumentNameKey)) : NULL;
OSKextLog( NULL,
kOSKextLogDebugLevel | kOSKextLogIPCFlag,
"Request for resource.");
if (!requestArgs) {
OSKextLog( NULL,
kOSKextLogProgressLevel | kOSKextLogIPCFlag,
"No arguments in kernel kext resource request.");
goto finish;
}
if (!kextIdentifier) {
OSKextLog( NULL,
kOSKextLogProgressLevel | kOSKextLogIPCFlag,
"No kext ID in kernel kext resource request.");
goto finish;
}
if (!resourceName) {
OSKextLog( NULL,
kOSKextLogProgressLevel | kOSKextLogIPCFlag,
"No resource name in kernel kext resource request.");
goto finish;
}
requestResult = kOSKextReturnNoMemory;
kextIdentifierCString = createUTF8CStringForCFString(kextIdentifier);
resourceNameCString = createUTF8CStringForCFString(resourceName);
if (!kextIdentifierCString || !resourceNameCString) {
OSKextLogMemError();
goto finish;
}
snprintf(crashInfo, sizeof(crashInfo), CRASH_INFO_KERNEL_KEXT_RESOURCE,
resourceNameCString, kextIdentifierCString);
setCrashLogMessage(crashInfo);
if (CFEqual(resourceName, CFSTR(kDSStoreFilename))) {
requestResult = kOSKextReturnInvalidArgument;
OSKextLog( NULL,
kOSKextLogProgressLevel | kOSKextLogIPCFlag | kOSKextLogFileAccessFlag,
"Request for %s resource by %s - not allowed.",
kDSStoreFilename, kextIdentifierCString);
goto finish;
}
readExtensions();
OSKextLog( NULL,
kOSKextLogProgressLevel | kOSKextLogIPCFlag,
"Kernel requests resource %s from kext id %s.",
resourceNameCString, kextIdentifierCString);
requestResult = kOSKextReturnNotFound;
osKext = OSKextCreateWithIdentifier(kCFAllocatorDefault, kextIdentifier);
if (!osKext) {
OSKextLog( NULL,
kOSKextLogProgressLevel | kOSKextLogIPCFlag,
"Kext id %s not found; can't retrieve requested resource.",
kextIdentifierCString);
goto finish;
}
kextURL = OSKextGetURL(osKext);
if (!kextURL ||
!CFURLGetFileSystemRepresentation(kextURL, TRUE,
(UInt8 *)kextPathCString, sizeof(kextPathCString))) {
strlcpy(kextPathCString, "(unknown)", sizeof("(unknown)"));
}
OSKextLog( NULL,
kOSKextLogProgressLevel | kOSKextLogIPCFlag,
"Seeking resource %s in %s.",
resourceNameCString, kextPathCString);
if (!OSKextIsValid(osKext)) {
OSKextLog( NULL,
kOSKextLogProgressLevel | kOSKextLogIPCFlag | kOSKextLogValidationFlag,
"%s is not valid; can't retrieve requested resource.",
kextPathCString);
requestResult = kOSKextReturnValidation;
goto finish;
}
if (!OSKextIsAuthentic(osKext)) {
OSKextLog( NULL,
kOSKextLogProgressLevel | kOSKextLogIPCFlag | kOSKextLogAuthenticationFlag,
"%s has incorrect permissions; can't retrieve requested resource.",
kextPathCString);
requestResult = kOSKextReturnAuthentication;
goto finish;
}
resource = OSKextCopyResource(osKext, resourceName,
NULL);
if (!resource) {
OSKextLog( NULL,
kOSKextLogProgressLevel | kOSKextLogIPCFlag | kOSKextLogFileAccessFlag,
"Can't find resource %s in %s.",
resourceNameCString, kextPathCString);
requestResult = kOSKextReturnNotFound;
goto finish;
}
requestResult = kOSReturnSuccess;
OSKextLog( NULL,
kOSKextLogProgressLevel | kOSKextLogIPCFlag | kOSKextLogValidationFlag,
"Found resource %s in %s; sending to kernel.",
resourceNameCString, kextPathCString);
finish:
(void) _OSKextSendResource(request, requestResult, resource);
SAFE_RELEASE(resource);
SAFE_RELEASE(osKext);
SAFE_FREE(kextIdentifierCString);
SAFE_FREE(resourceNameCString);
setCrashLogMessage(NULL);
return;
}
#pragma mark User Space Kext Load Requests
kern_return_t
_kextmanager_load_kext(
mach_port_t server,
audit_token_t audit_token,
char * xml_data_in,
int xml_data_length)
{
OSReturn result = kOSReturnError;
CFDataRef requestData = NULL; CFDictionaryRef request = NULL; CFErrorRef error = NULL; pid_t remote_pid = -1;
uid_t remote_euid = -1;
requestData = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault,
(const UInt8 *)xml_data_in, xml_data_length,
kCFAllocatorNull);
if (!requestData) {
OSKextLogMemError();
result = kOSKextReturnNoMemory;
goto finish;
}
request = CFPropertyListCreateWithData(kCFAllocatorDefault,
requestData, 0, NULL,
&error);
if (!request) {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogIPCFlag,
"Can't read kext load request.");
log_CFError( NULL,
kOSKextLogErrorLevel | kOSKextLogIPCFlag,
error);
result = kOSKextReturnSerialization;
goto finish;
}
if (CFGetTypeID(request) != CFDictionaryGetTypeID()) {
result = kOSKextReturnBadData;
goto finish;
}
audit_token_to_au32(audit_token, NULL,
&remote_euid, NULL, NULL, NULL,
&remote_pid, NULL, NULL);
result = kextdProcessUserLoadRequest(request, remote_euid, remote_pid);
finish:
SAFE_RELEASE(requestData);
SAFE_RELEASE(request);
SAFE_RELEASE(error);
OSKextFlushInfoDictionary(NULL );
OSKextFlushLoadInfo(NULL , true);
if (result == kOSReturnSuccess) {
vm_deallocate(mach_task_self(), (vm_address_t)xml_data_in,
(vm_size_t)xml_data_length);
}
return result;
}
const char * nameForPID(pid_t pid)
{
char * result = NULL;
int path_length = 0;
char path[PROC_PIDPATHINFO_MAXSIZE];
path_length = proc_pidpath(pid, path,
sizeof(path));
if (path_length > 0) {
result = basename(path);
}
if (!result) {
result = "(unknown)";
}
return result;
}
#define UNKNOWN_KEXT "unknown kext"
#define SYSTEM_FOLDER "/System/"
#define _kSystemExtensionsDirSlash (kSystemExtensionsDir "/")
#define _kLibraryExtensionsDirSlash (kLibraryExtensionsDir "/")
#define _kSystemFilesystemsDirSlash ("/System/Library/Filesystems/")
static CFURLRef createAbsOrRealURLForURL(
CFURLRef anURL,
uid_t remote_euid,
pid_t remote_pid,
OSReturn * error)
{
CFURLRef result = NULL;
OSReturn localError = kOSReturnSuccess;
Boolean inLE = FALSE;
Boolean inSLE = FALSE;
Boolean inSLF = FALSE;
char urlPathCString[PATH_MAX];
char realpathCString[PATH_MAX];
if (!CFURLGetFileSystemRepresentation(anURL, TRUE,
(UInt8 *)urlPathCString, sizeof(urlPathCString)))
{
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogLoadFlag | kOSKextLogIPCFlag,
"Can't get path from URL for kext load request.");
localError = kOSKextReturnSerialization;
goto finish;
}
if (remote_euid == 0) {
result = CFURLCopyAbsoluteURL(anURL);
if (!result) {
OSKextLogMemError();
goto finish;
}
goto finish;
} else {
inSLE = (0 == strncmp(urlPathCString, _kSystemExtensionsDirSlash,
strlen(_kSystemExtensionsDirSlash)));
inLE = (0 == strncmp(urlPathCString, _kLibraryExtensionsDirSlash,
strlen(_kLibraryExtensionsDirSlash)));
inSLF = (0 == strncmp(urlPathCString, _kSystemFilesystemsDirSlash,
strlen(_kSystemFilesystemsDirSlash)));
if (!inSLE && !inSLF && !inLE) {
localError = kOSKextReturnNotPrivileged;
if (!inSLE && !inSLF) {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogLoadFlag | kOSKextLogIPCFlag,
"Request from non-root process '%s' (euid %d) to load %s - "
"not in extensions dirs or filesystems folder.",
nameForPID(remote_pid), remote_euid, urlPathCString);
}
goto finish;
}
if (!realpath(urlPathCString, realpathCString)) {
localError = kOSReturnError; OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogLoadFlag | kOSKextLogIPCFlag,
"Unable to resolve raw path %s.", urlPathCString);
goto finish;
}
inSLE = (0 == strncmp(realpathCString, _kSystemExtensionsDirSlash,
strlen(_kSystemExtensionsDirSlash)));
inLE = (0 == strncmp(realpathCString, _kLibraryExtensionsDirSlash,
strlen(_kLibraryExtensionsDirSlash)));
inSLF = (0 == strncmp(realpathCString, _kSystemFilesystemsDirSlash,
strlen(_kSystemFilesystemsDirSlash)));
if (!inSLE && !inSLF && !inLE) {
localError = kOSKextReturnNotPrivileged;
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogLoadFlag | kOSKextLogIPCFlag,
"Request from non-root process '%s' (euid %d) to load %s - "
"(real path %s) - not in extensions dirs or filesystems folder.",
nameForPID(remote_pid), remote_euid, urlPathCString,
realpathCString);
goto finish;
}
}
result = CFURLCreateFromFileSystemRepresentation(kCFAllocatorDefault,
(UInt8 *)realpathCString, strlen(realpathCString), TRUE);
if (!result) {
OSKextLogMemError();
goto finish;
}
finish:
if (error) {
*error = localError;
}
return result;
}
static OSReturn
checkNonrootLoadAllowed(
OSKextRef kext,
uid_t remote_euid,
pid_t remote_pid)
{
OSReturn result = kOSKextReturnNotPrivileged;
CFArrayRef loadList = NULL; CFStringRef kextPath = NULL; Boolean kextAllows = TRUE;
char kextPathCString[PATH_MAX];
CFIndex count, index;
loadList = OSKextCopyLoadList(kext, TRUE);
if (!loadList) {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogLoadFlag |
kOSKextLogDependenciesFlag | kOSKextLogIPCFlag,
"Can't resolve dependencies for kext load request.");
result = kOSKextReturnDependencies;
goto finish;
}
count = CFArrayGetCount(loadList);
for (index = count - 1; index >= 0; index--) {
OSKextRef thisKext = (OSKextRef)CFArrayGetValueAtIndex(loadList, index);
CFBooleanRef allowed = (CFBooleanRef)OSKextGetValueForInfoDictionaryKey(
thisKext, CFSTR(kOSBundleAllowUserLoadKey));
CFURLRef kextURL = OSKextGetURL(thisKext);
SAFE_RELEASE_NULL(kextPath);
kextPath = CFURLCopyFileSystemPath(kextURL, kCFURLPOSIXPathStyle);
if (!kextPath) {
OSKextLogMemError();
result = kOSKextReturnNoMemory;
}
if (!CFURLGetFileSystemRepresentation(kextURL, TRUE,
(UInt8 *)kextPathCString, sizeof(kextPathCString))) {
strlcpy(kextPathCString, UNKNOWN_KEXT, sizeof(UNKNOWN_KEXT));
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogLoadFlag | kOSKextLogIPCFlag,
"Can't get path from URL for kext load request.");
result = kOSKextReturnSerialization;
goto finish;
}
if (!allowed ||
(CFGetTypeID(allowed) != CFBooleanGetTypeID()) ||
!CFBooleanGetValue(allowed)) {
kextAllows = FALSE;
goto finish;
}
}
result = kOSReturnSuccess;
finish:
SAFE_RELEASE(loadList);
SAFE_RELEASE(kextPath);
if (!kextAllows) {
result = kOSKextReturnNotPrivileged;
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogLoadFlag | kOSKextLogIPCFlag,
"Request from non-root process '%s' (euid %d) to load %s - not allowed.",
nameForPID(remote_pid), remote_euid, kextPathCString);
}
return result;
}
kern_return_t
kextdProcessUserLoadRequest(
CFDictionaryRef request,
uid_t remote_euid,
pid_t remote_pid)
{
OSReturn result = kOSReturnSuccess;
CFStringRef kextID = NULL; char * kextIDString = NULL; CFStringRef kextPath = NULL; CFArrayRef dependencyPaths = NULL; CFURLRef kextURL = NULL; CFURLRef kextAbsURL = NULL; OSKextRef theKext = NULL; CFArrayRef kexts = NULL; CFMutableArrayRef dependencyURLs = NULL; CFURLRef dependencyURL = NULL; CFURLRef dependencyAbsURL = NULL; CFArrayRef dependencyKexts = NULL; CFArrayRef loadList = NULL;
char kextPathString[PATH_MAX] = "unknown";
char crashInfo[sizeof(CRASH_INFO_USER_KEXT_LOAD) +
KMOD_MAX_NAME + PATH_MAX];
CFIndex count, index;
kextID = (CFStringRef)CFDictionaryGetValue(request, kKextLoadIdentifierKey);
if (kextID) {
if (CFGetTypeID(kextID) != CFStringGetTypeID()) {
result = kOSKextReturnInvalidArgument;
goto finish;
}
kextIDString = createUTF8CStringForCFString(kextID);
if (!kextIDString) {
OSKextLogMemError();
goto finish;
}
} else {
kextPath = (CFStringRef)CFDictionaryGetValue(request, kKextLoadPathKey);
if (!kextPath || CFGetTypeID(kextPath) != CFStringGetTypeID()) {
result = kOSKextReturnInvalidArgument;
goto finish;
}
if (!CFStringHasPrefix(kextPath, CFSTR("/"))) {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogLoadFlag | kOSKextLogIPCFlag,
"Error: Request from '%s' (euid %d) to load kext with relative path.",
nameForPID(remote_pid), remote_euid);
result = kOSKextReturnInvalidArgument;
goto finish;
}
kextURL = CFURLCreateWithFileSystemPath(kCFAllocatorDefault,
kextPath, kCFURLPOSIXPathStyle, true);
if (!kextURL) {
result = kOSKextReturnSerialization; goto finish;
}
result = kOSReturnError;
kextAbsURL = createAbsOrRealURLForURL(kextURL,
remote_euid, remote_pid, &result);
if (!kextAbsURL) {
goto finish;
}
CFURLGetFileSystemRepresentation(kextAbsURL, true,
(UInt8 *)kextPathString,
sizeof(kextPathString));
}
readExtensions();
if (remote_euid != 0) {
OSKextLog( NULL,
kOSKextLogProgressLevel | kOSKextLogLoadFlag | kOSKextLogIPCFlag,
"Request from '%s' (euid %d) to load %s.",
nameForPID(remote_pid), remote_euid,
kextIDString ? kextIDString : kextPathString);
}
dependencyPaths = (CFArrayRef)CFDictionaryGetValue(request,
kKextLoadDependenciesKey);
if (dependencyPaths) {
if (CFGetTypeID(dependencyPaths) != CFArrayGetTypeID()) {
result = kOSKextReturnInvalidArgument;
goto finish;
}
count = CFArrayGetCount(dependencyPaths);
dependencyURLs = CFArrayCreateMutable(kCFAllocatorDefault,
count,
&kCFTypeArrayCallBacks);
if (!dependencyURLs) {
result = kOSKextReturnNoMemory;
goto finish;
}
for (index = 0; index < count; index++) {
CFStringRef thisPath = (CFStringRef)CFArrayGetValueAtIndex(
dependencyPaths, index);
SAFE_RELEASE_NULL(dependencyURL);
SAFE_RELEASE_NULL(dependencyAbsURL);
if (CFGetTypeID(thisPath) != CFStringGetTypeID()) {
result = kOSKextReturnInvalidArgument;
goto finish;
}
if (!CFStringHasPrefix(thisPath, CFSTR("/"))) {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogLoadFlag | kOSKextLogIPCFlag,
"Error: Request to load kext using dependency with relative path.");
result = kOSKextReturnInvalidArgument;
goto finish;
}
dependencyURL = CFURLCreateWithFileSystemPath(kCFAllocatorDefault,
thisPath, kCFURLPOSIXPathStyle, true);
if (!dependencyURL) {
result = kOSKextReturnSerialization; goto finish;
}
result = kOSReturnError;
dependencyAbsURL = createAbsOrRealURLForURL(dependencyURL,
remote_euid, remote_pid, &result);
if (!dependencyAbsURL) {
goto finish;
}
CFArrayAppendValue(dependencyURLs, dependencyAbsURL);
}
dependencyKexts = OSKextCreateKextsFromURLs(kCFAllocatorDefault,
dependencyURLs);
if (!dependencyKexts) {
result = kOSReturnError;
goto finish;
}
}
snprintf(crashInfo, sizeof(crashInfo), CRASH_INFO_USER_KEXT_LOAD,
kextIDString ? kextIDString : kextPathString);
setCrashLogMessage(crashInfo);
if (kextID) {
theKext = OSKextGetKextWithIdentifier(kextID);
if (theKext) {
CFRetain(theKext); }
} else {
theKext = OSKextCreate(kCFAllocatorDefault, kextAbsURL);
if (theKext) {
kexts = OSKextCreateKextsFromURL(kCFAllocatorDefault, kextAbsURL);
kextIDString = createUTF8CStringForCFString(OSKextGetIdentifier(theKext));
if (!kextIDString) {
OSKextLogMemError();
goto finish;
}
}
}
if (!theKext) {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogLoadFlag | kOSKextLogIPCFlag,
"Error: Kext %s - not found/unable to create.",
kextIDString ? kextIDString : kextPathString);
result = kOSKextReturnNotFound;
goto finish;
}
if (remote_euid != 0) {
result = checkNonrootLoadAllowed(theKext, remote_euid, remote_pid);
if (result != kOSReturnSuccess) {
goto finish;
}
}
if (OSKextIsInExcludeList(theKext, false)) {
CFMutableDictionaryRef myAlertInfoDict = NULL; addKextToAlertDict(&myAlertInfoDict, theKext);
if (myAlertInfoDict) {
CFRetain(myAlertInfoDict); dispatch_async(dispatch_get_main_queue(), ^ {
writeKextAlertPlist(myAlertInfoDict, EXCLUDED_KEXT_ALERT);
});
SAFE_RELEASE(myAlertInfoDict);
}
messageTraceExcludedKext(theKext);
OSKextLog(NULL,
kOSKextLogErrorLevel | kOSKextLogArchiveFlag |
kOSKextLogValidationFlag | kOSKextLogGeneralFlag,
"%s is in exclude list; omitting.",
kextIDString ? kextIDString : kextPathString);
result = kOSKextReturnNotLoadable;
goto finish;
}
if (sandbox_check(remote_pid, "system-kext-load",
SANDBOX_FILTER_KEXT_BUNDLE_ID,
kextIDString) != 0 ) {
OSKextLog(NULL,
kOSKextLogErrorLevel | kOSKextLogArchiveFlag |
kOSKextLogValidationFlag | kOSKextLogGeneralFlag,
"%s failed sandbox check; omitting.", kextIDString);
result = kOSKextReturnNotLoadable;
goto finish;
}
OSStatus sigResult = checkKextSignature(theKext, true, false);
if ( sigResult != 0 ) {
if ( isInvalidSignatureAllowed() ) {
CFStringRef myKextPath = NULL; myKextPath = copyKextPath(theKext);
OSKextLogCFString(NULL,
kOSKextLogErrorLevel | kOSKextLogLoadFlag,
CFSTR("kext-dev-mode allowing invalid signature %ld 0x%02lX for kext \"%@\""),
(long)sigResult, (long)sigResult,
myKextPath ? myKextPath : CFSTR("Unknown"));
SAFE_RELEASE(myKextPath);
}
else {
CFStringRef myBundleID;
myBundleID = OSKextGetIdentifier(theKext);
OSKextLogCFString(NULL,
kOSKextLogErrorLevel |
kOSKextLogLoadFlag | kOSKextLogIPCFlag,
CFSTR("ERROR: invalid signature for %@, will not load"),
myBundleID ? myBundleID : CFSTR("Unknown"));
result = kOSKextReturnNotLoadable;
goto finish;
}
}
if (checkSignaturesOfDependents(theKext, true, false) != 0) {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogLoadFlag |
kOSKextLogDependenciesFlag | kOSKextLogIPCFlag,
"Signature failure in dependencies for kext load request.");
result = kOSKextReturnNotLoadable;
goto finish;
}
CFBooleanRef pgoref = (CFBooleanRef)
OSKextGetValueForInfoDictionaryKey(theKext, CFSTR("PGO"));
bool pgo = false;
if (pgoref &&
CFGetTypeID(pgoref) == CFBooleanGetTypeID())
{
pgo = CFBooleanGetValue(pgoref);
}
#if HAVE_DANGERZONE
dzRecordKextLoadUser(theKext);
if ( ! dzAllowKextLoad(theKext) ) {
result = kOSKextReturnNotLoadable;
goto finish;
}
#endif // HAVE_DANGERZONE
result = OSKextLoadWithOptions(theKext,
kOSKextExcludeNone,
kOSKextExcludeNone,
NULL,
pgo);
if (pgo && result == kOSReturnSuccess)
{
pgo_start_thread(theKext);
}
finish:
SAFE_RELEASE(kextURL);
SAFE_RELEASE(kextAbsURL);
SAFE_RELEASE(kexts);
SAFE_RELEASE(theKext);
SAFE_RELEASE(dependencyURLs);
SAFE_RELEASE(dependencyURL);
SAFE_RELEASE(dependencyAbsURL);
SAFE_RELEASE(dependencyKexts);
SAFE_RELEASE(loadList);
SAFE_FREE(kextIDString);
setCrashLogMessage(NULL);
return result;
}