Files
nvidia-installer/install-from-cwd.c
Aaron Plattner c056e256a1 570.86.16
2025-01-30 09:16:55 -08:00

1492 lines
48 KiB
C

/*
* nvidia-installer: A tool for installing NVIDIA software packages on
* Unix and Linux systems.
*
* Copyright (C) 2003 NVIDIA Corporation
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see <http://www.gnu.org/licenses>.
*
*
* install_from_cwd.c -
*/
#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <stddef.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include "nvidia-installer.h"
#include "user-interface.h"
#include "kernel.h"
#include "command-list.h"
#include "backup.h"
#include "files.h"
#include "misc.h"
#include "sanity.h"
#include "manifest.h"
/* local prototypes */
static Package *parse_manifest(Options *op);
static int install_kernel_modules(Options *op, Package *p);
static void free_package(Package *p);
static int assisted_module_signing(Options *op, Package *p);
static const KernelModuleInfo optional_modules[] = {
{
.module_name = "nvidia-uvm",
.optional_module_dependee = "CUDA",
.disable_option = "no-unified-memory",
.option_offset = offsetof(Options, install_uvm),
},
{
.module_name = "nvidia-drm",
.optional_module_dependee = "DRM-KMS",
.disable_option = "no-drm",
.option_offset = offsetof(Options, install_drm),
},
{
.module_name = "nvidia-peermem",
.optional_module_dependee = "GPUDirect RDMA p2p memory sharing",
.disable_option = "no-peermem",
.option_offset = offsetof(Options, install_peermem),
},
};
/*
* install_from_cwd() - perform an installation from the current
* working directory; we first ensure that we have a .manifest file in
* the cwd, and the files listed in the manifest exist and have
* correct checksums (to ensure the package hasn't been corrupted, not
* for anything security related).
*
* Second, make sure the user accepts the license.
*
* Then, optionally override the OpenGL and XFree86 installation
* prefixes.
*
* Determine the currently installed NVIDIA driver version (if any).
*
*/
int install_from_cwd(Options *op)
{
Package *p;
CommandList *c;
int ran_pre_install_hook = FALSE;
HookScriptStatus res;
static const char* module_only_text =
"kernel module for the ";
static const char* xconfig_success_text =
"Your X configuration file has been successfully updated. ";
static const char* edit_your_xorgconf_text =
" Please update your xorg.conf file as "
"appropriate; see the file /usr/share/doc/"
"NVIDIA_GLX-1.0/README.txt for details.";
/*
* validate the manifest file in the cwd, and process it, building
* a Package struct
*/
if ((p = parse_manifest(op)) == NULL) goto failed;
if (!op->x_files_packaged) {
edit_your_xorgconf_text = "";
}
ui_set_title(op, "%s (%s)", p->description, p->version);
/*
* warn the user if "legacy" GPUs are installed in this system
* and if no supported GPU is found, at all.
*/
check_for_nvidia_graphics_devices(op, p);
/* check that we are not running any X server */
if (!check_for_running_x(op)) goto failed;
/* run the distro pre unload hook */
res = run_distro_hook(op, "pre-unload");
if (res == HOOK_SCRIPT_FAIL) {
ui_error(op, "Pre-unload hook script failed");
goto failed;
}
/* make sure the kernel module is unloaded */
if (!check_for_unloaded_kernel_module(op)) goto failed;
ui_log(op, "Installing NVIDIA driver version %s.", p->version);
/*
* determine the current NVIDIA version (if any); ask the user if
* they really want to overwrite the existing installation
*/
if (!check_for_existing_driver(op, p)) goto exit_install;
/*
* check to see if an alternate method of installation is already installed
* or is available, but not installed; ask the user if they really want to
* install anyway despite the presence/availability of an alternate install.
*/
if (!check_for_alternate_install(op)) goto exit_install;
/* run the distro preinstall hook */
res = run_distro_hook(op, "pre-install");
if (res == HOOK_SCRIPT_FAIL) {
if (ui_multiple_choice(op, CONTINUE_ABORT_CHOICES,
NUM_CONTINUE_ABORT_CHOICES,
CONTINUE_CHOICE, /* Default choice */
"The distribution-provided pre-install "
"script failed! Are you sure you want "
"to continue?") == ABORT_CHOICE) {
goto failed;
}
} else if (res == HOOK_SCRIPT_SUCCESS) {
if (ui_multiple_choice(op, CONTINUE_ABORT_CHOICES,
NUM_CONTINUE_ABORT_CHOICES,
CONTINUE_CHOICE, /* Default choice */
"The distribution-provided pre-install script "
"completed successfully. If this is the first "
"time you have run the installer, this script "
"may have helped disable Nouveau, but a reboot "
"may be required first. "
"Would you like to continue, or would you "
"prefer to abort installation to reboot the "
"system?") == ABORT_CHOICE) {
goto exit_install;
}
ran_pre_install_hook = TRUE;
}
if (!op->no_kernel_modules) {
PrecompiledInfo *info = find_precompiled_kernel_interface(op, p);
if (info) {
free_precompiled(info);
/*
* make sure the required development tools are present on
* this system before trying to link the kernel interface.
*/
if (!check_precompiled_kernel_interface_tools(op)) return FALSE;
} else {
/*
* make sure the required development tools are present on
* this system before attempting to verify the compiler and
* trying to build a custom kernel interface.
*/
if (!check_development_tools(op, p)) return FALSE;
}
}
/* fail if the nouveau driver is currently in use */
if (!check_for_nouveau(op)) goto failed;
/* ask if we should install the optional kernel modules */
should_install_optional_modules(op, p, optional_modules,
ARRAY_LEN(optional_modules));
/* attempt to build the kernel modules for the target kernel */
if (!op->no_kernel_modules) {
if (!install_kernel_modules(op, p)) {
goto failed;
}
} else {
ui_warn(op, "You specified the '--no-kernel-modules' command line "
"option, nvidia-installer will not install any kernel "
"modules as part of this driver installation, and it will "
"not remove existing NVIDIA kernel modules not part of "
"an earlier NVIDIA driver installation. Please ensure "
"that NVIDIA kernel modules matching this driver version "
"are installed separately.");
}
/*
* if we are only installing the kernel modules, then remove
* everything else from the package; otherwise do some
* OpenGL-specific stuff
*/
if (op->kernel_modules_only) {
remove_non_kernel_module_files_from_package(p);
} else {
/* ask for the XFree86 and OpenGL installation prefixes. */
if (!get_prefixes(op)) goto failed;
/*
* if the package contains any .desktop files,
* process them (perform some search and replacing so
* that they reflect the correct installation path, etc)
* and add them to the package list (files to be installed).
*/
process_dot_desktop_files(op, p);
#if defined(NV_X86_64)
/*
* ask if we should install the 32bit compatibility files on
* this machine.
*/
should_install_compat32_files(op, p);
#endif /* NV_X86_64 */
}
if (op->no_opengl_files) {
remove_opengl_files_from_package(p);
} else {
check_for_vulkan_loader(op);
}
if (op->no_wine_files) {
remove_wine_files_from_package(p);
}
/*
* determine whether systemd files should be installed
*/
if (op->use_systemd != NV_OPTIONAL_BOOL_TRUE) {
remove_systemd_files_from_package(p);
}
/*
* Remove any kernel module source files that won't be installed.
*/
remove_non_installed_kernel_module_source_files_from_package(p);
/*
* now that we have the installation prefixes, build the
* destination for each file to be installed
*/
if (!set_destinations(op, p)) goto failed;
/*
* if we are installing OpenGL libraries, ensure that a symlink gets
* installed to /usr/lib/libGL.so.1. add_libgl_abi_symlink() sets its own
* destination, so it must be called after set_destinations().
*/
if (!op->kernel_modules_only && !op->no_opengl_files) {
add_libgl_abi_symlink(op, p);
}
if (!op->kernel_modules_only) {
/*
* uninstall the existing driver; this needs to be done before
* building the command list.
*
* XXX if we uninstall now, then build the command list, and
* then ask the user if they really want to execute the
* command list, if the user decides not to execute the
* command list, they'll be left with no driver installed.
*/
if (!run_existing_uninstaller(op)) goto failed;
/* initialize the backup log */
if (!init_backup(op, p)) goto failed;
}
if (!op->no_kernel_modules) {
/* Import the modules into DKMS, if requested */
dkms_register_module(op, p, get_kernel_name(op));
}
if (!check_libglvnd_files(op, p)) {
goto failed;
}
/* build a list of operations to execute to do the install */
if ((c = build_command_list(op, p)) == NULL) goto failed;
/* call the ui to get approval for the list of commands */
if (!ui_approve_command_list(op, c, "%s", p->description)) {
goto exit_install;
}
/* execute the command list */
if (!do_install(op, p, c)) goto failed;
/*
* Leave nvidia-drm loaded in case an X server with OutputClass-based driver
* matching is being used.
*/
if (!op->no_kernel_modules) {
if (package_includes_kernel_module(p, "nvidia-drm")) {
if (!load_kernel_module(op, "nvidia-drm")) {
goto failed;
}
}
if (package_includes_kernel_module(p, "nvidia-vgpu-vfio")) {
if (!load_kernel_module(op, "nvidia-vgpu-vfio")) {
goto failed;
}
}
}
/* run the distro postinstall script */
run_distro_hook(op, "post-install");
/*
* check that everything is installed properly (post-install
* sanity check)
*/
check_installed_files_from_package(op, p);
/* done */
if (!op->kernel_modules_only) {
module_only_text = "";
}
if (op->kernel_modules_only || op->no_nvidia_xconfig_question) {
xconfig_success_text = "";
edit_your_xorgconf_text = "";
} else {
int ret;
/* ask the user if they would like to run nvidia-xconfig */
const char *msg = "Would you like to run the nvidia-xconfig utility "
"to automatically update your X configuration file "
"so that the NVIDIA X driver will be used when you "
"restart X? Any pre-existing X configuration "
"file will be backed up.";
ret = run_nvidia_xconfig(op, FALSE, msg, op->run_nvidia_xconfig);
if (ret) {
edit_your_xorgconf_text = "";
} else {
xconfig_success_text = "";
}
}
ui_message(op,
"%sInstallation of the %s%s (version: %s) is now complete.%s",
xconfig_success_text, module_only_text,
p->description, p->version,
edit_your_xorgconf_text);
free_package(p);
return TRUE;
failed:
/*
* something bad happened during installation; print an error
* message and return FALSE
*/
if (op->logging) {
ui_error(op, "Installation has failed. Please see the file '%s' "
"for details. You may find suggestions on fixing "
"installation problems in the README available on the "
"Linux driver download page at www.nvidia.com.",
op->log_file_name);
} else {
ui_error(op, "Installation has failed. You may find suggestions "
"on fixing installation problems in the README available "
"on the Linux driver download page at www.nvidia.com.");
}
if (ran_pre_install_hook)
run_distro_hook(op, "failed-install");
/* fall through into exit_install... */
exit_install:
/*
* we are exiting installation; this can happen for reasons that
* do not merit the error message (e.g., the user declined the
* license agreement)
*/
free_package(p);
return FALSE;
} /* install_from_cwd() */
/*
* Attempt to build and install the appropriate kernel modules for the
* running kernel; we first check if prebuilt kernel interfaces exist.
* If yes, we try to link those into the final kernel module, else we
* try to build from source.
*
* If we succeed in building the kernel modules, we attempt to load
* them into the host kernel and add them to the list of files to
* install if the load attempt succeeds.
*/
static int install_kernel_modules(Options *op, Package *p)
{
PrecompiledInfo *precompiled_info;
process_dkms_conf(op,p);
/* determine where to install the kernel module */
if (!determine_kernel_module_installation_path(op)) return FALSE;
/* check '/proc/sys/kernel/modprobe' */
if (!check_proc_modprobe_path(op)) return FALSE;
/*
* do nvchooser-style logic to decide if we have a prebuilt kernel
* module for their kernel
*
* XXX One could make the argument that we should not actually do
* the building/linking now, but just add this to the list of
* operations and do it when we execute the operation list. I
* think it's better to make sure we have a kernel module early on
* -- a common problem for users will be not having a prebuilt
* kernel interface for their kernel, and not having the kernel
* headers installed, so it's better to catch that earlier on.
*/
if ((precompiled_info = find_precompiled_kernel_interface(op, p))) {
int i, precompiled_success = TRUE;
/*
* we have a prebuilt kernel interface package, so now link the
* kernel interface files to produce the kernel module.
*
* XXX if linking fails, maybe we should fall through and
* attempt to build the kernel module? No, if linking fails,
* then there is something pretty seriously wrong... better to
* abort.
*/
for (i = 0; i < precompiled_info->num_files; i++) {
if (!unpack_kernel_modules(op, p, p->kernel_module_build_directory,
&(precompiled_info->files[i]))) {
precompiled_success = FALSE;
break;
}
}
free_precompiled(precompiled_info);
if (!precompiled_success) {
return FALSE;
}
} else {
/*
* we do not have a prebuilt kernel interface; thus we'll need
* to compile the kernel interface, so determine where the
* kernel source files are.
*/
if (!determine_kernel_source_path(op, p)) return FALSE;
/* and now, build the kernel interface */
if (!build_kernel_modules(op, p)) return FALSE;
}
/* Optionally sign the kernel module */
if (!assisted_module_signing(op, p)) return FALSE;
/*
* if we got this far, we have a complete kernel module; test it
* to be sure it's OK
*/
if (!test_kernel_modules(op, p)) return FALSE;
/* add the kernel modules to the list of things to install */
add_kernel_modules_to_package(op, p);
return TRUE;
}
/*
* add_this_kernel() - build a precompiled kernel interface for the
* running kernel, and repackage the .run file to include the new
* precompiled kernel interface.
*/
int add_this_kernel(Options *op)
{
Package *p;
PrecompiledFileInfo *fileInfos;
/* parse the manifest */
if ((p = parse_manifest(op)) == NULL) goto failed;
/* make sure we have the development tools */
if (!check_development_tools(op, p)) goto failed;
/* find the kernel header files */
if (!determine_kernel_source_path(op, p)) goto failed;
/* build the precompiled files */
if (p->num_kernel_modules != build_kernel_interfaces(op, p, &fileInfos))
goto failed;
/* pack the precompiled files */
if (!pack_precompiled_files(op, p, p->num_kernel_modules, fileInfos))
goto failed;
free_package(p);
return TRUE;
failed:
ui_error(op, "Unable to add a precompiled kernel interface for the "
"running kernel.");
free_package(p);
return FALSE;
} /* add_this_kernel() */
/*
* Returns TRUE if the given module has a separate interface, FALSE otherwise.
*/
static int has_separate_interface_file(char *name) {
int i;
static const char* no_interface_modules[] = {
"nvidia-vgpu-vfio",
"nvidia-uvm",
"nvidia-drm",
"nvidia-peermem",
};
for (i = 0; i < ARRAY_LEN(no_interface_modules); i++) {
if (strcmp(no_interface_modules[i],name) == 0) {
return FALSE;
}
}
return TRUE;
};
/*
* Populate the module info records for optional records with information
* that can be used in e.g. error messages.
*/
static void populate_optional_module_info(KernelModuleInfo *module)
{
int i;
for (i = 0; i < ARRAY_LEN(optional_modules); i++) {
if (strcmp(optional_modules[i].module_name, module->module_name) == 0) {
module->is_optional = TRUE;
module->optional_module_dependee =
optional_modules[i].optional_module_dependee;
module->disable_option = optional_modules[i].disable_option;
module->option_offset = optional_modules[i].option_offset;
return;
}
}
}
/*
* 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);
}
/*
* 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;
}
/*
* parse_manifest() - open and read the .manifest file in the current
* directory.
*
* The first nine lines of the .manifest file are:
*
* - a description string
* - a version string
* - the kernel module file name
* - the kernel interface file name
* - the kernel module name (what `rmmod` and `modprobe` should use)
* - a whitespace-separated list of module names that should be
* removed before installing a new kernel module
* - a whitespace-separated list of kernel module filenames that
* should be uninstalled before installing a new kernel module
* - kernel module build directory
* - directory containing precompiled kernel interfaces
*
* The rest of the manifest file is file entries. A file entry is a
* whitespace-separated list containing:
*
* - a filename (relative to the cwd)
* - an octal value describing the permissions
* - a flag describing the file type
* - certain file types have an architecture
* - certain file types have a second flag
* - certain file types will have a path
* - file types which inherit their paths will have a path depth
* - symbolic links will name the target of the link
*/
static Package *parse_manifest (Options *op)
{
char *buf, *c, *tmpstr;
int line;
int fd, ret, len = 0;
struct stat stat_buf;
Package *p;
char *manifest = MAP_FAILED, *ptr;
int opengl_files_packaged = FALSE;
p = (Package *) nvalloc(sizeof (Package));
/* open the manifest file */
if ((fd = open(".manifest", O_RDONLY)) == -1) {
ui_error(op, "No package found for installation. Please run "
"this utility with the '--help' option for usage "
"information.");
goto fail;
}
if (fstat(fd, &stat_buf) == -1) goto cannot_open;
len = stat_buf.st_size;
manifest = mmap(0, len, PROT_READ, MAP_FILE|MAP_SHARED, fd, 0);
if (manifest == MAP_FAILED) goto cannot_open;
/* the first line is the description */
line = 1;
p->description = get_next_line(manifest, &ptr, manifest, len);
if (!p->description) goto invalid_manifest_file;
/* the second line is the version */
line++;
p->version = get_next_line(ptr, &ptr, manifest, len);
if (!p->version) goto invalid_manifest_file;
/* Ignore the third line */
line++;
nvfree(get_next_line(ptr, &ptr, manifest, len));
/* the fourth line is the list of kernel modules. */
line++;
tmpstr = get_next_line(ptr, &ptr, manifest, len);
if (parse_kernel_modules_list(p, tmpstr) == 0) {
goto invalid_manifest_file;
}
nvfree(tmpstr);
/*
* set the default value of excluded_kernel_modules to an empty, heap
* allocated string so that it can be freed and won't prematurely end
* an nvstrcat()ed string when unset.
*/
p->excluded_kernel_modules = nvstrdup("");
/*
* ignore the fifth through eigth lines
*/
line++;
nvfree(get_next_line(ptr, &ptr, manifest, len));
line++;
nvfree(get_next_line(ptr, &ptr, manifest, len));
line++;
nvfree(get_next_line(ptr, &ptr, manifest, len));
line++;
nvfree(get_next_line(ptr, &ptr, manifest, len));
/*
* allow the kernel module build directory to be overridden from the command
* line
*/
if (op->kernel_module_build_directory_override) {
p->kernel_module_build_directory =
op->kernel_module_build_directory_override;
op->kernel_module_build_directory_override = NULL;
} else {
struct module_type_info types;
int num_types;
num_types = valid_kernel_module_types(op, &types, FALSE);
if (num_types > 0) {
int selection;
if (num_types == 1) {
selection = 0;
} else {
selection = ui_multiple_choice(op, types.licenses, num_types,
types.default_entry,
"Multiple kernel module types are available for this "
"system. Which would you like to use?"
);
}
p->kernel_module_build_directory = nvstrdup(types.dirs[selection]);
} else {
p->kernel_module_build_directory = nvstrdup("kernel");
}
}
remove_trailing_slashes(p->kernel_module_build_directory);
/* the rest of the file is file entries */
line++;
for (; (buf = get_next_line(ptr, &ptr, manifest, len)); line++) {
char *flag = NULL;
PackageEntry entry;
int entry_success = FALSE;
if (buf[0] == '\0') {
free(buf);
break;
}
/* initialize the new entry */
memset(&entry, 0, sizeof(PackageEntry));
/* read the file name and permissions */
c = buf;
entry.file = read_next_word(buf, &c);
if (!entry.file) goto entry_done;
tmpstr = read_next_word(c, &c);
if (!tmpstr) goto entry_done;
/* translate the mode string into an octal mode */
ret = mode_string_to_mode(op, tmpstr, &entry.mode);
free(tmpstr);
if (!ret) goto entry_done;
/* every file has a type field */
entry.type = FILE_TYPE_NONE;
flag = read_next_word(c, &c);
if (!flag) goto entry_done;
entry.type = parse_manifest_file_type(flag, &entry.caps);
if (entry.type == FILE_TYPE_NONE) {
goto entry_done;
}
/* Track whether certain file types were packaged */
switch (entry.type) {
case FILE_TYPE_XMODULE_SHARED_LIB:
op->x_files_packaged = TRUE;
break;
case FILE_TYPE_VULKAN_ICD_JSON:
op->vulkan_icd_json_packaged = TRUE;
break;
case FILE_TYPE_VULKANSC_ICD_JSON:
op->vulkansc_icd_json_packaged = TRUE;
break;
default: break;
}
/* set opengl_files_packaged if any OpenGL files were packaged */
if (entry.caps.is_opengl) {
opengl_files_packaged = TRUE;
}
/* some libs/symlinks have an arch field */
entry.compat_arch = FILE_COMPAT_ARCH_NONE;
if (entry.caps.has_arch) {
nvfree(flag);
flag = read_next_word(c, &c);
if (!flag) goto entry_done;
if (strcmp(flag, "COMPAT32") == 0)
entry.compat_arch = FILE_COMPAT_ARCH_COMPAT32;
else if (strcmp(flag, "NATIVE") == 0)
entry.compat_arch = FILE_COMPAT_ARCH_NATIVE;
else {
goto entry_done;
}
}
/* if compat32 files are packaged, set compat32_files_packaged */
if (entry.compat_arch == FILE_COMPAT_ARCH_COMPAT32) {
op->compat32_files_packaged = TRUE;
}
/* some file types have a path field, or inherit their paths */
if (entry.caps.has_path) {
entry.path = read_next_word(c, &c);
if (!entry.path) goto invalid_manifest_file;
} else if (entry.caps.inherit_path) {
int i;
char *path, *depth, *slash;
const char * const depth_marker = "INHERIT_PATH_DEPTH:";
depth = read_next_word(c, &c);
if (!depth ||
strncmp(depth, depth_marker, strlen(depth_marker)) != 0) {
goto invalid_manifest_file;
}
entry.inherit_path_depth = atoi(depth + strlen(depth_marker));
nvfree(depth);
/* Remove the file component from the packaged filename */
path = entry.path = nvstrdup(entry.file);
slash = strrchr(path, '/');
if (slash == NULL) {
goto invalid_manifest_file;
}
slash[1] = '\0';
/* Strip leading directory components from the path */
for (i = 0; i < entry.inherit_path_depth; i++) {
slash = strchr(entry.path, '/');
if (slash == NULL) {
goto invalid_manifest_file;
}
entry.path = slash + 1;
}
entry.path = nvstrdup(entry.path);
nvfree(path);
} else {
entry.path = NULL;
}
/* symlinks have a target */
if (entry.caps.is_symlink) {
entry.target = read_next_word(c, &c);
if (!entry.target) goto invalid_manifest_file;
} else {
entry.target = NULL;
}
/*
* as a convenience for later, set the 'name' pointer to
* the basename contained in 'file' (ie the portion of
* 'file' without any leading directory components
*/
entry.name = strrchr(entry.file, '/');
if (entry.name) entry.name++;
if (!entry.name) entry.name = entry.file;
add_package_entry(p,
entry.file,
entry.path,
entry.name,
entry.target,
entry.dst,
entry.type,
entry.compat_arch,
entry.mode);
entry_success = TRUE;
entry_done:
/* clean up */
nvfree(buf);
nvfree(flag);
if (!entry_success) {
goto invalid_manifest_file;
}
}
/* If no OpenGL files were packaged, we can't install them. Set the
* no_opengl_files flag so that everything we skip when explicitly
* excluding OpenGL is also skipped when OpenGL is not packaged. */
if (!opengl_files_packaged) {
op->no_opengl_files = TRUE;
}
munmap(manifest, len);
if (fd != -1) close(fd);
return p;
cannot_open:
ui_error(op, "Failure opening package's .manifest file (%s).",
strerror(errno));
goto fail;
invalid_manifest_file:
ui_error(op, "Invalid .manifest file; error on line %d.", line);
goto fail;
fail:
free_package(p);
if (manifest != MAP_FAILED) munmap(manifest, len);
if (fd != -1) close(fd);
return NULL;
} /* parse_manifest() */
/*
* add_package_entry() - add a PackageEntry to the package's entries
* array.
*/
void add_package_entry(Package *p,
char *file,
char *path,
char *name,
char *target,
char *dst,
PackageEntryFileType type,
PackageEntryFileCompatArch compat_arch,
mode_t mode)
{
int n;
struct stat stat_buf;
n = p->num_entries;
p->entries =
(PackageEntry *) nvrealloc(p->entries, (n + 1) * sizeof(PackageEntry));
memset(&p->entries[n], 0, sizeof(PackageEntry));
p->entries[n].file = file;
p->entries[n].path = path;
p->entries[n].name = name;
p->entries[n].target = target;
p->entries[n].dst = dst;
p->entries[n].type = type;
p->entries[n].mode = mode;
p->entries[n].caps = get_file_type_capabilities(type);
p->entries[n].compat_arch = compat_arch;
if (stat(p->entries[n].file, &stat_buf) != -1) {
p->entries[n].inode = stat_buf.st_ino;
p->entries[n].device = stat_buf.st_dev;
} else {
p->entries[n].inode = 0;
p->entries[n].device = 0;
}
p->num_entries++;
} /* add_package_entry() */
/*
* free_package() - free the Package data structure
*/
static void free_package(Package *p)
{
int i;
if (!p) return;
nvfree(p->description);
nvfree(p->version);
nvfree(p->kernel_module_build_directory);
for (i = 0; i < p->num_kernel_modules; i++) {
free_kernel_module_info(p->kernel_modules[i]);
}
nvfree(p->kernel_modules);
nvfree(p->excluded_kernel_modules);
nvfree(p->kernel_make_logs);
for (i = 0; i < p->num_entries; i++) {
nvfree(p->entries[i].file);
nvfree(p->entries[i].path);
nvfree(p->entries[i].target);
nvfree(p->entries[i].dst);
/*
* Note: p->entries[i].name just points into
* p->entries[i].file, so don't free p->entries[i].name
*/
}
nvfree((char *) p->entries);
nvfree((char *) p);
} /* free_package() */
/*
* assisted_module_signing() - Guide the user through the module signing process
*/
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 *x509_hash, *private_key_path, *public_key_path;
int ret, generate_failed = FALSE;
const RunCommandOutputMatch output_match[] = {
{ .lines = 8, .initial_match = NULL },
{ 0 }
};
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 orthogonal
* 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. */
ret = run_command(op, NULL, TRUE, output_match, TRUE,
"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);
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;
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 key pair at this point, so we know we have it;
otherwise, we would have already returned by now. */
ret = run_command(op, &result, FALSE, NULL, FALSE,
op->utils[OPENSSL], " x509 -noout -fingerprint ",
"-inform DER -in ", op->module_signing_public_key,
NULL);
/* 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 */
ret = run_command(op, &result, FALSE, NULL, FALSE,
sha1sum, " ", op->module_signing_public_key,
NULL);
nvfree(sha1sum);
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) - 1);
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_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_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() */