#include #include #include #include #include #define ALLOW_PATCH_OUTPUT 0 #define ALLOW_NO_START 0 /******************************************************************************* * Global variables. *******************************************************************************/ static char * progname = "(unknown)"; extern char ** environ; #define KEXTD_LAUNCH "KEXTD_LAUNCH_USERID=" #define KEXTD_LAUNCH_FORMAT "KEXTD_LAUNCH_USERID=%d" #define KEXTD_AUTHORIZATION "KEXTD_AUTHORIZATION=" #define KEXTD_AUTHORIZATION_FORMAT "KEXTD_AUTHORIZATION=%d" /******************************************************************************* * Utility and callback functions. *******************************************************************************/ static Boolean check_file(const char * filename); static Boolean check_dir(const char * dirname, int writeable); static void verbose_log(const char * format, ...); static void error_log(const char * format, ...); static int user_approve(int default_answer, const char * format, ...); static const char * user_input(const char * format, ...); static Boolean addKextsToManager( KXKextManagerRef aManager, CFArrayRef kextNames, CFMutableArrayRef kextArray, Boolean do_tests); static void usage(int level); static CFDataRef createXMLDataForNonsecureKextload( int argc, const char ** argv, const char * kext_path); KXKextManagerError _KXKextRaiseSecurityAlert(KXKextRef aKext, uid_t euid, AuthorizationRef auth_ref); KXKextManagerError _KXKextMakeSecure(KXKextRef aKext); 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); extern const char * _KXKextCopyCanonicalPathnameAsCString(KXKextRef aKext); /******************************************************************************* *******************************************************************************/ int main(int argc, const char *argv[]) { int exit_code = 0; int failure_code = 0; // if any kext load fails during the loop int argc_mod = argc; int argc_opt_count = 0; const char ** argv_mod = argv; int optchar; KXKextManagerRef theKextManager = NULL; // must release CFURLRef kextURL = NULL; // must release KXKextManagerError result; CFIndex i, count; int approve; // for interactive mode queries /***** * Set by command line option flags. */ unsigned short int flag_n = 0; // used to sanity-check -n, -l, -m unsigned short int flag_l = 0; // before setting behavior-changing unsigned short int flag_m = 0; // variables do_load & do_start_matching Boolean get_addrs_from_kernel = false; // -A Boolean use_repository_caches = true; // -c to turn off Boolean skip_extensions_folder = false; // -e Boolean overwrite_symbols = true; // -i turns off int interactive_level = 0; // -i/-I turns on Boolean do_load = true; // -l is load but no matching Boolean do_start_kmod = true; // -j is load, don't start, // no matching Boolean do_start_matching = true; // -m is don't load but start // matching // -n is don't do either Boolean do_tests = false; // -t Boolean strict_auth = true; int verbose_level = 0; // -v Boolean safe_boot_mode = false; // -x Boolean pretend_authentic = false; // -z Boolean skip_dependencies = false; // -Z (and with -t only!) Boolean check_loaded_for_dependencies = true; // -D to turn off unsigned int addresses_cap = 10; unsigned int num_addresses = 0; char ** addresses = NULL; // -a; must free CFMutableArrayRef kextIDs = NULL; // -b; must release CFMutableArrayRef personalityNames = NULL; // -p; must release CFMutableArrayRef dependencyNames = NULL; // -d; must release CFMutableArrayRef repositoryDirectories = NULL; // -r; must release CFMutableArrayRef kextNames = NULL; // args; must release CFMutableArrayRef kextNamesToUse = NULL; // must release CFMutableArrayRef inauthenticKexts = NULL; // must release KXKextRef theKext = NULL; // don't release const char * default_kernel_file = "/mach"; const char * kernel_file = NULL; // overriden by -k option const char * symbol_dir = NULL; // set by -s option; // for writing debug files for kmods const char * patch_dir = NULL; // set by -P option; // for writing patchup files for kmods CFIndex inauthentic_kext_count = 0; CFIndex k = 0; char ** envp = NULL; Boolean kextd_launch = false; Boolean kextd_launch_with_ref = false; uid_t kextd_launch_userid = -1; AuthorizationRef kextd_authref = NULL; int kextd_auth_mbox = -1; AuthorizationExternalForm auth_ext_form; for (envp = environ; *envp; envp++) { char * env_string = *envp; if (!strncmp(env_string, KEXTD_LAUNCH, strlen(KEXTD_LAUNCH))) { kextd_launch = true; if (sscanf(env_string, KEXTD_LAUNCH_FORMAT, &kextd_launch_userid) != 1) { kextd_launch_userid = -1; } } if (!strncmp(env_string, KEXTD_AUTHORIZATION, strlen(KEXTD_AUTHORIZATION)) && (sscanf(env_string, KEXTD_AUTHORIZATION_FORMAT, &kextd_auth_mbox) == 1) && !lseek(kextd_auth_mbox, 0, SEEK_SET) && (read(kextd_auth_mbox, &auth_ext_form, sizeof(auth_ext_form)) == sizeof(auth_ext_form)) && (errAuthorizationSuccess == AuthorizationCreateFromExternalForm(&auth_ext_form, &kextd_authref))) { kextd_launch_with_ref = true; } } /***** * Find out what the program was invoked as. */ progname = rindex(argv[0], '/'); if (progname) { progname++; // go past the '/' } else { progname = (char *)argv[0]; } /***** * Allocate collection objects. */ addresses = (char **)malloc(addresses_cap * sizeof(char *)); if (!addresses) { exit_code = 1; fprintf(stderr, "memory allocation failure\n"); goto finish; } bzero(addresses, addresses_cap * sizeof(char *)); personalityNames = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks); if (!personalityNames) { exit_code = 1; fprintf(stderr, "memory allocation failure\n"); goto finish; } dependencyNames = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks); if (!dependencyNames) { exit_code = 1; fprintf(stderr, "memory allocation failure\n"); goto finish; } repositoryDirectories = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks); if (!repositoryDirectories) { exit_code = 1; fprintf(stderr, "memory allocation failure\n"); goto finish; } kextNames = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks); if (!kextNames) { exit_code = 1; fprintf(stderr, "memory allocation failure\n"); goto finish; } kextNamesToUse = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks); if (!kextNamesToUse) { exit_code = 1; fprintf(stderr, "memory allocation failure\n"); goto finish; } kextIDs = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks); if (!kextIDs) { exit_code = 1; fprintf(stderr, "memory allocation failure\n"); goto finish; } /***** * Process command line arguments. If running in kextload-compatibiliy * mode, accept its old set of options and none other. If running in * the new mode, process the new, larger set of options. */ // -j option currently masked out while ((optchar = getopt(argc, (char * const *)argv, "a:Ab:cd:DehiIk:lLmnp:P:r:s:tvxzZ")) != -1) { char * address_string = NULL; // don't free unsigned int address; CFStringRef optArg = NULL; // must release switch (optchar) { case 'a': flag_n = 1; // -a implies -n if (!optarg) { fprintf(stderr, "no argument for -a\n"); usage(0); exit_code = 1; goto finish; } address_string = index(optarg, '@'); if (!address_string) { fprintf(stderr, "invalid use of -a option\n\n"); usage(0); exit_code = 1; goto finish; } address_string++; address = strtoul(address_string, NULL, 16); if (!address) { fprintf(stderr, "address must be specified and non-zero\n\n"); usage(0); exit_code = 1; goto finish; } if (num_addresses >= addresses_cap) { addresses_cap *= 2; addresses = (char **)realloc(addresses, (addresses_cap * sizeof(char *))); if (!addresses) { fprintf(stderr, "memory allocation failure\n"); exit_code = 1; goto finish; } bzero(addresses + num_addresses, (addresses_cap-num_addresses) * sizeof(char *)); } addresses[num_addresses++] = optarg; break; case 'A': flag_n = 1; // -A implies -n get_addrs_from_kernel = true; break; case 'b': if (!optarg) { fprintf(stderr, "no argument for -b\n"); usage(0); exit_code = 1; goto finish; } optArg = CFStringCreateWithCString(kCFAllocatorDefault, optarg, kCFStringEncodingMacRoman); if (!optArg) { fprintf(stderr, "memory allocation failure\n"); exit_code = 1; goto finish; } CFArrayAppendValue(kextIDs, optArg); CFRelease(optArg); optArg = NULL; break; case 'c': use_repository_caches = false; break; case 'd': if (!optarg) { fprintf(stderr, "no argument for -d\n"); usage(0); exit_code = 1; goto finish; } optArg = CFStringCreateWithCString(kCFAllocatorDefault, optarg, kCFStringEncodingMacRoman); if (!optArg) { fprintf(stderr, "memory allocation failure\n"); exit_code = 1; goto finish; } CFArrayAppendValue(dependencyNames, optArg); CFRelease(optArg); optArg = NULL; break; case 'D': check_loaded_for_dependencies = false; break; case 'e': skip_extensions_folder = true; break; case 'h': usage(2); goto finish; break; case 'i': if (interactive_level) { fprintf(stderr, "use only one of -i or -I\n\n"); usage(0); exit_code = 1; goto finish; } overwrite_symbols = false; interactive_level = 1; break; case 'I': if (interactive_level) { fprintf(stderr, "use only one of -i or -I\n\n"); usage(0); exit_code = 1; goto finish; } overwrite_symbols = false; interactive_level = 2; break; #if ALLOW_NO_START case 'j': do_load = true; do_start_kmod = false; do_start_matching = false; break; #endif ALLOW_NO_START case 'k': if (kernel_file) { fprintf(stderr, "duplicate use of -k option\n\n"); usage(0); exit_code = 1; goto finish; } if (!optarg) { fprintf(stderr, "no argument for -k\n"); usage(0); exit_code = 1; goto finish; } kernel_file = optarg; break; case 'l': flag_l = 1; break; // see case 'r' for case 'L' case 'm': flag_m = 1; break; case 'n': flag_n = 1; break; case 'p': if (!optarg) { fprintf(stderr, "no argument for -p\n"); usage(0); exit_code = 1; goto finish; } optArg = CFStringCreateWithCString(kCFAllocatorDefault, optarg, kCFStringEncodingMacRoman); if (!optArg) { fprintf(stderr, "memory allocation failure\n"); exit_code = 1; goto finish; } CFArrayAppendValue(personalityNames, optArg); CFRelease(optArg); optArg = NULL; break; #if ALLOW_PATCH_OUTPUT case 'P': if (patch_dir) { fprintf(stderr, "duplicate use of -P option\n\n"); usage(0); exit_code = 1; goto finish; } if (!optarg) { fprintf(stderr, "no argument for -P\n"); usage(0); exit_code = 1; goto finish; } patch_dir = optarg; break; #endif case 'r': /* fall through */ case 'L': if (!optarg) { fprintf(stderr, "no argument for -%c\n", optchar); usage(0); exit_code = 1; goto finish; } optArg = CFStringCreateWithCString(kCFAllocatorDefault, optarg, kCFStringEncodingMacRoman); if (!optArg) { fprintf(stderr, "memory allocation failure\n"); exit_code = 1; goto finish; } CFArrayAppendValue(repositoryDirectories, optArg); CFRelease(optArg); optArg = NULL; break; case 's': if (symbol_dir) { fprintf(stderr, "duplicate use of -s option\n\n"); usage(0); exit_code = 1; goto finish; } if (!optarg) { fprintf(stderr, "no argument for -s\n"); usage(0); exit_code = 1; goto finish; } symbol_dir = optarg; break; case 't': do_tests = true; break; case 'v': { const char * next; if (verbose_level > 0) { fprintf(stderr, "duplicate use of -v option\n\n"); usage(0); exit_code = 1; goto finish; } if (optind >= argc) { verbose_level = 1; } else { next = argv[optind]; if ((next[0] == '1' || next[0] == '2' || next[0] == '3' || next[0] == '4' || next[0] == '5' || next[0] == '6') && next[1] == '\0') { verbose_level = atoi(next); optind++; } else { verbose_level = 1; } } } break; case 'x': safe_boot_mode = true; use_repository_caches = false; // -x implies -c break; case 'z': pretend_authentic = true; break; case 'Z': skip_dependencies = true; break; default: fprintf(stderr, "unknown option -%c\n", optchar); usage(0); exit_code = 1; goto finish; } } /* Update argc, argv based on option processing. */ argc_mod = argc - optind; argc_opt_count = optind; argv_mod = argv + optind; /***** * Check for bad combinations of options. */ if (flag_l + flag_m + flag_n > 1) { fprintf(stderr, "only one of -l/-m/-n is allowed" " (-a and -A imply -n)\n\n"); usage(0); exit_code = 1; goto finish; } else if (flag_l) { do_load = true; do_start_matching = false; } else if (flag_m) { do_load = false; do_start_matching = true; } else if (flag_n) { do_load = false; do_start_matching = false; } if (do_load && geteuid() != 0) { fprintf(stderr, "you must be running as root " "to load modules into the kernel\n"); exit_code = 1; goto finish; } if (num_addresses > 0 && get_addrs_from_kernel) { fprintf(stderr, "don't use -a with -A\n"); usage(0); exit_code = 1; goto finish; } if (num_addresses > 0 && (do_load || do_start_matching)) { fprintf(stderr, "don't use -a with -l or -m\n"); usage(0); exit_code = 1; goto finish; } if (get_addrs_from_kernel && (do_load || do_start_matching)) { fprintf(stderr, "don't use -A with -l or -m\n"); usage(0); exit_code = 1; goto finish; } // FIXME: Is this a valid restriction? The default kernel_file // FIXME: ...is /mach, which is written out by the kernel during // FIXME: ...boot, and always represents the currently running kernel. // if (kernel_file && (do_load || do_start_matching)) { fprintf(stderr, "use -k only with -n\n"); usage(0); exit_code = 1; goto finish; } /***** * Make sure if we're just doing debug symbols that we don't * load or start matching (-a/-A imply -n). */ if (num_addresses > 0 || get_addrs_from_kernel) { do_load = 0; do_start_matching = 0; } if (pretend_authentic && do_load) { fprintf(stderr, "-z is only allowed when not loading\n"); usage(0); exit_code = 1; goto finish; } /**** * If we're getting addresses from the kernel we have to call * down there, so we might as well check what's loaded before * resolving dependencies too (-A overrides -D). If we're not * performing a load, then don't check (-n/-m implies -D). */ if (get_addrs_from_kernel) { check_loaded_for_dependencies = true; } else if (!do_load) { check_loaded_for_dependencies = false; } /***** * If we're not loading and have no request to emit a symbol * or patch file, there's nothing to do! */ if (!do_tests && !do_load && !do_start_matching && !symbol_dir && !patch_dir) { fprintf(stderr, "no work to do; check your options\n\n"); usage(0); exit_code = 1; goto finish; } /***** * -Z is only allowed if you don't need to generate dependencies. */ if (skip_dependencies && (!do_tests || do_load || symbol_dir || patch_dir)) { #if ALLOW_PATCH_OUTPUT fprintf(stderr, "use -Z only with -nt and not with -s or -P\n"); #else fprintf(stderr, "use -Z only with -nt and not with -s\n"); #endif ALLOW_PATCH_OUTPUT usage(0); exit_code = 1; goto finish; } /***** * Record the kext names from the command line. */ for (i = 0; i < argc_mod; i++) { CFStringRef kextName = CFStringCreateWithCString(kCFAllocatorDefault, argv_mod[i], kCFStringEncodingMacRoman); if (!kextName) { fprintf(stderr, "memory allocation failure\n"); exit_code = 1; goto finish; } CFArrayAppendValue(kextNames, kextName); CFRelease(kextName); } if (CFArrayGetCount(kextNames) == 0 && CFArrayGetCount(kextIDs) == 0) { fprintf(stderr, "no kernel extension specified\n\n"); usage(0); exit_code = 1; goto finish; } /***** * Make sure we scan the standard Extensions folder unless told explicitly * not to. */ if (!skip_extensions_folder) { CFArrayInsertValueAtIndex(repositoryDirectories, 0, kKXSystemExtensionsFolder); } /***** * Make sure the kernel file, symbol directory, and patch directory * exist and are readable/writable. */ if (!kernel_file) { kernel_file = default_kernel_file; if (!do_load && verbose_level >= 1) { verbose_log("no kernel file specified; using %s", kernel_file); } } if (!check_file(kernel_file)) { // check_file() prints an error message exit_code = 1; goto finish; } if (symbol_dir) { if (!check_dir(symbol_dir, 1)) { // check_dir() prints an error message exit_code = 1; goto finish; } } if (patch_dir) { if (!check_dir(patch_dir, 1)) { // check_dir() prints an error message exit_code = 1; goto finish; } } /***** * Set up the kext manager. */ theKextManager = KXKextManagerCreate(kCFAllocatorDefault); if (!theKextManager) { fprintf(stderr, "can't allocate kernel extension manager\n"); exit_code = 1; goto finish; } result = KXKextManagerInit(theKextManager, true /* load_in_task */, safe_boot_mode); if (result != kKXKextManagerErrorNone) { fprintf(stderr, "can't initialize kernel extension manager (%s)\n", KXKextManagerErrorStaticCStringForError(result)); exit_code = 1; goto finish; } KXKextManagerSetPerformsFullTests(theKextManager, do_tests); KXKextManagerSetPerformsStrictAuthentication(theKextManager, strict_auth); KXKextManagerSetLogLevel(theKextManager, verbose_level); KXKextManagerSetLogFunction(theKextManager, &verbose_log); KXKextManagerSetErrorLogFunction(theKextManager, &error_log); KXKextManagerSetUserVetoFunction(theKextManager, &user_approve); KXKextManagerSetUserApproveFunction(theKextManager, &user_approve); KXKextManagerSetUserInputFunction(theKextManager, &user_input); /***** * Disable clearing of relationships until we're done putting everything * together. */ KXKextManagerDisableClearRelationships(theKextManager); /***** * Add the dependency kexts to the manager. Do this *before* adding * whole repositories; the kext manager searches for duplicate versions * in order of addition, so we want explicitly named dependencies to * be found first. (This won't help a bit if a later version is in a * repository, of course; the user has to know how to use the program, * after all.) */ if (!addKextsToManager(theKextManager, dependencyNames, NULL, do_tests)) { exit_code = 1; goto finish; } /***** * Add the extensions folders specified with -r to the manager. */ count = CFArrayGetCount(repositoryDirectories); for (i = 0; i < count; i++) { CFStringRef directory = (CFStringRef)CFArrayGetValueAtIndex( repositoryDirectories, i); CFURLRef directoryURL = CFURLCreateWithFileSystemPath(kCFAllocatorDefault, directory, kCFURLPOSIXPathStyle, true); if (!directoryURL) { fprintf(stderr, "memory allocation failure\n"); exit_code = 1; goto finish; } result = KXKextManagerAddRepositoryDirectory(theKextManager, directoryURL, true /* scanForKexts */, use_repository_caches, NULL); if (result != kKXKextManagerErrorNone) { fprintf(stderr, "can't add repository (%s).\n", KXKextManagerErrorStaticCStringForError(result)); exit_code = 1; goto finish; } CFRelease(directoryURL); directoryURL = NULL; } /***** * Add each kext named on the command line to the manager. */ if (!addKextsToManager(theKextManager, kextNames, kextNamesToUse, do_tests)) { exit_code = 1; goto finish; } /***** * Either fake authentication, or if doing all tests then authenticate * everything. (It may not be necessary to do this for * *everything*; might be able to just do it on the named kexts.) */ if (pretend_authentic) { // Yes, do this even if do_tests is true; -tz means fake authentication. KXKextManagerMarkKextsAuthentic(theKextManager); } else if (do_tests) { KXKextManagerAuthenticateKexts(theKextManager); } KXKextManagerEnableClearRelationships(theKextManager); /***** * If doing full tests, assemble the whole database of version and * dependency relationships. (It may not be necessary to do this for * *everything*; might be able to just do it on the named kexts.) */ if (do_tests) { KXKextManagerCalculateVersionRelationships(theKextManager); if (!skip_dependencies) { KXKextManagerResolveAllKextDependencies(theKextManager); } } /***** * If we got CFBundleIdentifiers, then look them up. We just add to the * kextNamesToUse array here. */ count = CFArrayGetCount(kextIDs); for (i = 0; i < count; i++) { CFStringRef thisKextID = (CFStringRef) CFArrayGetValueAtIndex(kextIDs, i); KXKextRef theKext = KXKextManagerGetKextWithIdentifier( theKextManager, thisKextID); CFStringRef kextName = NULL; // must release char name_buffer[255]; if (!CFStringGetCString(thisKextID, name_buffer, sizeof(name_buffer) - 1, kCFStringEncodingMacRoman)) { fprintf(stderr, "internal error; no memory?\n"); exit_code = 1; goto finish; } if (verbose_level >= 1) { verbose_log("looking up extension with identifier %s\n", name_buffer); } if (!theKext) { if (!CFStringGetCString(thisKextID, name_buffer, sizeof(name_buffer) - 1, kCFStringEncodingMacRoman)) { fprintf(stderr, "internal error; no memory?\n"); exit_code = 1; goto finish; } fprintf(stderr, "can't find extension with identifier %s\n", name_buffer); exit_code = 1; goto finish; } kextName = KXKextCopyAbsolutePath(theKext); if (!kextName) { fprintf(stderr, "memory allocation failure\n"); exit_code = 1; goto finish; } if (!CFStringGetCString(kextName, name_buffer, sizeof(name_buffer) - 1, kCFStringEncodingMacRoman)) { fprintf(stderr, "internal error; no memory?\n"); exit_code = 1; goto finish; } if (verbose_level >= 1) { verbose_log("found extension bundle %s", name_buffer); } CFArrayAppendValue(kextNamesToUse, kextName); CFRelease(kextName); } /***** * Get busy loading kexts. */ count = CFArrayGetCount(kextNamesToUse); for (i = 0; i < count; i++) { CFStringRef kextName = NULL; // don't release char kext_name[MAXPATHLEN]; unsigned short cache_retry_count = 1; /***** * If the last iteration flipped failure_code, make the exit_code * nonzero and reset failure_code for this iteration. */ if (failure_code) { exit_code = 1; failure_code = 0; } retry: if (inauthenticKexts) { CFRelease(inauthenticKexts); inauthenticKexts = NULL; } inauthenticKexts = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks); if (!inauthenticKexts) { exit_code = 1; fprintf(stderr, "memory allocation failure\n"); goto finish; } if (kextURL) { CFRelease(kextURL); kextURL = NULL; } kextName = CFArrayGetValueAtIndex(kextNamesToUse, i); if (!CFStringGetCString(kextName, kext_name, sizeof(kext_name) - 1, kCFStringEncodingMacRoman)) { fprintf(stderr, "memory allocation or string conversion failure\n"); exit_code = 1; goto finish; } kextURL = CFURLCreateWithFileSystemPath(kCFAllocatorDefault, kextName, kCFURLPOSIXPathStyle, true); if (!kextURL) { fprintf(stderr, "memory allocation failure\n"); exit_code = 1; goto finish; } theKext = KXKextManagerGetKextWithURL(theKextManager, kextURL); if (!theKext) { fprintf(stderr, "can't find extension %s in database\n", kext_name); failure_code = 1; continue; } /***** * Confirm kext is loadable. */ if (safe_boot_mode && !KXKextIsEligibleDuringSafeBoot(theKext)) { fprintf(stderr, "extension %s is not eligible for safe boot " "(has no OSBundleRequired setting, " "or all personalities have nonzero IOKitDebug setting)\n", kext_name); failure_code = 1; // keep going here to print diagnostics and hit the "continue" below } // don't offer to fix or load a kext if run in diagnostic mode. If a // dependency is nonsecure, then checking here would really involve too // much work. if (!KXKextIsValid(theKext) || (do_tests && ( (!pretend_authentic && !KXKextIsAuthentic(theKext)) || (!skip_dependencies && !KXKextGetHasAllDependencies(theKext)) ))) { fprintf(stderr, "kernel extension %s has problems", kext_name); if (do_tests) { fprintf(stderr, ":\n"); KXKextPrintDiagnostics(theKext, stderr); } else { fprintf(stderr, " (run %s with -t for diagnostic output)\n", progname); } failure_code = 1; } if (failure_code) { continue; } if (do_tests || verbose_level > 0) { verbose_log("extension %s appears to be valid", kext_name); } if (KXKextHasDebugProperties(theKext)) { verbose_log("notice: extension %s has debug properties set", kext_name); } /* If there's nothing else to do for this kext, then move on * to the next one. */ if (!do_load && !do_start_matching && !patch_dir && !symbol_dir) { continue; } /***** * If requested, get user approval to continue. */ if (interactive_level > 0 && do_load) { approve = user_approve(1, "Load extension %s and its dependencies", kext_name); if (approve < 0) { fprintf(stderr, "error reading response\n"); failure_code = 1; goto finish; } else if (approve == 0) { fprintf(stderr, "not loading extension %s\n", kext_name); continue; } } if (failure_code) { continue; } result = _KXKextManagerPrepareKextForLoading( theKextManager, theKext, kext_name, check_loaded_for_dependencies, do_load, inauthenticKexts); if (result == kKXKextManagerErrorAlreadyLoaded || result == kKXKextManagerErrorLoadedVersionDiffers) { // this is not considered a failure verbose_log("extension %s is already loaded", kext_name); continue; } else if (result != kKXKextManagerErrorNone && result != kKXKextManagerErrorAuthentication) { failure_code = 1; exit_code = 1; continue; } inauthentic_kext_count = CFArrayGetCount(inauthenticKexts); if (inauthentic_kext_count) { for (k = 0; k < inauthentic_kext_count; k++) { uid_t login_euid = -1; KXKextRef thisKext = (KXKextRef)CFArrayGetValueAtIndex(inauthenticKexts, k); if (kextd_launch) { login_euid = kextd_launch_userid; } else { login_euid = _KextManagerGetLoggedInUserid(); } if (login_euid == -1) { if (!kextd_launch) { const char * kext_path = NULL; // must free CFDataRef xmlData = NULL; // must release const char * rpc_data = NULL; // don't free size_t data_length = 0; kext_path = _KXKextCopyCanonicalPathnameAsCString(thisKext); if (!kext_path) { fprintf(stderr, "memory allocation failure\n"); exit_code = 1; goto finish; } xmlData = createXMLDataForNonsecureKextload( argc_opt_count, argv, kext_path); if (!xmlData) { fprintf(stderr, "memory allocation failure\n"); exit_code = 1; goto finish; } rpc_data = CFDataGetBytePtr(xmlData); data_length = CFDataGetLength(xmlData); fprintf(stderr, "no user logged in, can't offer to fix kext %s; " "informing kextd\n", kext_name); _KextManagerRecordNonsecureKextload(rpc_data, data_length); if (kext_path) free((char *)kext_path); if (xmlData) CFRelease(xmlData); failure_code = 1; break; } } else { const char * kext_path = NULL; // must free CFDataRef xmlData = NULL; // must release const char * rpc_data = NULL; // don't free size_t data_length = 0; kext_path = _KXKextCopyCanonicalPathnameAsCString(thisKext); if (!kext_path) { fprintf(stderr, "memory allocation failure\n"); exit_code = 1; goto finish; } /* Raise the security alert. If this fails on an IPC error, * tell kextd using _KextManagerRecordNonsecureKext(), * just in case. */ if (!kextd_launch_with_ref) AuthorizationCreate(NULL, NULL, 0, &kextd_authref); switch ( _KXKextRaiseSecurityAlert(thisKext, login_euid, kextd_authref)) { case kKXKextManagerErrorNone: break; case kKXKextManagerErrorIPC: if (!kextd_launch) { xmlData = createXMLDataForNonsecureKextload( argc_opt_count, argv, kext_path); if (!xmlData) { fprintf(stderr, "memory allocation failure\n"); exit_code = 1; goto finish; } rpc_data = CFDataGetBytePtr(xmlData); data_length = CFDataGetLength(xmlData); _KextManagerRecordNonsecureKextload(rpc_data, data_length); if (xmlData) CFRelease(xmlData); } break; case kKXKextManagerErrorUserAbort: fprintf(stderr, "load aborted for extension %s\n", kext_name); /* fall through */ default: failure_code = 1; break; } if (kext_path) free((char *)kext_path); if (failure_code) { break; /* for loop */ } } } } if (failure_code) { continue; } /***** * We can't load a kext that has no code (or can we?). */ if ( (do_load || patch_dir || symbol_dir) && KXKextGetDeclaresExecutable(theKext)) { result = _KXKextManagerLoadKextUsingOptions( theKextManager, theKext, kext_name, kernel_file, patch_dir, symbol_dir, do_load, do_start_kmod, interactive_level, (interactive_level > 0) /* ask overwrite symbols */, overwrite_symbols, get_addrs_from_kernel, num_addresses, addresses); if (result == kKXKextManagerErrorInvalidArgument) { failure_code = 1; continue; } else if (result == kKXKextManagerErrorAlreadyLoaded || result == kKXKextManagerErrorLoadedVersionDiffers) { // this is not considered a failure verbose_log("extension %s is already loaded", kext_name); continue; } else if (result == kKXKextManagerErrorUserAbort) { if (do_load) { fprintf(stderr, "load aborted for extension %s\n", kext_name); } // user abort is not a failure continue; } else if (result == kKXKextManagerErrorCache) { if (cache_retry_count == 0) { continue; } if (interactive_level > 0 ) { approve = user_approve(1, "Cache inconsistency for %s; rescan and try again", kext_name); if (approve < 0) { fprintf(stderr, "error reading response\n"); failure_code = 1; goto finish; } else if (approve == 0) { fprintf(stderr, "skipping extension %s\n", kext_name); continue; } } else { fprintf(stderr, "rescanning all kexts due to cache inconsistency\n"); } KXKextManagerResetAllRepositories(theKextManager); cache_retry_count--; goto retry; } else if (result != kKXKextManagerErrorNone) { if (do_load) { fprintf(stderr, "load failed for extension %s\n", kext_name); } if (do_tests && !KXKextIsLoadable(theKext, safe_boot_mode)) { fprintf(stderr, "kernel extension problems:\n"); KXKextPrintDiagnostics(theKext, stderr); } else { fprintf(stderr, " (run %s with -t for diagnostic output)\n", progname); } failure_code = 1; continue; } if (do_load) { verbose_log("%s loaded successfully", kext_name); } } else if (do_load && verbose_level >= 1) { verbose_log("extension %s has no executable", kext_name); // FIXME: Is this an error? } if (failure_code) { continue; } /***** * Send the personalities down to the kernel. */ if (do_start_matching && KXKextHasPersonalities(theKext)) { result = KXKextManagerSendKextPersonalitiesToCatalog( theKextManager, theKext, personalityNames, (interactive_level > 0), safe_boot_mode); if (result == kKXKextManagerErrorNone && (interactive_level || verbose_level >= 1) ) { verbose_log("matching started for %s", kext_name); } else if (result != kKXKextManagerErrorNone) { failure_code = 1; if (interactive_level || verbose_level >= 1) { verbose_log("start matching failed for %s", kext_name); } } } if (failure_code) { continue; } } if (failure_code) { exit_code = 1; } finish: /***** * Clean everything up. */ AuthorizationFree(kextd_authref, 0); if (kextd_auth_mbox >= 0) close (kextd_auth_mbox); if (addresses) free(addresses); if (repositoryDirectories) CFRelease(repositoryDirectories); if (dependencyNames) CFRelease(dependencyNames); if (personalityNames) CFRelease(personalityNames); if (kextNames) CFRelease(kextNames); if (kextNamesToUse) CFRelease(kextNamesToUse); if (kextIDs) CFRelease(kextIDs); if (inauthenticKexts) CFRelease(inauthenticKexts); if (theKextManager) CFRelease(theKextManager); exit(exit_code); return exit_code; } /******************************************************************************* * check_file() * * This function makes sure that a given file exists, is a regular file, and * is readable. *******************************************************************************/ static Boolean check_file(const char * filename) { Boolean result = true; // assume success struct stat stat_buf; if (stat(filename, &stat_buf) != 0) { perror(filename); result = false; goto finish; } if ( !(stat_buf.st_mode & S_IFREG) ) { fprintf(stderr, "%s is not a regular file\n", filename); result = false; goto finish; } if (access(filename, R_OK) != 0) { fprintf(stderr, "%s is not readable\n", filename); result = false; goto finish; } finish: return result; } /******************************************************************************* * check_dir() * * This function makes sure that a given directory exists, and is writeable. *******************************************************************************/ static Boolean check_dir(const char * dirname, int writeable) { int result = true; // assume success struct stat stat_buf; if (stat(dirname, &stat_buf) != 0) { perror(dirname); result = false; goto finish; } if ( !(stat_buf.st_mode & S_IFDIR) ) { fprintf(stderr, "%s is not a directory\n", dirname); result = false; goto finish; } if (writeable) { if (access(dirname, W_OK) != 0) { fprintf(stderr, "%s is not writeable\n", dirname); result = false; goto finish; } } finish: return result; } /******************************************************************************* * verbose_log() * * Print a log message prefixed with the name of the program. *******************************************************************************/ static void verbose_log(const char * format, ...) { va_list ap; char fake_buffer[2]; int output_length; char * output_string; va_start(ap, format); output_length = vsnprintf(fake_buffer, 1, format, ap); va_end(ap); output_string = (char *)malloc(output_length + 1); if (!output_string) { fprintf(stderr, "memory allocation failure\n"); return; } va_start(ap, format); vsprintf(output_string, format, ap); va_end(ap); va_start(ap, format); fprintf(stdout, "%s: %s\n", progname, output_string); va_end(ap); fflush(stdout); free(output_string); return; } /******************************************************************************* * error_log() * * Print an error message prefixed with the name of the program. *******************************************************************************/ static void error_log(const char * format, ...) { va_list ap; char fake_buffer[2]; int output_length; char * output_string; va_start(ap, format); output_length = vsnprintf(fake_buffer, 1, format, ap); va_end(ap); output_string = (char *)malloc(output_length + 1); if (!output_string) { fprintf(stderr, "memory allocation failure\n"); return; } va_start(ap, format); vsprintf(output_string, format, ap); va_end(ap); va_start(ap, format); fprintf(stderr, "%s: %s\n", progname, output_string); va_end(ap); fflush(stderr); free(output_string); return; } /******************************************************************************* * user_approve() * * Ask the user a question and wait for a yes/no answer. *******************************************************************************/ static int user_approve(int default_answer, const char * format, ...) { int result = 1; va_list ap; char fake_buffer[2]; int output_length; char * output_string; char * prompt_string = NULL; int c, x; va_start(ap, format); output_length = vsnprintf(fake_buffer, 1, format, ap); va_end(ap); output_string = (char *)malloc(output_length + 1); if (!output_string) { fprintf(stderr, "memory allocation failure\n"); result = -1; goto finish; } va_start(ap, format); vsprintf(output_string, format, ap); va_end(ap); prompt_string = default_answer ? " [Y/n]" : " [y/N]"; while ( 1 ) { fprintf(stdout, "%s%s%s", output_string, prompt_string, "? "); fflush(stdout); c = fgetc(stdin); if (c == EOF) { result = -1; goto finish; } /* Make sure we get a newline. */ if ( c != '\n' ) { do { x = fgetc(stdin); } while (x != '\n' && x != EOF); if (x == EOF) { result = -1; goto finish; } } if (c == '\n') { result = default_answer ? 1 : 0; goto finish; } else if (tolower(c) == 'y') { result = 1; goto finish; } else if (tolower(c) == 'n') { result = 0; goto finish; } } finish: if (output_string) free(output_string); return result; } /******************************************************************************* * user_input() * * Ask the user for input. *******************************************************************************/ static const char * user_input(const char * format, ...) { char * result = NULL; // return value va_list ap; char fake_buffer[2]; int output_length; char * output_string = NULL; unsigned index; size_t size = 80; int c; result = (char *)malloc(size); if (!result) { goto finish; } index = 0; va_start(ap, format); output_length = vsnprintf(fake_buffer, 1, format, ap); va_end(ap); output_string = (char *)malloc(output_length + 1); if (!output_string) { fprintf(stderr, "memory allocation failure\n"); result = NULL; goto finish; } va_start(ap, format); vsprintf(output_string, format, ap); va_end(ap); fprintf(stdout, "%s ", output_string); fflush(stdout); c = fgetc(stdin); while (c != '\n' && c != EOF) { if (index >= size) { size += 80; result = realloc(result, size); if (!result) { goto finish; } } result[index++] = (char)c; c = fgetc(stdin); } if (c == EOF) { if (result) free(result); result = NULL; goto finish; } finish: if (output_string) free(output_string); return result; } /******************************************************************************* * addKextsToManager() * * Add the kexts named in the kextNames array to the given kext manager, and * put their names into the kextNamesToUse. *******************************************************************************/ static Boolean addKextsToManager( KXKextManagerRef aManager, CFArrayRef kextNames, CFMutableArrayRef kextNamesToUse, Boolean do_tests) { Boolean result = true; // assume success KXKextManagerError kxresult = kKXKextManagerErrorNone; CFIndex i, count; KXKextRef theKext = NULL; // don't release CFURLRef kextURL = NULL; // must release /***** * Add each kext named to the manager. */ count = CFArrayGetCount(kextNames); for (i = 0; i < count; i++) { char name_buffer[MAXPATHLEN]; CFStringRef kextName = (CFStringRef)CFArrayGetValueAtIndex( kextNames, i); if (kextURL) { CFRelease(kextURL); kextURL = NULL; } kextURL = CFURLCreateWithFileSystemPath(kCFAllocatorDefault, kextName, kCFURLPOSIXPathStyle, true); if (!kextURL) { fprintf(stderr, "memory allocation failure\n"); result = false; goto finish; } if (!CFStringGetCString(kextName, name_buffer, sizeof(name_buffer) - 1, kCFStringEncodingMacRoman)) { fprintf(stderr, "memory allocation or string conversion error\n"); result = false; goto finish; } kxresult = KXKextManagerAddKextWithURL(aManager, kextURL, true, &theKext); if (kxresult != kKXKextManagerErrorNone) { fprintf(stderr, "can't add kernel extension %s (%s)", name_buffer, KXKextManagerErrorStaticCStringForError(kxresult)); fprintf(stderr, " (run %s on this kext with -t for diagnostic output)\n", progname); #if 0 if (do_tests && theKext) { fprintf(stderr, "kernel extension problems:\n"); KXKextPrintDiagnostics(theKext, stderr); } continue; #endif 0 } if (kextNamesToUse && theKext && (kxresult == kKXKextManagerErrorNone || do_tests)) { CFArrayAppendValue(kextNamesToUse, kextName); } } finish: if (kextURL) CFRelease(kextURL); return result; } /******************************************************************************* * usage() *******************************************************************************/ static void usage(int level) { fprintf(stderr, "usage: %s [-h] [-v [1-6]] [-t [-Z]] [-i | -I] [-x] [-z] [-e] [-c] [-D]\n" " [-k kernel_file] [-d extension] ... [-r directory] ...\n" // -j currently masked out of code " [-l | -m | -n | -A | -a kext_id@address ...] [-s directory]\n" " [-p personality] ... [-b bundle_id] ... [--] [extension] ...\n" "\n", progname); if (level < 1) { return; } if (level == 1) { fprintf(stderr, "use %s -h for an explanation of each option\n", progname); return; } fprintf(stderr, " extension: the kext bundle to load\n"); fprintf(stderr, " -a kext_id@address: kext_id is loaded at address\n"); fprintf(stderr, " -A: get load addresses for all kexts from those in the kernel\n"); fprintf(stderr, " -b bundle_id: load/use the kext whose CFBundleIdentifier is " "bundle_id\n"); fprintf(stderr, " -c: don't use repository caches; scan repository folders\n"); fprintf(stderr, " -d extension: consider extension as a candidate dependency\n"); fprintf(stderr, " -D: don't check for loaded kexts when resolving " "dependencies\n"); fprintf(stderr, " -e: don't examine /System/Library/Extensions\n"); fprintf(stderr, " -h: print this message\n"); fprintf(stderr, " -i: interactive mode\n"); fprintf(stderr, " -I: interactive mode for extension and all its dependencies\n"); #if ALLOW_NO_START fprintf(stderr, " -j: just load; don't even start the extension running\n"); #endif fprintf(stderr, " -k kernel_file: link against kernel_file (default is /mach)\n"); fprintf(stderr, " -l: load & start only; don't start matching\n"); fprintf(stderr, " -L: same as -r (remains for backward compatibility)\n"); fprintf(stderr, " -m: don't load but do start matching\n"); fprintf(stderr, " -n: neither load nor start matching\n"); fprintf(stderr, " -p personality: send the named personality to the catalog\n"); #if ALLOW_PATCH_OUTPUT fprintf(stderr, " -P directory: write the patched binary file into directory\n"); #endif ALLOW_PATCH_OUTPUT fprintf(stderr, " -r directory: use directory as a repository of dependency kexts\n"); fprintf(stderr, " -s directory: write symbol files for all kexts into directory\n"); fprintf(stderr, " -t: perform all possible tests and print a report on " "the extension\n"); fprintf(stderr, " -v: verbose mode; print info about load process\n"); fprintf(stderr, " -x: run in safe boot mode (only qualified kexts will load)\n"); fprintf(stderr, " -z: don't authenticate kexts (for use during development)\n"); fprintf(stderr, " -Z: don't check dependencies when diagnosing with -nt\n"); fprintf(stderr, " --: end of options\n"); return; } /******************************************************************************* * createXMLDataForNonsecureKextload() *******************************************************************************/ static CFDataRef createXMLDataForNonsecureKextload( int argc, const char ** argv, const char * kext_path) { char * working_dir = NULL; // must free CFDataRef xmlData = NULL; // returned, caller must release CFMutableDictionaryRef dataDictionary = NULL; // must release CFDataRef dataValue = NULL; // scratch value, must release CFStringRef dataKey = NULL; // scratch key, must release CFMutableArrayRef argvArray = NULL; // must release int i; dataDictionary = CFDictionaryCreateMutable( kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); if (!dataDictionary) { goto finish; } argvArray = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks); if (!argvArray) { goto finish; } /* Add the working directory for kextload to the dictionary. */ working_dir = getcwd(NULL, 0); if (!working_dir) { goto finish; } dataValue = CFDataCreate(kCFAllocatorDefault, working_dir, 1 + strlen(working_dir)); if (!dataValue) { goto finish; } CFDictionarySetValue(dataDictionary, CFSTR("workingDir"), dataValue); CFRelease(dataValue); dataValue = NULL; /* Add the path to kextload as argv[0]. */ dataValue = CFDataCreate(kCFAllocatorDefault, "/sbin/kextload", 1 + strlen("/sbin/kextload")); if (!dataValue) { goto finish; } CFArrayAppendValue(argvArray, dataValue); CFRelease(dataValue); dataValue = NULL; /* Add the command line options used for this kextload invocation. */ for (i = 1 /* skip slot 0! */; i < argc; i++) { if (!strcmp(argv[i], "-b")) { i++; // skip -b and its arg continue; } dataValue = CFDataCreate(kCFAllocatorDefault, argv[i], 1 + strlen(argv[i])); if (!dataValue) { goto finish; } CFArrayAppendValue(argvArray, dataValue); CFRelease(dataValue); dataValue = NULL; } /* Add the kext to load. */ dataValue = CFDataCreate(kCFAllocatorDefault, kext_path, 1 + strlen(kext_path)); if (!dataValue) { goto finish; } CFArrayAppendValue(argvArray, dataValue); CFRelease(dataValue); dataValue = NULL; CFDictionarySetValue(dataDictionary, CFSTR("argv"), argvArray); CFRelease(argvArray); argvArray = NULL; /* Roll up the XML data. */ xmlData = CFPropertyListCreateXMLData(kCFAllocatorDefault, dataDictionary); if (!xmlData) { goto finish; } finish: if (working_dir) free(working_dir); if (dataDictionary) CFRelease(dataDictionary); if (dataValue) CFRelease(dataValue); if (dataKey) CFRelease(dataKey); if (argvArray) CFRelease(argvArray); return xmlData; } /******************************************************************************* * *******************************************************************************/ KXKextManagerError _KXKextRaiseSecurityAlert(KXKextRef aKext, uid_t euid, AuthorizationRef auth_ref) { KXKextManagerError result = kKXKextManagerErrorNone; CFMutableDictionaryRef alertDict = NULL; // must release (reused) CFMutableArrayRef alertMessageArray = NULL; // must release (reused) CFURLRef iokitFrameworkBundleURL = NULL; // must release CFUserNotificationRef securityNotification = NULL; // must release CFUserNotificationRef failureNotification = NULL; // must release SInt32 userNotificationError = 0; CFOptionFlags response = 0; int userNotificationResult = 0; uid_t real_euid = geteuid(); #ifdef NO_CFUserNotification result = kKXKextManagerErrorUnspecified; goto finish; #else alertDict = CFDictionaryCreateMutable( kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); if (!alertDict) { result = kKXKextManagerErrorUnspecified; goto finish; } iokitFrameworkBundleURL = CFURLCreateWithFileSystemPath( kCFAllocatorDefault, CFSTR("/System/Library/Frameworks/IOKit.framework"), kCFURLPOSIXPathStyle, true); if (!iokitFrameworkBundleURL) { result = kKXKextManagerErrorUnspecified; goto finish; } alertMessageArray = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks); if (!alertMessageArray) { result = kKXKextManagerErrorUnspecified; goto finish; } /* This is the localized format string for the alert message. */ 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); CFRelease(alertMessageArray); alertMessageArray = NULL; securityNotification = CFUserNotificationCreate(kCFAllocatorDefault, 0 /* time interval */, kCFUserNotificationCautionAlertLevel, &userNotificationError, alertDict); if (!securityNotification) { fprintf(stderr, "error creating user notification (%d)\n", userNotificationError); result = kKXKextManagerErrorUnspecified; goto finish; } userNotificationResult = CFUserNotificationReceiveResponse( securityNotification, 0, &response); if (userNotificationResult != 0) { fprintf(stderr, "can't ask user to allow load of nonsecure kext\n"); result = kKXKextManagerErrorIPC; goto finish; } if (response == kCFUserNotificationDefaultResponse) { // The default response is to not load the kext. result = kKXKextManagerErrorUserAbort; goto finish; } else if (response == kCFUserNotificationAlternateResponse || response == kCFUserNotificationOtherResponse) { AuthorizationItem fixkextright = { "system.kext.make_secure", 0, NULL, 0 }; const AuthorizationItemSet fixkextrightset = { 1, &fixkextright }; int flags = kAuthorizationFlagExtendRights | kAuthorizationFlagInteractionAllowed; OSStatus auth_result = errAuthorizationSuccess; if (seteuid(euid) != 0) { fprintf(stderr, "call to seteuid() failed\n"); result = kKXKextManagerErrorUnspecified; goto finish; } auth_result = AuthorizationCopyRights(auth_ref, &fixkextrightset, NULL, flags, NULL); seteuid(real_euid); if (auth_result != errAuthorizationSuccess) { result = kKXKextManagerErrorUserAbort; goto finish; } KXKextManagerRequalifyKext(KXKextGetManager(aKext), aKext); if (response == kCFUserNotificationAlternateResponse) { result = _KXKextMakeSecure(aKext); /* If the kext couldn't be made secure (presumably because it's on * a read-only or closed network filesystem) then inform the user * and mark it authentic so it will be loaded anyway. */ if (result == kKXKextManagerErrorFileAccess) { result = kKXKextManagerErrorNone; KXKextMarkAuthentic(aKext); alertDict = CFDictionaryCreateMutable( kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); if (!alertDict) { result = kKXKextManagerErrorUnspecified; goto finish; } alertMessageArray = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks); if (!alertMessageArray) { result = kKXKextManagerErrorUnspecified; goto finish; } /* This is the localized format string for the alert message. */ 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); CFRelease(alertMessageArray); alertMessageArray = NULL; failureNotification = CFUserNotificationCreate(kCFAllocatorDefault, 0 /* time interval */, kCFUserNotificationCautionAlertLevel, &userNotificationError, alertDict); if (!failureNotification) { fprintf(stderr, "error creating user notification (%d)\n", userNotificationError); result = kKXKextManagerErrorUnspecified; goto finish; } userNotificationResult = CFUserNotificationReceiveResponse( failureNotification, 0, &response); if (userNotificationResult != 0) { fprintf(stderr, "couldn't get response to failure notification\n"); result = kKXKextManagerErrorIPC; goto finish; } } goto finish; } else if (response == kCFUserNotificationOtherResponse) { KXKextMarkAuthentic(aKext); goto finish; } } #endif /* NO_CFUserNotification */ finish: if (alertDict) CFRelease(alertDict); if (alertMessageArray) CFRelease(alertMessageArray); if (securityNotification) CFRelease(securityNotification); if (failureNotification) CFRelease(failureNotification); if (iokitFrameworkBundleURL) CFRelease(iokitFrameworkBundleURL); return result; }