/* * Iterate over the list of kernel modules from the manifest file; generate * and store module information records for each module in the Package. */ static int parse_kernel_modules_list(Package *p, char *list) { char *name; p->num_kernel_modules = 0; /* in case this gets called more than once */ for (name = strtok(list, " "); name; name = strtok(NULL, " ")) { KernelModuleInfo *module; p->kernel_modules = nvrealloc(p->kernel_modules, (p->num_kernel_modules + 1) * sizeof(p->kernel_modules[0])); module = p->kernel_modules + p->num_kernel_modules; memset(module, 0, sizeof(*module)); module->module_name = nvstrdup(name); module->module_filename = nvstrcat(name, ".ko", NULL); module->has_separate_interface_file = has_separate_interface_file(name); if (module->has_separate_interface_file) { char *core_binary = nvidia_to_nv(name, "-kernel.o_binary"); module->interface_filename = nvidia_to_nv(name, "-linux.o"); module->core_object_name = nvstrcat(name, "/", core_binary, NULL); nvfree(core_binary); } populate_optional_module_info(module); p->num_kernel_modules++; } return p->num_kernel_modules; }
/* * Return a string with 'suffix' appended to the original source string, after * replacing "nvidia" with "nv" at the beginning of the original source string. */ static char *nvidia_to_nv(const char *name, const char *suffix) { if (strncmp("nvidia", name, strlen("nvidia")) != 0) { return NULL; } return nvstrcat("nv", name + strlen("nvidia"), suffix, NULL); }
/* * nv_append_sprintf() - similar to glib's g_string_append_printf(), except * instead of operating on a GString it operates on a (char **). Appends a * formatted string to the end of the dynamically-allocated string pointed to by * *buf (or the empty string if *buf is NULL), potentially reallocating the * string in the process. This function only returns on succcess. */ void nv_append_sprintf(char **buf, const char *fmt, ...) { char *prefix, *suffix; prefix = *buf; NV_VSNPRINTF(suffix, fmt); if (!prefix) { *buf = suffix; } else { *buf = nvstrcat(prefix, suffix, NULL); free(prefix); free(suffix); } }
static void send_control(int sock, char *string, ...) { va_list args; char *line = NULL; char *newline; char *s = NULL; line = nvstrdup(string); va_start(args, string); s = va_arg(args, char *); while (s) { newline = nvstrcat(line, s, NULL); nvfree(line); line = newline; s = va_arg(args, char *); } va_end(args); write(sock, line, strlen(line)); nvfree(line); } /* send_control() */
void nvgetopt_print_help(const NVGetoptOption *options, unsigned int include_mask, nvgetopt_print_help_callback_ptr callback) { const NVGetoptOption *o; int i; for (i = 0; options[i].name; i++) { char *msg = NULL, *arg = NULL, *description = NULL; o = &options[i]; /* Skip options with no help text */ if (!o->description) { continue; } /* skip options who don't have all the bits of include_mask */ if ((o->flags & include_mask) != include_mask) { continue; } /* if we are going to need the argument, process it now */ arg = NULL; if (o->flags & NVGETOPT_HAS_ARGUMENT) { if (o->arg_name) { arg = strdup(o->arg_name); } else { char *tmp; arg = strdup(o->name); for (tmp = arg; tmp && *tmp; tmp++) { *tmp = toupper(*tmp); } } } msg = NULL; /* * create the long version of the option, possibly with an * argument; e.g., "--foo" or "--foo=BAR" */ if (arg) { msg = nvstrcat("--", o->name, "=", arg, NULL); } else { msg = nvstrcat("--", o->name, NULL); } /* * prepend the single character version of the option, * possibly with an argument; e.g., "-f" or "-f BAR" */ if (o->val <= UCHAR_MAX && o->val >= 0 && isalpha(o->val)) { char scratch[16]; char *tmp; snprintf(scratch, sizeof(scratch), "%c", o->val); if (arg) { tmp = nvstrcat("-", scratch, " ", arg, ", ", msg, NULL); } else { tmp = nvstrcat("-", scratch, ", ", msg, NULL); } free(msg); msg = tmp; } /* append the boolean version of the option; e.g., "--no-foo" */ if (((o->flags & NVGETOPT_IS_BOOLEAN) && !(o->flags & NVGETOPT_HAS_ARGUMENT)) || (o->flags & NVGETOPT_ALLOW_DISABLE)) { char *tmp = nvstrcat(msg, ", --no-", o->name, NULL); free(msg); msg = tmp; } /* process the description text */ description = cook_description(o->description); /* give the strings to the caller to format and print */ callback(msg, description); free(msg); free(arg); free(description); } }
char *nv_prepend_to_string_list(char *list, const char *item, const char *delim) { char *new_list = nvstrcat(item, list ? delim : NULL, list, NULL); nvfree(list); return new_list; }
static int assisted_module_signing(Options *op, Package *p) { int generate_keys = FALSE, do_sign = FALSE, secureboot, i; secureboot = secure_boot_enabled(); if (secureboot < 0) { ui_log(op, "Unable to determine if Secure Boot is enabled: %s", strerror(-secureboot)); } if (op->kernel_module_signed) { /* The kernel module is already signed, e.g. from linking a precompiled * interface + appending a detached signature */ return TRUE; } if (test_kernel_config_option(op, p, "CONFIG_DUMMY_OPTION") == KERNEL_CONFIG_OPTION_UNKNOWN) { /* Unable to test kernel configuration options, possibly due to * missing kernel headers. Since we might be installing on a * system that doesn't have the headers, bail out. */ return TRUE; } if (op->module_signing_secret_key && op->module_signing_public_key) { /* If the user supplied signing keys, sign the module, regardless of * whether or not we actually need to. */ do_sign = TRUE; } else if (test_kernel_config_option(op, p, "CONFIG_MODULE_SIG_FORCE") == KERNEL_CONFIG_OPTION_DEFINED) { /* If CONFIG_MODULE_SIG_FORCE is set, we must sign. */ ui_message(op, "The target kernel has CONFIG_MODULE_SIG_FORCE set, " "which means that it requires that kernel modules be " "cryptographically signed by a trusted key."); do_sign = TRUE; } else if (secureboot != 1 && !op->expert) { /* If this is a non-UEFI system, or a UEFI system with secure boot * disabled, or we are unable to determine whether the system has * secure boot enabled, bail out unless in expert mode. */ return TRUE; } else if (test_kernel_config_option(op, p, "CONFIG_MODULE_SIG") == KERNEL_CONFIG_OPTION_DEFINED){ /* The kernel may or may not enforce module signatures; ask the user * whether to sign the module. */ const char *choices[2] = { "Sign the kernel module", "Install without signing" }; const char* sb_message = (secureboot == 1) ? "This system also has UEFI Secure Boot " "enabled; many distributions enforce " "module signature verification on UEFI " "systems when Secure Boot is enabled. " : ""; do_sign = (ui_multiple_choice(op, choices, 2, 1, "The target kernel " "has CONFIG_MODULE_SIG set, which means " "that it supports cryptographic " "signatures on kernel modules. On some " "systems, the kernel may refuse to load " "modules without a valid signature from " "a trusted key. %sWould you like to sign " "the NVIDIA kernel module?", sb_message) == 0); } if (!do_sign) { /* The user explicitly opted out of module signing, or the kernel does * not support module signatures, and no signing keys were provided; * there is nothing for us to do here. */ return TRUE; } /* If we're missing either key, we need to get both from the user. */ if (!op->module_signing_secret_key || !op->module_signing_public_key) { const char *choices[2] = { "Use an existing key pair", "Generate a new key pair" }; generate_keys = (ui_multiple_choice(op, choices, 2, 1, "Would you like " "to sign the NVIDIA kernel module " "with an existing key pair, or " "would you like to generate a new " "one?") == 1); if (generate_keys) { char *cmdline, *x509_hash, *private_key_path, *public_key_path; int ret, generate_failed = FALSE; if (!op->utils[OPENSSL]) { ui_error(op, "Unable to generate key pair: openssl not " "found!"); return FALSE; } /* Determine what hashing algorithm to use for the generated X.509 * certificate. XXX The default is to use the same hash that is * used for signing modules; the two hashes are actually orthognal * to each other, but by choosing the module signing hash we are * guaranteed that the chosen hash will be built into the kernel. */ if (op->module_signing_x509_hash) { x509_hash = nvstrdup(op->module_signing_x509_hash); } else { char *guess, *guess_trimmed, *warn = NULL; char *no_guess = "Unable to guess the module signing hash."; char *common_warn = "The module signing certificate generated " "by nvidia-installer will be signed with " "sha256 as a fallback. If the resulting " "certificate fails to import into your " "kernel's trusted keyring, please run the " "installer again, and either use a pre-" "generated key pair, or set the " "--module-signing-x509-hash option if you " "plan to generate a new key pair with " "nvidia-installer."; guess = guess_module_signing_hash(op, p->kernel_module_build_directory); if (guess == NULL) { warn = no_guess; goto guess_fail; } guess_trimmed = nv_trim_space(guess); guess_trimmed = nv_trim_char_strict(guess_trimmed, '"'); if (guess_trimmed) { if (strlen(guess_trimmed) == 0) { warn = no_guess; goto guess_fail; } x509_hash = nvstrdup(guess_trimmed); } else { warn = "Error while parsing the detected module signing " "hash."; goto guess_fail; } guess_fail: nvfree(guess); if (warn) { ui_warn(op, "%s %s", warn, common_warn); x509_hash = nvstrdup("sha256"); } } log_printf(op, NULL, "Generating key pair for module signing..."); /* Generate temporary files for the signing key and certificate */ private_key_path = write_temp_file(op, 0, NULL, 0600); public_key_path = write_temp_file(op, 0, NULL, 0644); if (!private_key_path || !public_key_path) { ui_error(op, "Failed to create one or more temporary files for " "the module signing keys."); generate_failed = TRUE; goto generate_done; } /* Generate a key pair using openssl. * XXX We assume that sign-file requires the X.509 certificate * in DER format; if this changes in the future we will need * to be able to accommodate the actual required format. */ cmdline = nvstrcat("cd ", p->kernel_module_build_directory, "; ", op->utils[OPENSSL], " req -new -x509 -newkey " "rsa:2048 -days 7300 -nodes -subj " "\"/CN=nvidia-installer generated signing key/\"" " -keyout ", private_key_path, " -outform DER -out ", public_key_path, " -", x509_hash, NULL); nvfree(x509_hash); ret = run_command(op, cmdline, NULL, TRUE, 8, TRUE); nvfree(cmdline); if (ret != 0) { ui_error(op, "Failed to generate key pair!"); generate_failed = TRUE; goto generate_done; } log_printf(op, NULL, "Signing keys generated successfully."); /* Set the signing keys to the newly generated pair. */ op->module_signing_secret_key = nvstrdup(private_key_path); op->module_signing_public_key = nvstrdup(public_key_path); generate_done: nvfree(private_key_path); nvfree(public_key_path); if (generate_failed) { return FALSE; } } else { /* The user already has keys; prompt for their locations. */ op->module_signing_secret_key = get_filename(op, op->module_signing_secret_key, "Please provide the path to the private key"); op->module_signing_public_key = get_filename(op, op->module_signing_public_key, "Please provide the path to the public key"); } } /* Now that we have keys (user-supplied or installer-generated), * sign the kernel module/s which we built earlier. */ for (i = 0; i < p->num_kernel_modules; i++) { if (!sign_kernel_module(op, p->kernel_module_build_directory, p->kernel_modules[i].module_filename, TRUE)) { return FALSE; } } if (generate_keys) { /* If keys were generated, we should install the verification cert * so that the user can make the kernel trust it, and either delete * or install the private signing key. */ char *name, *result = NULL, *fingerprint, *cmdline; char short_fingerprint[9]; int ret, delete_secret_key; delete_secret_key = ui_yes_no(op, TRUE, "The NVIDIA kernel module was " "successfully signed with a newly " "generated key pair. Would you like to " "delete the private signing key?"); /* Get the fingerprint of the X.509 certificate. We already used openssl to create a keypair at this point, so we know we have it; otherwise, we would have already returned by now. */ cmdline = nvstrcat(op->utils[OPENSSL], " x509 -noout -fingerprint ", "-inform DER -in ", op->module_signing_public_key, NULL); ret = run_command(op, cmdline, &result, FALSE, 0, FALSE); nvfree(cmdline); /* Format: "SHA1 Fingerprint=00:00:00:00:..." */ fingerprint = strchr(result, '=') + 1; if (ret != 0 || !fingerprint || strlen(fingerprint) < 40) { char *sha1sum = find_system_util("sha1sum"); if (sha1sum) { /* the openssl command failed, or we parsed its output * incorrectly; try to get a sha1sum of the DER certificate */ cmdline = nvstrcat(sha1sum, " ", op->module_signing_public_key, NULL); ret = run_command(op, cmdline, &result, FALSE, 0, FALSE); nvfree(sha1sum); nvfree(cmdline); fingerprint = result; } if (!sha1sum || ret != 0 || !fingerprint || strlen(fingerprint) < 40) { /* Unable to determine fingerprint */ fingerprint = "UNKNOWN"; } else { char *end = strchr(fingerprint, ' '); *end = '\0'; } } else { /* Remove any ':' characters from fingerprint and truncate */ char *tmp = nv_strreplace(fingerprint, ":", ""); strncpy(short_fingerprint, tmp, sizeof(short_fingerprint)); nvfree(tmp); } short_fingerprint[sizeof(short_fingerprint) - 1] = '\0'; /* Add the public key to the package */ /* XXX name will be leaked when freeing package */ name = nvstrcat("nvidia-modsign-crt-", short_fingerprint, ".der", NULL); add_package_entry(p, nvstrdup(op->module_signing_public_key), NULL, /* path */ name, NULL, /* target */ NULL, /* dst */ FILE_TYPE_MODULE_SIGNING_KEY, FILE_TLS_CLASS_NONE, FILE_COMPAT_ARCH_NONE, 0444); ui_message(op, "An X.509 certificate containing the public signing " "key will be installed to %s/%s. The SHA1 fingerprint of " "this certificate is: %s.\n\nThis certificate must be " "added to a key database which is trusted by your kernel " "in order for the kernel to be able to verify the module " "signature.", op->module_signing_key_path, name, fingerprint); nvfree(result); /* Delete or install the private key */ if (delete_secret_key) { secure_delete(op, op->module_signing_secret_key); } else { /* Add the private key to the package */ name = nvstrcat("nvidia-modsign-key-", short_fingerprint, ".key", NULL); add_package_entry(p, nvstrdup(op->module_signing_secret_key), NULL, /* path */ name, NULL, /* target */ NULL, /* dst */ FILE_TYPE_MODULE_SIGNING_KEY, FILE_TLS_CLASS_NONE, FILE_COMPAT_ARCH_NONE, 0400); ui_message(op, "The private signing key will be installed to %s/%s. " "After the public key is added to a key database which " "is trusted by your kernel, you may reuse the saved " "public/private key pair to sign additional kernel " "modules, without needing to re-enroll the public key. " "Please take some reasonable precautions to secure the " "private key: see the README for suggestions.", op->module_signing_key_path, name); } } /* if (generate_keys) */ return TRUE; } /* assisted_module_signing() */
DevicesPtr find_devices(Options *op) { DevicesPtr pDevices = NULL; DisplayDevicePtr pDisplayDevice; int i, j, n, count = 0; unsigned int mask, bit; DeviceRec tmpDevice; NvCfgPciDevice *devs = NULL; NvCfgBool is_primary_device; NvCfgBool ret; char *lib_path; void *lib_handle; NvCfgBool (*__getDevices)(int *n, NvCfgDevice **devs); NvCfgBool (*__openDevice)(int bus, int slot, NvCfgDeviceHandle *handle); NvCfgBool (*__getPciDevices)(int *n, NvCfgPciDevice **devs); NvCfgBool (*__openPciDevice)(int domain, int bus, int slot, int function, NvCfgDeviceHandle *handle); NvCfgBool (*__getNumCRTCs)(NvCfgDeviceHandle handle, int *crtcs); NvCfgBool (*__getProductName)(NvCfgDeviceHandle handle, char **name); NvCfgBool (*__getDisplayDevices)(NvCfgDeviceHandle handle, unsigned int *display_device_mask); NvCfgBool (*__getEDID)(NvCfgDeviceHandle handle, unsigned int display_device, NvCfgDisplayDeviceInformation *info); NvCfgBool (*__isPrimaryDevice)(NvCfgDeviceHandle handle, NvCfgBool *is_primary_device); NvCfgBool (*__closeDevice)(NvCfgDeviceHandle handle); NvCfgBool (*__getDeviceUUID)(NvCfgDeviceHandle handle, char **uuid); /* dlopen() the nvidia-cfg library */ #define __LIB_NAME "libnvidia-cfg.so.1" if (op->nvidia_cfg_path) { lib_path = nvstrcat(op->nvidia_cfg_path, "/", __LIB_NAME, NULL); } else { lib_path = strdup(__LIB_NAME); } lib_handle = dlopen(lib_path, RTLD_NOW); nvfree(lib_path); if (!lib_handle) { nv_warning_msg("error opening %s: %s.", __LIB_NAME, dlerror()); return NULL; } #define __GET_FUNC(proc, name) \ (proc) = dlsym(lib_handle, (name)); \ if (!(proc)) { \ nv_warning_msg("error retrieving symbol %s from %s: %s", \ (name), __LIB_NAME, dlerror()); \ dlclose(lib_handle); \ return NULL; \ } /* required functions */ __GET_FUNC(__getDevices, "nvCfgGetDevices"); __GET_FUNC(__openDevice, "nvCfgOpenDevice"); __GET_FUNC(__getPciDevices, "nvCfgGetPciDevices"); __GET_FUNC(__openPciDevice, "nvCfgOpenPciDevice"); __GET_FUNC(__getNumCRTCs, "nvCfgGetNumCRTCs"); __GET_FUNC(__getProductName, "nvCfgGetProductName"); __GET_FUNC(__getDisplayDevices, "nvCfgGetDisplayDevices"); __GET_FUNC(__getEDID, "nvCfgGetEDID"); __GET_FUNC(__closeDevice, "nvCfgCloseDevice"); __GET_FUNC(__getDeviceUUID, "nvCfgGetDeviceUUID"); /* optional functions */ __isPrimaryDevice = dlsym(lib_handle, "nvCfgIsPrimaryDevice"); if (__getPciDevices(&count, &devs) != NVCFG_TRUE) { return NULL; } if (count == 0) return NULL; pDevices = nvalloc(sizeof(DevicesRec)); pDevices->devices = nvalloc(sizeof(DeviceRec) * count); pDevices->nDevices = count; for (i = 0; i < count; i++) { pDevices->devices[i].dev = devs[i]; if (__openPciDevice(devs[i].domain, devs[i].bus, devs[i].slot, 0, &(pDevices->devices[i].handle)) != NVCFG_TRUE) { goto fail; } if (__getNumCRTCs(pDevices->devices[i].handle, &pDevices->devices[i].crtcs) != NVCFG_TRUE) { goto fail; } if (__getProductName(pDevices->devices[i].handle, &pDevices->devices[i].name) != NVCFG_TRUE) { /* This call may fail with little impact to the Device section */ pDevices->devices[i].name = NULL; } if (__getDeviceUUID(pDevices->devices[i].handle, &pDevices->devices[i].uuid) != NVCFG_TRUE) { goto fail; } if (__getDisplayDevices(pDevices->devices[i].handle, &mask) != NVCFG_TRUE) { goto fail; } pDevices->devices[i].displayDeviceMask = mask; /* count the number of display devices */ for (n = j = 0; j < 32; j++) { if (mask & (1 << j)) n++; } pDevices->devices[i].nDisplayDevices = n; if (n) { /* allocate the info array of the right size */ pDevices->devices[i].displayDevices = nvalloc(sizeof(DisplayDeviceRec) * n); /* fill in the info array */ for (n = j = 0; j < 32; j++) { bit = 1 << j; if (!(bit & mask)) continue; pDisplayDevice = &pDevices->devices[i].displayDevices[n]; pDisplayDevice->mask = bit; if (__getEDID(pDevices->devices[i].handle, bit, &pDisplayDevice->info) != NVCFG_TRUE) { pDisplayDevice->info_valid = FALSE; } else { pDisplayDevice->info_valid = TRUE; } n++; } } else { pDevices->devices[i].displayDevices = NULL; } if ((i != 0) && (__isPrimaryDevice != NULL) && (__isPrimaryDevice(pDevices->devices[i].handle, &is_primary_device) == NVCFG_TRUE) && (is_primary_device == NVCFG_TRUE)) { memcpy(&tmpDevice, &pDevices->devices[0], sizeof(DeviceRec)); memcpy(&pDevices->devices[0], &pDevices->devices[i], sizeof(DeviceRec)); memcpy(&pDevices->devices[i], &tmpDevice, sizeof(DeviceRec)); } ret = __closeDevice(pDevices->devices[i].handle); pDevices->devices[i].handle = NULL; if (ret != NVCFG_TRUE) { goto fail; } } goto done; fail: nv_warning_msg("Unable to use the nvidia-cfg library to query NVIDIA " "hardware."); for (i = 0; i < pDevices->nDevices; i++) { /* close the opened device */ if (pDevices->devices[i].handle) { __closeDevice(pDevices->devices[i].handle); } } free_devices(pDevices); pDevices = NULL; /* fall through */ done: if (devs) free(devs); return pDevices; } /* find_devices() */