#include <libkern/c++/OSContainers.h>
#include <IOKit/IOCatalogue.h>
#include <IOKit/IOLib.h>
#include <libsa/kext.h>
#include <libsa/catalogue.h>
extern "C" {
#include <mach-o/kld.h>
#include <libsa/vers_rsrc.h>
#include <libsa/stdlib.h>
#include <mach/kmod.h>
#include <vm/vm_kern.h>
#include <mach/kern_return.h>
#include <mach-o/fat.h>
#include <mach_loader.h>
#include "kld_patch.h"
#include "dgraph.h"
#include "load.h"
};
extern "C" {
extern kern_return_t
kmod_create_internal(
kmod_info_t *info,
kmod_t *id);
extern kern_return_t
kmod_destroy_internal(kmod_t id);
extern kern_return_t
kmod_start_or_stop(
kmod_t id,
int start,
kmod_args_t *data,
mach_msg_type_number_t *dataCount);
extern kern_return_t kmod_retain(kmod_t id);
extern kern_return_t kmod_release(kmod_t id);
extern Boolean kmod_load_request(const char * moduleName, Boolean make_request);
};
extern kmod_args_t
get_module_data(OSDictionary * kextPlist, mach_msg_type_number_t * datalen);
extern struct mac_module_data *osdict_encode(OSDictionary *dict);
#define DEBUG
#ifdef DEBUG
#define LOG_DELAY(x) IODelay((x) * 1000000)
#define VTYELLOW "\033[33m"
#define VTRESET "\033[0m"
#else
#define LOG_DELAY(x)
#define VTYELLOW
#define VTRESET
#endif
#define KERNEL_PREFIX "com.apple.kernel"
#define KPI_PREFIX "com.apple.kpi"
static
bool getKext(
const char * bundleid,
OSDictionary ** plist,
unsigned char ** code,
unsigned long * code_size,
bool * caller_owns_code)
{
bool result = true;
OSDictionary * extensionsDict; OSDictionary * extDict; OSDictionary * extPlist; unsigned long code_size_local;
extensionsDict = getStartupExtensions();
if (!extensionsDict) {
IOLog("startup extensions dictionary is missing\n");
result = false;
goto finish;
}
extDict = OSDynamicCast(OSDictionary,
extensionsDict->getObject(bundleid));
if (!extDict) {
IOLog("extension \"%s\" cannot be found\n",
bundleid);
result = false;
goto finish;
}
if (plist) {
extPlist = OSDynamicCast(OSDictionary, extDict->getObject("plist"));
if (!extPlist) {
IOLog("extension \"%s\" has no info dictionary\n",
bundleid);
result = false;
goto finish;
}
*plist = extPlist;
}
if (code) {
if (!caller_owns_code) {
IOLog("getKext(): invalid usage (caller_owns_code not provided)\n");
result = false;
goto finish;
}
*code = 0;
if (code_size) {
*code_size = 0;
}
*caller_owns_code = false;
*code = (unsigned char *)kld_file_getaddr(bundleid,
(unsigned long *)&code_size_local);
if (*code) {
if (code_size) {
*code_size = code_size_local;
}
} else {
OSData * driverCode = 0;
driverCode = OSDynamicCast(OSData, extDict->getObject("code"));
if (driverCode) {
*code = (unsigned char *)driverCode->getBytesNoCopy();
if (code_size) {
*code_size = driverCode->getLength();
}
} else { OSData * compressedCode = 0;
compressedCode = OSDynamicCast(OSData,
extDict->getObject("compressedCode"));
if (compressedCode) {
if (!uncompressModule(compressedCode, &driverCode)) {
IOLog("extension \"%s\": couldn't uncompress code\n",
bundleid);
result = false;
goto finish;
}
*caller_owns_code = true;
*code = (unsigned char *)driverCode->getBytesNoCopy();
if (code_size) {
*code_size = driverCode->getLength();
}
driverCode->release();
}
}
}
}
finish:
return result;
}
static
bool verifyCompatibility(OSString * extName, OSString * requiredVersion)
{
OSDictionary * extPlist; OSString * extVersion; OSString * extCompatVersion; VERS_version ext_version;
VERS_version ext_compat_version;
VERS_version required_version;
if (!getKext(extName->getCStringNoCopy(), &extPlist, NULL, NULL, NULL)) {
return false;
}
extVersion = OSDynamicCast(OSString,
extPlist->getObject("CFBundleVersion"));
if (!extVersion) {
IOLog("verifyCompatibility(): "
"Extension \"%s\" has no \"CFBundleVersion\" property.\n",
extName->getCStringNoCopy());
return false;
}
extCompatVersion = OSDynamicCast(OSString,
extPlist->getObject("OSBundleCompatibleVersion"));
if (!extCompatVersion) {
IOLog("verifyCompatibility(): "
"Extension \"%s\" has no \"OSBundleCompatibleVersion\" property.\n",
extName->getCStringNoCopy());
return false;
}
required_version = VERS_parse_string(requiredVersion->getCStringNoCopy());
if (required_version < 0) {
IOLog("verifyCompatibility(): "
"Can't parse required version \"%s\" of dependency %s.\n",
requiredVersion->getCStringNoCopy(),
extName->getCStringNoCopy());
return false;
}
ext_version = VERS_parse_string(extVersion->getCStringNoCopy());
if (ext_version < 0) {
IOLog("verifyCompatibility(): "
"Can't parse version \"%s\" of dependency %s.\n",
extVersion->getCStringNoCopy(),
extName->getCStringNoCopy());
return false;
}
ext_compat_version = VERS_parse_string(extCompatVersion->getCStringNoCopy());
if (ext_compat_version < 0) {
IOLog("verifyCompatibility(): "
"Can't parse compatible version \"%s\" of dependency %s.\n",
extCompatVersion->getCStringNoCopy(),
extName->getCStringNoCopy());
return false;
}
if (required_version > ext_version || required_version < ext_compat_version) {
return false;
}
return true;
}
static
bool kextIsDependency(const char * kext_name, char * is_kernel) {
bool result = true;
OSDictionary * extensionsDict = 0; OSDictionary * extDict = 0; OSDictionary * extPlist = 0; OSBoolean * isKernelResourceObj = 0; OSData * driverCode = 0; OSData * compressedCode = 0;
if (is_kernel) {
*is_kernel = 0;
}
extensionsDict = getStartupExtensions();
if (!extensionsDict) {
IOLog("startup extensions dictionary is missing\n");
result = false;
goto finish;
}
extDict = OSDynamicCast(OSDictionary,
extensionsDict->getObject(kext_name));
if (!extDict) {
IOLog("extension \"%s\" cannot be found\n",
kext_name);
result = false;
goto finish;
}
extPlist = OSDynamicCast(OSDictionary, extDict->getObject("plist"));
if (!extPlist) {
IOLog("extension \"%s\" has no info dictionary\n",
kext_name);
result = false;
goto finish;
}
isKernelResourceObj = OSDynamicCast(OSBoolean,
extPlist->getObject("OSKernelResource"));
if (isKernelResourceObj && isKernelResourceObj->isTrue()) {
if (is_kernel) {
*is_kernel = 1;
}
}
driverCode = OSDynamicCast(OSData, extDict->getObject("code"));
compressedCode = OSDynamicCast(OSData,
extDict->getObject("compressedCode"));
if ((driverCode || compressedCode) && is_kernel && *is_kernel) {
*is_kernel = 2;
}
if (!driverCode && !compressedCode && !isKernelResourceObj) {
result = false;
goto finish;
}
finish:
return result;
}
static bool
addDependenciesForKext(OSDictionary * kextPlist,
OSArray * dependencyList,
OSString * trueParent,
Boolean skipKernelDependencies)
{
bool result = true;
bool hasDirectKernelDependency = false;
bool hasKernelStyleDependency = false;
bool hasKPIStyleDependency = false;
OSString * kextName = 0; OSDictionary * libraries = 0; OSCollectionIterator * keyIterator = 0; OSString * libraryName = 0; OSString * dependentName = 0;
kextName = OSDynamicCast(OSString,
kextPlist->getObject("CFBundleIdentifier"));
if (!kextName) {
result = false;
goto finish;
}
libraries = OSDynamicCast(OSDictionary,
kextPlist->getObject("OSBundleLibraries"));
if (!libraries) {
result = true;
goto finish;
}
keyIterator = OSCollectionIterator::withCollection(libraries);
if (!keyIterator) {
result = false;
goto finish;
}
dependentName = trueParent ? trueParent : kextName;
while ( (libraryName = OSDynamicCast(OSString,
keyIterator->getNextObject())) ) {
OSString * libraryVersion = OSDynamicCast(OSString,
libraries->getObject(libraryName));
if (!libraryVersion) {
result = false;
goto finish;
}
if (!verifyCompatibility(libraryName, libraryVersion)) {
result = false;
goto finish;
} else {
char is_kernel_component;
if (!kextIsDependency(libraryName->getCStringNoCopy(),
&is_kernel_component)) {
is_kernel_component = 0;
}
if (!skipKernelDependencies || !is_kernel_component) {
dependencyList->setObject(dependentName);
dependencyList->setObject(libraryName);
}
if (!hasDirectKernelDependency && is_kernel_component) {
hasDirectKernelDependency = true;
}
if (strncmp(libraryName->getCStringNoCopy(),
KERNEL_PREFIX, strlen(KERNEL_PREFIX)) == 0) {
hasKernelStyleDependency = true;
} else if (strncmp(libraryName->getCStringNoCopy(),
KPI_PREFIX, strlen(KPI_PREFIX)) == 0) {
hasKPIStyleDependency = true;
}
}
}
if (!hasDirectKernelDependency) {
const OSSymbol * kernelName = 0;
dependencyList->setObject(dependentName);
kernelName = OSSymbol::withCString("com.apple.kernel.libkern");
if (!kernelName) {
result = false;
goto finish;
}
dependencyList->setObject(kernelName);
kernelName->release();
IOLog("Extension \"%s\" has no explicit kernel dependency; using version 6.0.\n",
kextName->getCStringNoCopy());
} else if (hasKernelStyleDependency && hasKPIStyleDependency) {
IOLog("Extension \"%s\" has immediate dependencies "
"on both com.apple.kernel and com.apple.kpi components; use only one style.\n",
kextName->getCStringNoCopy());
}
finish:
if (keyIterator) keyIterator->release();
return result;
}
static
bool getVersionForKext(OSDictionary * kextPlist, char ** version)
{
OSString * kextName = 0; OSString * kextVersion;
kextName = OSDynamicCast(OSString,
kextPlist->getObject("CFBundleIdentifier"));
if (!kextName) {
return false;
}
kextVersion = OSDynamicCast(OSString,
kextPlist->getObject("CFBundleVersion"));
if (!kextVersion) {
IOLog("getVersionForKext(): "
"Extension \"%s\" has no \"CFBundleVersion\" property.\n",
kextName->getCStringNoCopy());
return false;
}
if (version) {
*version = (char *)kextVersion->getCStringNoCopy();
}
return true;
}
static
bool add_dependencies_for_kmod(const char * kmod_name, dgraph_t * dgraph)
{
bool result = true;
OSDictionary * kextPlist = 0; unsigned int index = 0;
OSArray * dependencyList = 0; unsigned char * code = 0;
unsigned long code_length = 0;
bool code_is_kmem = false;
char * kmod_vers = 0; char is_kernel_component = 0;
dgraph_entry_t * dgraph_entry = 0; dgraph_entry_t * dgraph_dependency = 0; bool kext_is_dependency = true;
#if CONFIG_MACF_KEXT
kmod_args_t user_data = 0;
mach_msg_type_number_t user_data_length;
#endif
if (!getKext(kmod_name, &kextPlist, &code, &code_length,
&code_is_kmem)) {
IOLog("can't find extension %s\n", kmod_name);
result = false;
goto finish;
}
if (!kextIsDependency(kmod_name, &is_kernel_component)) {
IOLog("extension %s is not loadable\n", kmod_name);
result = false;
goto finish;
}
if (!getVersionForKext(kextPlist, &kmod_vers)) {
IOLog("can't get version for extension %s\n", kmod_name);
result = false;
goto finish;
}
#if CONFIG_MACF_KEXT
user_data = get_module_data(kextPlist, &user_data_length);
#endif
dgraph_entry = dgraph_add_dependent(dgraph, kmod_name,
code, code_length, code_is_kmem,
#if CONFIG_MACF_KEXT
user_data, user_data_length,
#endif
kmod_name, kmod_vers,
0 , is_kernel_component);
if (!dgraph_entry) {
IOLog("can't record %s in dependency graph\n", kmod_name);
result = false;
goto finish;
}
if (code) {
if (kload_map_entry(dgraph_entry) != kload_error_none) {
IOLog("can't map %s in preparation for loading\n", kmod_name);
result = false;
goto finish;
}
}
code = 0;
code_length = 0;
code_is_kmem = false;
dependencyList = OSArray::withCapacity(5);
if (!dependencyList) {
IOLog("memory allocation failure\n");
result = false;
goto finish;
}
index = 0;
if (!addDependenciesForKext(kextPlist, dependencyList, NULL, false)) {
IOLog("can't determine immediate dependencies for extension %s\n",
kmod_name);
result = false;
goto finish;
}
for (index = 0; index < dependencyList->getCount(); index += 2) {
OSString * dependentName = 0;
OSString * libraryName = 0;
const char * dependent_name = 0;
const char * library_name = 0;
if (index > (2 * 255)) {
IOLog("extension dependency graph ridiculously long, indicating a loop\n");
result = false;
goto finish;
}
dependentName = OSDynamicCast(OSString,
dependencyList->getObject(index));
libraryName = OSDynamicCast(OSString,
dependencyList->getObject(index + 1));
if (!dependentName || !libraryName) {
IOLog("malformed dependency list\n");
result = false;
goto finish;
}
dependent_name = dependentName->getCStringNoCopy();
library_name = libraryName->getCStringNoCopy();
if (!getKext(library_name, &kextPlist, NULL, NULL, NULL)) {
IOLog("can't find extension %s\n", library_name);
result = false;
goto finish;
}
OSString * string = OSDynamicCast(OSString,
kextPlist->getObject("OSBundleSharedExecutableIdentifier"));
if (string) {
library_name = string->getCStringNoCopy();
if (!getKext(library_name, &kextPlist, NULL, NULL, NULL)) {
IOLog("can't find extension %s\n", library_name);
result = false;
goto finish;
}
}
kext_is_dependency = kextIsDependency(library_name,
&is_kernel_component);
if (kext_is_dependency) {
dgraph_entry = dgraph_find_dependent(dgraph, dependent_name);
if (!dgraph_entry) {
IOLog("internal error with dependency graph\n");
LOG_DELAY(1);
result = false;
goto finish;
}
if (!getVersionForKext(kextPlist, &kmod_vers)) {
IOLog("can't get version for extension %s\n", library_name);
result = false;
goto finish;
}
if (!getKext(library_name, NULL ,
&code, &code_length, &code_is_kmem)) {
IOLog("can't find extension %s\n", library_name);
result = false;
goto finish;
}
#if CONFIG_MACF_KEXT
user_data = get_module_data(kextPlist, &user_data_length);
#endif
dgraph_dependency = dgraph_add_dependency(dgraph, dgraph_entry,
library_name, code, code_length, code_is_kmem,
#if CONFIG_MACF_KEXT
user_data, user_data_length,
#endif
library_name, kmod_vers,
0 , is_kernel_component);
if (!dgraph_dependency) {
IOLog("can't record dependency %s -> %s\n", dependent_name,
library_name);
result = false;
goto finish;
}
if (code) {
if (kload_map_entry(dgraph_dependency) != kload_error_none) {
IOLog("can't map %s in preparation for loading\n", library_name);
result = false;
goto finish;
}
}
code = 0;
code_length = 0;
code_is_kmem = false;
}
if (!addDependenciesForKext(kextPlist, dependencyList,
kext_is_dependency ? NULL : dependentName, !kext_is_dependency)) {
IOLog("can't determine immediate dependencies for extension %s\n",
library_name);
result = false;
goto finish;
}
}
finish:
if (code && code_is_kmem) {
kmem_free(kernel_map, (unsigned int)code, code_length);
}
if (dependencyList) dependencyList->release();
#if CONFIG_MACF_KEXT
if (user_data && !result) {
vm_map_copy_discard((vm_map_copy_t)user_data);
}
#endif
return result;
}
__private_extern__
kern_return_t load_kernel_extension(char * kmod_name)
{
kern_return_t result = KERN_SUCCESS;
kload_error load_result = kload_error_none;
dgraph_t dgraph;
bool free_dgraph = false;
kmod_info_t * kmod_info;
#if 0
kload_set_log_level(kload_log_level_load_details);
#endif
if ((kmod_info = kmod_lookupbyname_locked(kmod_name))) {
kfree(kmod_info, sizeof(kmod_info_t));
return KERN_SUCCESS;
}
if (dgraph_init(&dgraph) != dgraph_valid) {
IOLog("Can't initialize dependency graph to load %s.\n",
kmod_name);
result = KERN_FAILURE;
goto finish;
}
free_dgraph = true;
if (!add_dependencies_for_kmod(kmod_name, &dgraph)) {
IOLog("Can't determine dependencies for %s.\n",
kmod_name);
result = KERN_FAILURE;
goto finish;
}
dgraph.root = dgraph_find_root(&dgraph);
if (!dgraph.root) {
IOLog("Dependency graph to load %s has no root.\n",
kmod_name);
result = KERN_FAILURE;
goto finish;
}
if (dgraph.root->is_kernel_component) {
result = KERN_SUCCESS;
goto finish;
}
dgraph_establish_load_order(&dgraph);
load_result = kload_load_dgraph(&dgraph);
if (load_result != kload_error_none &&
load_result != kload_error_already_loaded) {
IOLog(VTYELLOW "Failed to load extension %s.\n" VTRESET, kmod_name);
result = KERN_FAILURE;
goto finish;
}
finish:
if (free_dgraph) {
dgraph_free(&dgraph, 0 );
}
return result;
}
#define COM_APPLE "com.apple."
__private_extern__ void
load_security_extensions (void)
{
OSDictionary * extensionsDict = NULL; OSCollectionIterator* keyIterator = NULL; OSString * key = NULL; OSDictionary * extDict; OSDictionary * extPlist; OSBoolean * isSec = 0; Boolean ret;
extensionsDict = getStartupExtensions();
if (!extensionsDict) {
IOLog("startup extensions dictionary is missing\n");
LOG_DELAY(1);
return;
}
keyIterator = OSCollectionIterator::withCollection(extensionsDict);
if (!keyIterator) {
IOLog("Error: Failed to allocate iterator for extensions.\n");
LOG_DELAY(1);
return;
}
while ((key = OSDynamicCast(OSString, keyIterator->getNextObject()))) {
const char * bundle_id = key->getCStringNoCopy();
if (!bundle_id || (strncmp(bundle_id, COM_APPLE, strlen(COM_APPLE)) != 0)) {
continue;
}
extDict = OSDynamicCast(OSDictionary, extensionsDict->getObject(key));
if (!extDict) {
IOLog("extension \"%s\" cannot be found\n",
key->getCStringNoCopy());
continue;
}
extPlist = OSDynamicCast(OSDictionary, extDict->getObject("plist"));
if (!extPlist) {
IOLog("extension \"%s\" has no info dictionary\n",
key->getCStringNoCopy());
continue;
}
isSec = OSDynamicCast(OSBoolean,
extPlist->getObject("AppleSecurityExtension"));
if (isSec && isSec->isTrue()) {
printf("Loading security extension %s\n", key->getCStringNoCopy());
ret = kmod_load_request(key->getCStringNoCopy(), false);
if (!ret) {
load_kernel_extension((char *)key->getCStringNoCopy());
}
}
}
if (keyIterator)
keyIterator->release();
return;
}