#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 "globals.h"
#include <IOKit/kext/KXKextManager.h>
#include <IOKit/kext/kextmanager_types.h>
#include "paths.h"
#include "request.h"
#include "logging.h"
#include "queue.h"
#include "PTLock.h"
uid_t logged_in_uid = -1;
AuthorizationRef gAuthRef = NULL;
#ifndef NO_CFUserNotification
CFMutableArrayRef gPendedNonsecureKexts = NULL; CFMutableArrayRef gPendedKextloadOperations = NULL; CFMutableArrayRef gScheduledNonsecureKexts = NULL;
pid_t gFork_pid = -1;
CFUserNotificationRef gSecurityNotification = NULL; CFUserNotificationRef gFailureNotification = NULL; KXKextRef gSecurityAlertKext = NULL; Boolean gResendSecurityAlertKextPersonalities = false;
CFOptionFlags gSecurityAlertResponse = 0;
CFOptionFlags gFailureAlertResponse = 0;
PTLockRef gUserAuthorizationLock = NULL; Boolean gWaitingForAuthorization = false;
Boolean gAuthorizationReady = false;
OSStatus auth_result = errAuthorizationSuccess;
#endif
extern char ** environ;
static KXKextManagerError __kextd_load_kext(KXKextRef theKext,
const char * kmod_name);
#ifndef NO_CFUserNotification
static void _kextd_poll_alert_response(void);
static void _kextd_raise_security_alert(KXKextRef aKext);
static void _kextd_raise_failure_alert(KXKextRef aKext);
void _kextd_launch_authorization(void);
void * _kextd_authorize_user(void * arg);
#endif
extern char * CFURLCopyCString(CFURLRef anURL);
extern const char * _KXKextCopyCanonicalPathnameAsCString(KXKextRef aKext);
extern KXKextManagerError _KXKextMakeSecure(KXKextRef aKext);
extern KXKextManagerError _KXKextRaiseSecurityAlert(KXKextRef aKext, uid_t euid);
extern KXKextManagerError _KXKextManagerPrepareKextForLoading(
KXKextManagerRef aKextManager,
KXKextRef aKext,
const char * kext_name,
Boolean check_loaded_for_dependencies,
Boolean do_load,
CFMutableArrayRef inauthenticKexts);
extern KXKextManagerError _KXKextManagerLoadKextUsingOptions(
KXKextManagerRef aKextManager,
KXKextRef aKext,
const char * kext_name,
const char * kernel_file,
const char * patch_dir,
const char * symbol_dir,
Boolean do_load,
Boolean do_start_kext,
int interactive_level,
Boolean ask_overwrite_symbols,
Boolean overwrite_symbols,
Boolean get_addrs_from_kernel,
unsigned int num_addresses,
char ** addresses);
Boolean kextd_launch_kernel_request_thread(void)
{
Boolean result = true;
pthread_attr_t kernel_request_thread_attr;
pthread_t kernel_request_thread;
queue_init(&g_request_queue);
gKernelRequestQueueLock = PTLockCreate();
if (!gKernelRequestQueueLock) {
kextd_error_log("failed to create kernel request queue lock");
result = false;
goto finish;
}
gRunLoopSourceLock = PTLockCreate();
if (!gRunLoopSourceLock) {
kextd_error_log(
"failed to create kernel request run loop source lock");
result = false;
goto finish;
}
gUserAuthorizationLock = PTLockCreate();
if (!gUserAuthorizationLock) {
kextd_error_log("failed to create kernel request queue lock");
result = false;
goto finish;
}
pthread_attr_init(&kernel_request_thread_attr);
pthread_create(&kernel_request_thread,
&kernel_request_thread_attr,
kextd_kernel_request_loop, NULL);
pthread_detach(kernel_request_thread);
finish:
if (!result) {
if (gKernelRequestQueueLock) {
PTLockFree(gKernelRequestQueueLock);
}
if (gRunLoopSourceLock) {
PTLockFree(gRunLoopSourceLock);
}
if (gUserAuthorizationLock) {
PTLockFree(gUserAuthorizationLock);
}
}
return result;
}
void * kextd_kernel_request_loop(void * arg)
{
kmod_args_t data = 0; mach_msg_type_number_t data_count = 0;
char * kmod_name = NULL; mach_port_t host_port = PORT_NULL;
host_port = mach_host_self();
if (!MACH_PORT_VALID(host_port)) {
}
while (1) {
kern_return_t kern_result;
kmod_load_extension_cmd_t * request;
unsigned int request_type;
if (data) {
kern_result = vm_deallocate(mach_task_self(),
(vm_address_t)data, data_count);
if (kern_result != KERN_SUCCESS) {
kextd_error_log("vm_deallocate() failed; aborting");
exit(1);
}
data = 0;
data_count = 0;
}
kern_result = kmod_control(host_port, 0, KMOD_CNTL_GET_CMD,
&data, &data_count);
if (kern_result != KERN_SUCCESS) {
kextd_error_log(
"kmod_control() error # %d; aborting kernel request loop",
kern_result);
goto finish;
}
request = (kmod_load_extension_cmd_t *)data;
request_type = request->type;
switch (request_type) {
case kIOCatalogMatchIdle:
break;
case KMOD_LOAD_EXTENSION_PACKET:
kmod_name = strdup(request->name);
if (!kmod_name) {
kextd_error_log(
"failed to read kmod name from kernel request");
continue;
}
break;
default:
kextd_error_log(
"received invalid kernel request, type %d",
request_type);
continue;
break;
}
if (kmod_name) {
request_t * load_request;
load_request = (request_t *)malloc(sizeof(request_t));
if (!load_request) {
kextd_error_log(
"failed to allocate data for kernel request");
} else {
memset(load_request, 0, sizeof(request_t));
load_request->type = load_request->type;
load_request->kmodname = kmod_name;
PTLockTakeLock(gKernelRequestQueueLock);
queue_enter(&g_request_queue, load_request, request_t *, link);
PTLockUnlock(gKernelRequestQueueLock);
PTLockTakeLock(gRunLoopSourceLock);
CFRunLoopSourceSignal(gKernelRequestRunLoopSource);
CFRunLoopWakeUp(gMainRunLoop);
PTLockUnlock(gRunLoopSourceLock);
}
}
}
finish:
if (PORT_NULL != host_port) {
mach_port_deallocate(mach_task_self(), host_port);
}
pthread_exit(NULL);
return NULL;
}
static int load_request_equal(request_t * a, request_t * b)
{
if (a->type != b->type) return 0;
if (strcmp(a->kmodname, b->kmodname)) return 0;
return 1;
}
void kextd_handle_kernel_request(void * info)
{
PTLockTakeLock(gKernelRequestQueueLock);
while (!queue_empty(&g_request_queue)) {
request_t * load_request = NULL; request_t * this_load_request = NULL; unsigned int type;
char * kmod_name = NULL;
load_request = (request_t *)queue_first(&g_request_queue);
queue_remove(&g_request_queue, load_request, request_t *, link);
this_load_request = (request_t *)queue_first(&g_request_queue);
while (!queue_end((request_t *)&g_request_queue, this_load_request)) {
request_t * next_load_request = NULL; next_load_request = (request_t *)
queue_next(&this_load_request->link);
if (load_request_equal(load_request, this_load_request)) {
queue_remove(&g_request_queue, this_load_request,
request_t *, link);
free(this_load_request->kmodname);
free(this_load_request);
}
this_load_request = next_load_request;
}
PTLockUnlock(gKernelRequestQueueLock);
type = load_request->type;
kmod_name = load_request->kmodname;
free(load_request);
if (kmod_name) {
kextd_load_kext(kmod_name, NULL);
free(kmod_name);
}
PTLockTakeLock(gKernelRequestQueueLock);
}
PTLockUnlock(gKernelRequestQueueLock);
return;
}
void kextd_load_kext(char * kmod_name,
KXKextManagerError * kext_result )
{
CFStringRef kextID = NULL; KXKextRef theKext = NULL; KXKextManagerError load_result = kKXKextManagerErrorNone;
kextID = CFStringCreateWithCString(kCFAllocatorDefault, kmod_name,
kCFStringEncodingMacRoman);
if (!kextID) {
return;
}
if (g_verbose_level > 0) {
kextd_log("kernel requests extension with id %s", kmod_name);
}
theKext = KXKextManagerGetKextWithIdentifier(gKextManager, kextID);
if (!theKext) {
KXKextManagerError remove_result;
CFDictionaryRef personality = NULL; const void * keys[1];
const void * values[1];
if (kext_result) {
*kext_result = kKXKextManagerErrorKextNotFound;
}
kextd_error_log("can't find extension with id %s",
kmod_name);
keys[0] = CFSTR("CFBundleIdentifier");
values[0] = kextID;
personality = CFDictionaryCreate(kCFAllocatorDefault,
keys, values, 1, &kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
if (!personality) {
kextd_error_log("out of memory");
goto finish;
}
remove_result = KXKextManagerRemovePersonalitiesFromCatalog(
gKextManager, personality);
CFRelease(personality);
if (remove_result != kKXKextManagerErrorNone) {
kextd_error_log("failed to remove personalities from IOCatalogue");
}
goto finish;
}
load_result = __kextd_load_kext(theKext, kmod_name);
if (kext_result) {
*kext_result = load_result;
}
finish:
if (kextID) CFRelease(kextID);
return;
}
static KXKextManagerError __kextd_load_kext(KXKextRef theKext,
const char * kmod_name)
{
KXKextManagerError load_result = kKXKextManagerErrorNone;
kern_return_t kern_result;
CFMutableArrayRef inauthenticKexts = NULL; #ifndef NO_CFUserNotification
CFIndex inauthentic_kext_count = 0;
CFIndex k = 0;
#endif
inauthenticKexts = CFArrayCreateMutable(kCFAllocatorDefault,
0, &kCFTypeArrayCallBacks);
if (!inauthenticKexts) {
load_result = kKXKextManagerErrorNoMemory;
goto finish;
}
load_result = _KXKextManagerPrepareKextForLoading(
gKextManager, theKext, NULL ,
true , true ,
inauthenticKexts);
if (load_result == kKXKextManagerErrorCache) {
kextd_error_log("scheduling rescan of all kexts due to cache "
"inconsistency");
CFRunLoopSourceSignal(gRescanRunLoopSource);
CFRunLoopWakeUp(gMainRunLoop);
goto finish;
} else if (load_result == kKXKextManagerErrorAlreadyLoaded ||
load_result == kKXKextManagerErrorLoadedVersionDiffers) {
goto post_load;
#ifndef NO_CFUserNotification
} else if (load_result == kKXKextManagerErrorAuthentication) {
if (logged_in_uid == -1) {
CFIndex count, i;
Boolean addIt = true;
count = CFArrayGetCount(gPendedNonsecureKexts);
for (i = 0; i < count; i++) {
KXKextRef scanKext = (KXKextRef)
CFArrayGetValueAtIndex(gPendedNonsecureKexts, i);
if (scanKext == theKext) {
addIt = false;
break;
}
}
if (addIt) {
CFArrayAppendValue(gPendedNonsecureKexts, theKext);
}
goto post_load;
} else {
inauthentic_kext_count = CFArrayGetCount(inauthenticKexts);
if (inauthentic_kext_count) {
for (k = 0; k < inauthentic_kext_count; k++) {
KXKextRef thisKext = (KXKextRef)CFArrayGetValueAtIndex(
inauthenticKexts, k);
CFArrayAppendValue(gScheduledNonsecureKexts, thisKext);
}
PTLockTakeLock(gRunLoopSourceLock);
CFRunLoopSourceSignal(gNonsecureKextRunLoopSource);
CFRunLoopWakeUp(gMainRunLoop);
PTLockUnlock(gRunLoopSourceLock);
goto post_load;
}
}
#endif
} else if (load_result != kKXKextManagerErrorNone) {
goto post_load;
}
load_result = _KXKextManagerLoadKextUsingOptions(
gKextManager,
theKext,
NULL, g_kernel_file, g_patch_dir,
g_symbol_dir,
true, true, false, false, gOverwrite_symbols,
false, 0, NULL);
post_load:
if (load_result == kKXKextManagerErrorNone ||
load_result == kKXKextManagerErrorAlreadyLoaded) {
kern_result = IOCatalogueModuleLoaded(g_io_master_port,
(char *)kmod_name);
if (kern_result != KERN_SUCCESS) {
kextd_error_log("failed to notify IOCatalogue that %s loaded",
kmod_name);
}
goto finish;
}
if (load_result != kKXKextManagerErrorNone &&
load_result != kKXKextManagerErrorAlreadyLoaded &&
load_result != kKXKextManagerErrorLoadedVersionDiffers) {
KXKextManagerRemoveKextPersonalitiesFromCatalog(
gKextManager, theKext);
goto finish;
}
finish:
if (inauthenticKexts) CFRelease(inauthenticKexts);
return load_result;
}
#ifndef NO_CFUserNotification
void kextd_resend_nonsecure_personalities(void)
{
CFMutableArrayRef allKextPersonalities = NULL; CFArrayRef thisKextPersonalities = NULL; CFIndex inauthentic_kext_count = 0;
CFIndex k;
allKextPersonalities = CFArrayCreateMutable(kCFAllocatorDefault,
0, &kCFTypeArrayCallBacks);
if (!allKextPersonalities) {
goto finish;
}
inauthentic_kext_count = CFArrayGetCount(gPendedNonsecureKexts);
if (inauthentic_kext_count && logged_in_uid != -1) {
for (k = 0; k < inauthentic_kext_count; k++) {
KXKextRef thisKext = (KXKextRef)CFArrayGetValueAtIndex(
gPendedNonsecureKexts, k);
if (thisKextPersonalities) {
CFRelease(thisKextPersonalities);
thisKextPersonalities = NULL;
}
thisKextPersonalities = KXKextCopyPersonalitiesArray(thisKext);
if (!thisKextPersonalities) {
goto finish;
}
CFArrayAppendArray(allKextPersonalities, thisKextPersonalities,
CFRangeMake(0, CFArrayGetCount(thisKextPersonalities)));
KXKextManagerRequalifyKext(gKextManager, thisKext);
}
}
CFArrayRemoveAllValues(gPendedNonsecureKexts);
if (KXKextManagerSendPersonalitiesToCatalog(gKextManager,
allKextPersonalities) != kKXKextManagerErrorNone) {
kextd_error_log("can't send kext personalities to kernel");
goto finish;
}
PTLockTakeLock(gRunLoopSourceLock);
CFRunLoopSourceSignal(gNonsecureKextRunLoopSource);
CFRunLoopWakeUp(gMainRunLoop);
PTLockUnlock(gRunLoopSourceLock);
finish:
if (thisKextPersonalities) CFRelease(thisKextPersonalities);
if (allKextPersonalities) CFRelease(allKextPersonalities);
return;
}
#endif
kern_return_t _kextmanager_path_for_bundle_id(
mach_port_t server,
kext_bundle_id_t bundle_id,
posix_path_t path,
KXKextManagerError * kext_result)
{
kern_return_t result = KERN_SUCCESS;
KXKextManagerError kmResult = kKXKextManagerErrorNone;
CFStringRef kextID = NULL; KXKextRef theKext = NULL; CFURLRef kextURL = NULL; CFStringRef kextPath = NULL; char * kext_path = NULL;
path[0] = '\0';
if (g_verbose_level >= 1) {
kextd_log("received client request for path to bundle %s", bundle_id);
}
kextID = CFStringCreateWithCString(kCFAllocatorDefault, bundle_id,
kCFStringEncodingMacRoman);
if (!kextID) {
kmResult = kKXKextManagerErrorNoMemory;
goto finish;
}
theKext = KXKextManagerGetLoadedOrLatestKextWithIdentifier(
gKextManager, kextID);
if (!theKext) {
if (g_verbose_level >= 1) {
kextd_log("bundle %s not found", bundle_id);
}
kmResult = kKXKextManagerErrorKextNotFound;
goto finish;
}
kextURL = KXKextGetAbsoluteURL(theKext);
if (!kextURL) {
kmResult = kKXKextManagerErrorNoMemory;
goto finish;
}
kext_path = CFURLCopyCString(kextURL);
if (!kext_path) {
kmResult = kKXKextManagerErrorUnspecified;
goto finish;
}
strcpy(path, kext_path);
if (g_verbose_level >= 1) {
kextd_log("returning bundle path %s", path);
}
finish:
if (kextID) CFRelease(kextID);
if (kextPath) CFRelease(kextPath);
if (kext_path) free(kext_path);
if (kext_result) {
*kext_result = kmResult;
}
gClientUID = -1;
return result;
}
kern_return_t kext_load_bundle_with_id(
mach_port_t server,
char * bundle_id,
KXKextManagerError * kext_result)
{
kern_return_t result = KERN_FAILURE;
if (kext_result) {
*kext_result = kKXKextManagerErrorUnspecified;
}
goto finish;
kextd_load_kext(bundle_id, kext_result);
result = KERN_SUCCESS;
finish:
gClientUID = -1;
return result;
}
extern CFArrayRef _KXKextRepositoryGetCandidateKexts(
KXKextRepositoryRef aRepository);
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 = KERN_SUCCESS;
CFStringRef propertyKey = NULL;
CFMutableArrayRef propertyValues = NULL; CFDictionaryRef infoDictionary = NULL; CFTypeRef value = NULL;
CFArrayRef repositories = NULL; CFIndex numRepositories, i;
CFArrayRef candidateKexts = NULL;
CFMutableDictionaryRef newDict = NULL; CFStringRef kextPath = NULL; CFStringRef kextVersion = NULL;
CFDataRef xmlData = NULL;
if (g_verbose_level >= 1) {
kextd_log("received client request for property value array");
}
if (!xml_data_out || !xml_data_length) {
result = KERN_INVALID_ARGUMENT;
goto finish;
}
propertyKey = CFStringCreateWithCString(kCFAllocatorDefault, property_key,
kCFStringEncodingMacRoman);
if (!propertyKey) {
result = KERN_FAILURE;
goto finish;
}
propertyValues = CFArrayCreateMutable(kCFAllocatorDefault,
0, &kCFTypeArrayCallBacks);
if (!propertyValues) {
result = KERN_FAILURE;
goto finish;
}
repositories = KXKextManagerGetRepositories(gKextManager);
numRepositories = CFArrayGetCount(repositories);
for (i = 0; i < numRepositories; i++) {
CFIndex numKexts, k;
KXKextRepositoryRef thisRepository =
(KXKextRepositoryRef)CFArrayGetValueAtIndex(
repositories, i);
candidateKexts = _KXKextRepositoryGetCandidateKexts(thisRepository);
numKexts = CFArrayGetCount(candidateKexts);
for (k = 0; k < numKexts; k++) {
KXKextRef thisKext =
(KXKextRef)CFArrayGetValueAtIndex(candidateKexts, k);
if (!KXKextIsValid(thisKext)) {
continue;
}
if (KXKextManagerGetSafeBootMode(gKextManager) &&
!KXKextIsEligibleDuringSafeBoot(thisKext)) {
continue;
}
if (!KXKextIsEnabled(thisKext)) {
continue;
}
infoDictionary = KXKextGetInfoDictionary(thisKext);
if (!infoDictionary) {
continue;
}
value = CFDictionaryGetValue(infoDictionary, propertyKey);
if (!value) {
continue;
}
newDict = CFDictionaryCreateMutable(
kCFAllocatorDefault, 0,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
if (!newDict) {
result = KERN_FAILURE;
goto finish;
}
CFDictionarySetValue(newDict, CFSTR("Data"), value);
CFDictionarySetValue(newDict, CFSTR("CFBundleIdentifier"),
KXKextGetBundleIdentifier(thisKext));
kextPath = KXKextCopyAbsolutePath(thisKext);
if (!kextPath) {
result = KERN_FAILURE;
goto finish;
}
CFDictionarySetValue(newDict, CFSTR("OSBundlePath"), kextPath);
CFRelease(kextPath);
kextPath = NULL;
kextVersion = CFDictionaryGetValue(infoDictionary,
CFSTR("CFBundleVersion"));
if (!kextVersion) {
result = KERN_FAILURE;
goto finish;
}
CFDictionarySetValue(newDict, CFSTR("CFBundleVersion"),
kextVersion);
kextVersion = NULL;
CFArrayAppendValue(propertyValues, newDict);
CFRelease(newDict);
newDict = NULL;
}
}
xmlData = CFPropertyListCreateXMLData(kCFAllocatorDefault,
propertyValues);
if (!xmlData) {
result = KERN_FAILURE;
goto finish;
}
*xml_data_length = (int)CFDataGetLength(xmlData);
result = vm_allocate(mach_task_self(), (vm_address_t *)xml_data_out,
*xml_data_length, VM_FLAGS_ANYWHERE);
if (result != KERN_SUCCESS) {
goto finish;
}
memcpy(*xml_data_out, CFDataGetBytePtr(xmlData), *xml_data_length);
finish:
if (propertyKey) CFRelease(propertyKey);
if (propertyValues) CFRelease(propertyValues);
if (newDict) CFRelease(newDict);
if (kextPath) CFRelease(kextPath);
if (xmlData) CFRelease(xmlData);
gClientUID = -1;
return result;
}
kern_return_t _kextmanager_user_did_log_in(
mach_port_t server,
int euid,
AuthorizationExternalForm authref)
{
kern_return_t result = KERN_SUCCESS;
logged_in_uid = euid;
if (AuthorizationCreateFromExternalForm(&authref,
&gAuthRef) != errAuthorizationSuccess) {
result = KERN_FAILURE;
goto finish;
}
#ifndef NO_CFUserNotification
kextd_resend_nonsecure_personalities();
#endif
finish:
gClientUID = -1;
return result;
}
kern_return_t _kextmanager_user_will_log_out(
mach_port_t server,
int euid)
{
kern_return_t result = KERN_SUCCESS;
if (gSecurityNotification) {
CFUserNotificationCancel(gSecurityNotification);
CFRelease(gSecurityNotification);
gSecurityNotification = NULL;
}
if (gFailureNotification) {
CFUserNotificationCancel(gFailureNotification);
CFRelease(gFailureNotification);
gFailureNotification = NULL;
}
gSecurityAlertKext = NULL;
gResendSecurityAlertKextPersonalities = false;
PTLockTakeLock(gUserAuthorizationLock);
gWaitingForAuthorization = false;
gAuthorizationReady = false;
PTLockUnlock(gUserAuthorizationLock);
if (gAuthRef) {
AuthorizationFree(gAuthRef, 0);
gAuthRef = NULL;
}
logged_in_uid = -1;
gClientUID = -1;
return result;
}
kern_return_t _kextmanager_get_logged_in_userid(
mach_port_t server,
int * euid)
{
kern_return_t result = KERN_SUCCESS;
*euid = logged_in_uid;
gClientUID = -1;
return result;
}
#ifndef NO_CFUserNotification
void kextd_handle_pended_kextload(void * info)
{
CFDictionaryRef loadDataDict = NULL; CFDataRef loadData = NULL; CFDataRef dataValue = NULL; const char * data_value = NULL; CFArrayRef argvArray = NULL; CFStringRef errorString = NULL; Boolean scheduleAnother = false;
Boolean waitingForAuthorization = false;
Boolean authorizationReady = false;
FILE *ext_auth_mbox = NULL;
char *kextd_ext_auth_env = NULL;
PTLockTakeLock(gUserAuthorizationLock);
waitingForAuthorization = gWaitingForAuthorization;
authorizationReady = gAuthorizationReady;
PTLockUnlock(gUserAuthorizationLock);
if (gFork_pid != -1) {
pid_t wait_pid;
int status = 0;
wait_pid = waitpid(gFork_pid, &status, WUNTRACED | WNOHANG);
if (wait_pid) {
gFork_pid = -1;
if (WIFEXITED(status)) {
} else if (WIFSIGNALED(status)) {
kextd_error_log("forked kextload task exited by signal (%d)",
WTERMSIG(status));
} else if (WIFSTOPPED(status)) {
kextd_error_log("forked kextload load task has stopped");
} else {
kextd_error_log("unknown result from forked kextload task");
}
}
sleep(1); scheduleAnother = true;
} else if (logged_in_uid != -1 && waitingForAuthorization) {
sleep(1); scheduleAnother = true;
} else if (logged_in_uid != -1 && (authorizationReady ||
gSecurityNotification || gFailureNotification) ) {
_kextd_poll_alert_response();
scheduleAnother = true;
} else if (gSecurityAlertKext && gResendSecurityAlertKextPersonalities) {
KXKextManagerSendKextPersonalitiesToCatalog(
gKextManager, gSecurityAlertKext, NULL, false ,
KXKextManagerGetSafeBootMode(gKextManager));
gSecurityAlertKext = NULL;
gResendSecurityAlertKextPersonalities = false;
scheduleAnother = true;
} else if (CFArrayGetCount(gScheduledNonsecureKexts) && logged_in_uid != -1) {
KXKextRef thisKext = NULL; thisKext = (KXKextRef)CFArrayGetValueAtIndex(gScheduledNonsecureKexts, 0);
if (!thisKext) {
scheduleAnother = true;
goto finish;
}
CFArrayRemoveValueAtIndex(gScheduledNonsecureKexts, 0);
if (KXKextIsAuthentic(thisKext)) {
scheduleAnother = true;
goto finish;
}
_kextd_raise_security_alert(thisKext);
scheduleAnother = true;
} else if (CFArrayGetCount(gPendedKextloadOperations) && logged_in_uid != -1) {
const char * working_dir = NULL; int k_argc = 0;
AuthorizationExternalForm auth_ext_form;
loadData = (CFDataRef)CFArrayGetValueAtIndex(
gPendedKextloadOperations, 0);
if (loadData) {
CFRetain(loadData);
CFArrayRemoveValueAtIndex(gPendedKextloadOperations, 0);
} else {
kextd_error_log("can't get pended kextload operation data");
scheduleAnother = true;
goto finish;
}
gFork_pid = -1;
loadDataDict = CFPropertyListCreateFromXMLData(
kCFAllocatorDefault, loadData,
kCFPropertyListImmutable, &errorString);
if (!loadDataDict) {
if (errorString) {
CFIndex length = CFStringGetLength(errorString);
char * error_string = (char *)malloc((1+length) * sizeof(char));
if (!error_string) {
goto finish;
}
if (CFStringGetCString(errorString, error_string,
length, kCFStringEncodingMacRoman)) {
kextd_error_log("error reading kextload operation data: %s",
error_string);
} else {
kextd_error_log(
"unknown error reading kextload operation data");
}
free(error_string);
}
goto finish;
}
dataValue = (CFDataRef)CFDictionaryGetValue(loadDataDict,
CFSTR("workingDir"));
if (!dataValue) {
goto finish;
}
working_dir = CFDataGetBytePtr(dataValue);
argvArray = (CFArrayRef)CFDictionaryGetValue(loadDataDict,
CFSTR("argv"));
if (!argvArray) {
goto finish;
}
k_argc = CFArrayGetCount(argvArray);
if (k_argc <= 1) {
goto finish;
}
if (errAuthorizationSuccess == AuthorizationMakeExternalForm(gAuthRef, &auth_ext_form))
{
do {
ext_auth_mbox = tmpfile();
if (!ext_auth_mbox)
break;
if (fwrite(&auth_ext_form, sizeof(auth_ext_form), 1, ext_auth_mbox) != 1)
{
fclose(ext_auth_mbox);
break;
}
fflush(ext_auth_mbox);
asprintf(&kextd_ext_auth_env, "KEXTD_AUTHORIZATION=%d", fileno(ext_auth_mbox));
} while (0);
}
gFork_pid = fork();
if (gFork_pid < 0) {
kextd_error_log("can't fork child process to run kextload");
gFork_pid = -1;
goto finish;
} else if (gFork_pid == 0) {
char ** k_argv = NULL; char kextd_launch[100]; int env_length = 0;
char ** envp = NULL;
char ** envp_copy = NULL;
char ** kextload_env = NULL; int i;
k_argv = (char **)malloc((1 + k_argc) * sizeof(char *));
if (!k_argv) {
kextd_error_log("can't build argv array for kextload");
exit(-1);
}
bzero(k_argv, (1 + k_argc) * sizeof(char *));
for (i = 0; i < k_argc; i++) {
dataValue = (CFDataRef)CFArrayGetValueAtIndex(argvArray, i);
data_value = CFDataGetBytePtr(dataValue);
k_argv[i] = (char *)data_value;
}
for (env_length = 0, envp = environ; *envp; envp++) {
env_length++;
}
env_length++; if (ext_auth_mbox >= 0)
env_length++; env_length++;
kextload_env = (char **)malloc(env_length * sizeof(char *));
if (!kextload_env) {
kextd_error_log("can't build environment for kextload");
exit(-1);
}
bzero(kextload_env, env_length * sizeof(char *));
for (envp = environ, envp_copy = kextload_env;
*envp; envp++, envp_copy++) {
*envp_copy = *envp;
}
sprintf(kextd_launch, "KEXTD_LAUNCH_USERID=%d", logged_in_uid);
*envp_copy++ = kextd_launch;
if (kextd_ext_auth_env)
*envp_copy++ = kextd_ext_auth_env;
*envp_copy++ = NULL;
if (execve(k_argv[0], k_argv, kextload_env) == -1) {
kextd_error_log("can't exec kextload (%s)", strerror(errno));
exit(-1); }
if (k_argv) {
free(k_argv);
k_argv = NULL;
}
if (kextload_env) {
free(kextload_env);
kextload_env = NULL;
}
} else {
scheduleAnother = true;
goto finish;
}
}
finish:
if (ext_auth_mbox)
fclose(ext_auth_mbox);
if (kextd_ext_auth_env) {
free(kextd_ext_auth_env);
kextd_ext_auth_env = NULL;
}
if (loadData) CFRelease(loadData);
if (loadDataDict) CFRelease(loadDataDict);
if (errorString) CFRelease(errorString);
if (scheduleAnother) {
PTLockTakeLock(gRunLoopSourceLock);
CFRunLoopSourceSignal(gNonsecureKextRunLoopSource);
CFRunLoopWakeUp(gMainRunLoop);
PTLockUnlock(gRunLoopSourceLock);
}
return;
}
#endif
kern_return_t _kextmanager_record_nonsecure_kextload(
mach_port_t server,
char * load_data,
int load_data_length)
{
kern_return_t result = KERN_SUCCESS;
#ifndef NO_CFUserNotification
CFDataRef loadData = NULL;
loadData = CFDataCreate(kCFAllocatorDefault, load_data, load_data_length);
if (!loadData) {
result = KERN_FAILURE;
goto finish;
}
CFArrayAppendValue(gPendedKextloadOperations, loadData);
#endif
finish:
#ifndef NO_CFUserNotification
if (loadData) CFRelease(loadData);
#endif
gClientUID = -1;
return result;
}
#ifndef NO_CFUserNotification
static void _kextd_raise_security_alert(KXKextRef aKext)
{
CFMutableDictionaryRef alertDict = NULL; CFMutableArrayRef alertMessageArray = NULL; CFURLRef iokitFrameworkBundleURL = NULL; SInt32 userNotificationError = 0;
gResendSecurityAlertKextPersonalities = false;
alertDict = CFDictionaryCreateMutable(
kCFAllocatorDefault, 0,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
if (!alertDict) {
goto finish;
}
iokitFrameworkBundleURL = CFURLCreateWithFileSystemPath(
kCFAllocatorDefault,
CFSTR("/System/Library/Frameworks/IOKit.framework"),
kCFURLPOSIXPathStyle, true);
if (!iokitFrameworkBundleURL) {
goto finish;
}
alertMessageArray = CFArrayCreateMutable(kCFAllocatorDefault, 0,
&kCFTypeArrayCallBacks);
if (!alertMessageArray) {
goto finish;
}
CFArrayAppendValue(alertMessageArray,
CFSTR("The file \""));
CFArrayAppendValue(alertMessageArray, KXKextGetBundleDirectoryName(aKext));
CFArrayAppendValue(alertMessageArray,
CFSTR("\" has problems that may reduce the security of "
"your computer. You should contact the manufacturer of the "
"product you are using for a new version. If you are sure the "
"file is OK, you can allow the application to use it, or fix "
"it and then use it. If you click Don't Use, any other files "
"that depend on this file will not be used."));
CFDictionarySetValue(alertDict, kCFUserNotificationLocalizationURLKey,
iokitFrameworkBundleURL);
CFDictionarySetValue(alertDict, kCFUserNotificationAlertHeaderKey,
CFSTR("The program you are using needs to use a system file that may "
"reduce the security of your computer."));
CFDictionarySetValue(alertDict, kCFUserNotificationDefaultButtonTitleKey,
CFSTR("Don't Use"));
CFDictionarySetValue(alertDict, kCFUserNotificationAlternateButtonTitleKey,
CFSTR("Fix and Use"));
CFDictionarySetValue(alertDict, kCFUserNotificationOtherButtonTitleKey,
CFSTR("Use"));
CFDictionarySetValue(alertDict, kCFUserNotificationAlertMessageKey,
alertMessageArray);
gSecurityAlertResponse = 0;
gSecurityNotification = CFUserNotificationCreate(kCFAllocatorDefault,
0 , kCFUserNotificationCautionAlertLevel,
&userNotificationError, alertDict);
if (!gSecurityNotification) {
kextd_error_log(
"error creating user notification (%d)", userNotificationError);
goto finish;
}
gSecurityAlertKext = aKext;
PTLockTakeLock(gRunLoopSourceLock);
CFRunLoopSourceSignal(gNonsecureKextRunLoopSource);
CFRunLoopWakeUp(gMainRunLoop);
PTLockUnlock(gRunLoopSourceLock);
finish:
if (alertDict) CFRelease(alertDict);
if (alertMessageArray) CFRelease(alertMessageArray);
if (iokitFrameworkBundleURL) CFRelease(iokitFrameworkBundleURL);
return;
}
static void _kextd_raise_failure_alert(KXKextRef aKext)
{
CFMutableDictionaryRef alertDict = NULL; CFMutableArrayRef alertMessageArray = NULL; CFURLRef iokitFrameworkBundleURL = NULL; SInt32 userNotificationError = 0;
alertDict = CFDictionaryCreateMutable(
kCFAllocatorDefault, 0,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
if (!alertDict) {
goto finish;
}
iokitFrameworkBundleURL = CFURLCreateWithFileSystemPath(
kCFAllocatorDefault,
CFSTR("/System/Library/Frameworks/IOKit.framework"),
kCFURLPOSIXPathStyle, true);
if (!iokitFrameworkBundleURL) {
goto finish;
}
alertMessageArray = CFArrayCreateMutable(kCFAllocatorDefault, 0,
&kCFTypeArrayCallBacks);
if (!alertMessageArray) {
goto finish;
}
CFArrayAppendValue(alertMessageArray,
CFSTR("The file \""));
CFArrayAppendValue(alertMessageArray, KXKextGetBundleDirectoryName(aKext));
CFArrayAppendValue(alertMessageArray,
CFSTR("\" could not be fixed, but it will be used anyway."));
CFDictionarySetValue(alertDict, kCFUserNotificationLocalizationURLKey,
iokitFrameworkBundleURL);
CFDictionarySetValue(alertDict, kCFUserNotificationAlertHeaderKey,
CFSTR("File Access Error"));
CFDictionarySetValue(alertDict, kCFUserNotificationDefaultButtonTitleKey,
CFSTR("OK"));
CFDictionarySetValue(alertDict, kCFUserNotificationAlertMessageKey,
alertMessageArray);
gFailureAlertResponse = 0;
gFailureNotification = CFUserNotificationCreate(kCFAllocatorDefault,
0 , kCFUserNotificationCautionAlertLevel,
&userNotificationError, alertDict);
if (!gFailureNotification) {
kextd_error_log(
"error creating user notification (%d)", userNotificationError);
goto finish;
}
PTLockTakeLock(gRunLoopSourceLock);
CFRunLoopSourceSignal(gNonsecureKextRunLoopSource);
CFRunLoopWakeUp(gMainRunLoop);
PTLockUnlock(gRunLoopSourceLock);
finish:
if (alertDict) CFRelease(alertDict);
if (alertMessageArray) CFRelease(alertMessageArray);
if (iokitFrameworkBundleURL) CFRelease(iokitFrameworkBundleURL);
return;
}
static void _kextd_poll_alert_response(void)
{
int userNotificationResult = 0;
Boolean authorizationReady = false;
PTLockTakeLock(gUserAuthorizationLock);
authorizationReady = gAuthorizationReady;
PTLockUnlock(gUserAuthorizationLock);
if (authorizationReady && gSecurityAlertKext) {
PTLockTakeLock(gUserAuthorizationLock);
gWaitingForAuthorization = false;
gAuthorizationReady = false;
PTLockUnlock(gUserAuthorizationLock);
if (auth_result != errAuthorizationSuccess) {
gResendSecurityAlertKextPersonalities = false;
gSecurityAlertKext = NULL;
goto finish;
}
KXKextManagerRequalifyKext(gKextManager, gSecurityAlertKext);
if (gSecurityAlertResponse == kCFUserNotificationAlternateResponse) {
KXKextManagerError makeSecureResult = _KXKextMakeSecure(gSecurityAlertKext);
if (makeSecureResult == kKXKextManagerErrorNone) {
gResendSecurityAlertKextPersonalities = true;
} else if (makeSecureResult == kKXKextManagerErrorFileAccess) {
KXKextMarkAuthentic(gSecurityAlertKext);
_kextd_raise_failure_alert(gSecurityAlertKext);
} else {
gSecurityAlertKext = NULL;
}
goto finish;
} else if (gSecurityAlertResponse == kCFUserNotificationOtherResponse) {
KXKextMarkAuthentic(gSecurityAlertKext);
gResendSecurityAlertKextPersonalities = true;
goto finish;
}
} else if (gFailureNotification && gSecurityAlertKext) {
userNotificationResult = CFUserNotificationReceiveResponse(
gFailureNotification, 1, &gFailureAlertResponse);
if (userNotificationResult != 0) {
goto finish;
} else {
gResendSecurityAlertKextPersonalities = true;
CFRelease(gFailureNotification);
gFailureNotification = NULL;
}
} else if (gSecurityNotification && gSecurityAlertKext) {
userNotificationResult = CFUserNotificationReceiveResponse(
gSecurityNotification, 1, &gSecurityAlertResponse);
if (userNotificationResult != 0) {
goto finish;
} else {
CFRelease(gSecurityNotification);
gSecurityNotification = NULL;
}
if (gSecurityAlertResponse == kCFUserNotificationDefaultResponse) {
gResendSecurityAlertKextPersonalities = false;
gSecurityAlertKext = NULL;
goto finish;
} else if (gSecurityAlertResponse == kCFUserNotificationAlternateResponse ||
gSecurityAlertResponse == kCFUserNotificationOtherResponse) {
PTLockTakeLock(gUserAuthorizationLock);
gWaitingForAuthorization = true;
gAuthorizationReady = false;
PTLockUnlock(gUserAuthorizationLock);
_kextd_launch_authorization();
}
}
finish:
return;
}
void _kextd_launch_authorization(void)
{
pthread_attr_t auth_thread_attr;
pthread_t auth_request_thread;
pthread_attr_init(&auth_thread_attr);
pthread_create(&auth_request_thread,
&auth_thread_attr,
_kextd_authorize_user, NULL);
pthread_detach(auth_request_thread);
}
void * _kextd_authorize_user(void * arg)
{
uid_t real_euid = geteuid();
AuthorizationItem fixkextright = { "system.kext.make_secure", 0,
NULL, 0 };
const AuthorizationItemSet fixkextrightset = { 1, &fixkextright };
int flags = kAuthorizationFlagExtendRights |
kAuthorizationFlagInteractionAllowed;
if (seteuid(logged_in_uid) != 0) {
kextd_error_log("call to seteuid() failed");
auth_result = errAuthorizationDenied;
PTLockTakeLock(gUserAuthorizationLock);
gWaitingForAuthorization = false;
gAuthorizationReady = true;
PTLockUnlock(gUserAuthorizationLock);
goto finish; }
auth_result = AuthorizationCopyRights(gAuthRef, &fixkextrightset,
NULL, flags, NULL);
seteuid(real_euid);
PTLockTakeLock(gUserAuthorizationLock);
gWaitingForAuthorization = false;
gAuthorizationReady = true;
PTLockUnlock(gUserAuthorizationLock);
finish:
pthread_exit(NULL);
return NULL;
}
#endif