diff options
-rw-r--r-- | COPYING | 39 | ||||
-rw-r--r-- | common-utils/common-utils.c | 99 | ||||
-rw-r--r-- | common-utils/common-utils.h | 5 | ||||
-rw-r--r-- | install-from-cwd.c | 60 | ||||
-rw-r--r-- | kernel.c | 89 | ||||
-rw-r--r-- | kernel.h | 1 | ||||
-rw-r--r-- | misc.c | 137 | ||||
-rw-r--r-- | nvidia-installer.c | 3 | ||||
-rw-r--r-- | nvidia-installer.h | 1 | ||||
-rw-r--r-- | option_table.h | 10 | ||||
-rw-r--r-- | version.mk | 2 |
11 files changed, 349 insertions, 97 deletions
@@ -1,12 +1,12 @@ - GNU GENERAL PUBLIC LICENSE - Version 2, June 1991 + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 - Copyright (C) 1989, 1991 Free Software Foundation, Inc. - 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. - Preamble + Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public @@ -15,7 +15,7 @@ software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by -the GNU Library General Public License instead.) You can apply it to +the GNU Lesser General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not @@ -55,8 +55,8 @@ patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. - - GNU GENERAL PUBLIC LICENSE + + GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains @@ -110,7 +110,7 @@ above, provided that you also meet all of these conditions: License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) - + These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in @@ -168,7 +168,7 @@ access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. - + 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is @@ -225,7 +225,7 @@ impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. - + 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License @@ -255,7 +255,7 @@ make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. - NO WARRANTY + NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN @@ -277,9 +277,9 @@ YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Programs + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it @@ -303,10 +303,9 @@ the "copyright" line and a pointer to where the full notice is found. 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, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. Also add information on how to contact you by electronic and paper mail. @@ -336,5 +335,5 @@ necessary. Here is a sample; alter the names: This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the -library. If this is what you want to do, use the GNU Library General +library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. diff --git a/common-utils/common-utils.c b/common-utils/common-utils.c index a11d996..7b18294 100644 --- a/common-utils/common-utils.c +++ b/common-utils/common-utils.c @@ -319,6 +319,19 @@ char *tilde_expansion(const char *str) } /* tilde_expansion() */ +/* + * nv_prepend_to_string_list() - add a new string to a string list, delimited + * by the given string delimiter. The original list is freed. + */ + +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; +} + + /****************************************************************************/ /* TextRows helper functions */ /****************************************************************************/ @@ -693,3 +706,89 @@ char *nvstrchrnul(char *s, int c) } return result; } + + +/****************************************************************************/ +/* string helper functions */ +/****************************************************************************/ + +/* + * nv_trim_space() - remove any leading and trailing whitespace from a string + * and return a pointer to the modified string. The original string may be + * modified; the returned value should *NOT* be deallocated with free(), since + * it may point somewhere other than the beginning of the original string. If + * the original string was a malloc()ed buffer, that string should be stored + * separately from the returned value of nv_strip_space, and freed. + */ + +char *nv_trim_space(char *string) { + char *ret, *end; + + for (ret = string; *ret && isspace(*ret); ret++); + for (end = ret + strlen(ret); end >= ret && isspace(*end); end--) { + *end = '\0'; + } + + return ret; +} + +/* + * trim_char() - helper function to remove a character from the initial and + * final positions of a string, and optionally report how many replacements + * were made. The returned value should not be free()d (see nv_trim_space()). + */ + +static char *trim_char(char *string, char trim, int *count) { + int len, replaced = 0; + + if (!string || trim == '\0') { + return NULL; + } + + len = strlen(string); + + if (string[0] == trim) { + string++; + replaced++; + } + + if (string[len - 1] == trim) { + string[len - 1] = '\0'; + replaced++; + } + + if (count) { + *count = replaced; + } + + return string; +} + +/* + * nv_trim_char() - remove a character from the initial and final positions of + * a string. The returned value should not be free()d (see nv_trim_space()). + */ + +char *nv_trim_char(char *string, char trim) { + return trim_char(string, trim, NULL); +} + +/* + * nv_trim_char_strict() - remove a character from the initial and final + * positions of a string. If no replacements were made, or if replacements were + * made at both positions, return the modified string. Otherwise, return NULL. + * The returned value should not be free()d (see nv_trim_space()). + */ + +char *nv_trim_char_strict(char *string, char trim) { + int count = 0; + char *trimmed; + + trimmed = trim_char(string, trim, &count); + + if (count == 0 || count == 2) { + return trimmed; + } + + return NULL; +} diff --git a/common-utils/common-utils.h b/common-utils/common-utils.h index 7e2838f..6dced25 100644 --- a/common-utils/common-utils.h +++ b/common-utils/common-utils.h @@ -70,6 +70,7 @@ char *nvasprintf(const char *fmt, ...) NV_ATTRIBUTE_PRINTF(1, 2); void nvfree(void *s); char *tilde_expansion(const char *str); +char *nv_prepend_to_string_list(char *list, const char *item, const char *delim); TextRows *nv_format_text_rows(const char *prefix, const char *str, @@ -89,6 +90,10 @@ void fmt(FILE *stream, const char *prefix, const char *fmt, ...) NV_ATTRIBUTE_PR char *fget_next_line(FILE *fp, int *eof); +char *nv_trim_space(char *string); +char *nv_trim_char(char *string, char trim); +char *nv_trim_char_strict(char *string, char trim); + /* * NV_VSNPRINTF(): macro that assigns buf using vsnprintf(). This is * correct for differing semantics of the vsnprintf() return value: diff --git a/install-from-cwd.c b/install-from-cwd.c index 7fe719d..c2d9a0d 100644 --- a/install-from-cwd.c +++ b/install-from-cwd.c @@ -1023,7 +1023,7 @@ static int assisted_module_signing(Options *op, Package *p) "existing key pair, or 'No' to generate a " "new key pair."); if (generate_keys) { - char *cmdline; + char *cmdline, *x509_hash; int ret; if (!op->utils[OPENSSL]) { @@ -1032,6 +1032,61 @@ static int assisted_module_signing(Options *op, Package *p) 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); + + 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 a key pair using openssl. @@ -1044,7 +1099,8 @@ static int assisted_module_signing(Options *op, Package *p) "rsa:2048 -days 7300 -nodes -sha256 -subj " "\"/CN=nvidia-installer generated signing key/\"" " -keyout " SECKEY_NAME " -outform DER -out " - PUBKEY_NAME, NULL); + PUBKEY_NAME, " -", x509_hash, NULL); + nvfree(x509_hash); ret = run_command(op, cmdline, NULL, TRUE, 8, TRUE); @@ -1176,31 +1176,55 @@ int test_kernel_module(Options *op, Package *p) nvfree(module_path); if (ret != 0) { - int ignore_error; + int ignore_error = FALSE, secureboot, module_sig_force, enokey; + const char *probable_reason, *signature_related; + + enokey = (-ret == ENOKEY); + secureboot = (secure_boot_enabled() == 1); + module_sig_force = + (test_kernel_config_option(op, p, "CONFIG_MODULE_SIG_FORCE") == + KERNEL_CONFIG_OPTION_DEFINED); + + if (enokey) { + probable_reason = ","; + signature_related = ""; + } else if (module_sig_force) { + probable_reason = ". CONFIG_MODULE_SIG_FORCE is set on the target " + "kernel, so this is likely"; + } else if (secureboot) { + probable_reason = ". Secure boot is enabled on this system, so " + "this is likely"; + } else { + probable_reason = ", possibly"; + } - /* Handle cases where we might want to allow the kernel module to be - * installed, even though the test load failed. */ - switch (-ret) { - case ENOKEY: + if (!enokey) { + signature_related = "if this module loading failure is due to the " + "lack of a trusted signature, "; + } + + if (enokey || secureboot || module_sig_force || op->expert) { if (op->kernel_module_signed) { + ignore_error = ui_yes_no(op, TRUE, - "The signed kernel module failed to " - "load, because the kernel does not " - "trust any key which is capable of " - "verifying the module signature. " - "Would you like to install the signed " - "kernel module anyway?\n\nNote that " - "you will not be able to load the " - "installed module until after a key " - "that can verify the module signature " - "is added to a key database that is " - "trusted by the kernel. This will " - "likely require rebooting your " - "computer."); + "The signed kernel module failed to " + "load%s because the kernel does not " + "trust any key which is capable of " + "verifying the module signature. " + "Would you like to install the signed " + "kernel module anyway?\n\nNote that %s" + "you will not be able to load the " + "installed module until after a key " + "that can verify the module signature " + "is added to a key database that is " + "trusted by the kernel. This will " + "likely require rebooting your " + "computer.", probable_reason, + signature_related); } else { - char *secureboot_message, *dkms_message; + const char *secureboot_message, *dkms_message; - secureboot_message = secure_boot_enabled() == 1 ? + secureboot_message = secureboot == 1 ? "and sign the kernel module when " "prompted to do so." : "and set the --module-signing-secret-" @@ -1214,17 +1238,12 @@ int test_kernel_module(Options *op, Package *p) "with DKMS, so please select the " "non-DKMS option when building the " "kernel module to be signed." : ""; - - ui_error(op, "The kernel module failed to load, because it " + ui_error(op, "The kernel module failed to load%s because it " "was not signed by a key that is trusted by the " "kernel. Please try installing the driver again, %s%s", - secureboot_message, dkms_message); + probable_reason, secureboot_message, dkms_message); ignore_error = FALSE; } - break; - - default: - ignore_error = FALSE; } if (ignore_error) { @@ -1614,6 +1633,22 @@ KernelConfigOptionStatus test_kernel_config_option(Options* op, Package *p, /* + * guess_module_signing_hash() - return the hash algorithm used for + * signing kernel modules, or NULL if it can't be determined. + */ + +char *guess_module_signing_hash(Options *op, Package *p) +{ + char *ret; + + if (run_conftest(op, p, "guess_module_signing_hash", &ret)) { + return ret; + } + + return NULL; +} + +/* *************************************************************************** * local static routines *************************************************************************** @@ -46,6 +46,7 @@ char *get_kernel_name (Options*); KernelConfigOptionStatus test_kernel_config_option (Options*, Package*, const char*); int sign_kernel_module (Options*, const char*, int); +char *guess_module_signing_hash (Options*, Package*); #ifndef ENOKEY #define ENOKEY 126 /* Required key not available */ @@ -2485,47 +2485,46 @@ static int nouveau_is_present(void) -/* - * Write a modprobe configuration fragment to disable loading of - * nouveau: - * - * if [ -d /etc/modprobe.d ]; then - * name=/etc/modprobe.d/nvidia-installer-nouveau-blacklist - * echo "# generated by nvidia-installer" > $name - * echo "blacklist nouveau" >> $name - * echo "options nouveau modeset=0" >> $name - * fi - * - * Returns TRUE if successful; returns FALSE if there was a failure. - */ - -#define ETC_MODPROBE_D "/etc/modprobe.d" -#define ETC_MODPROBE_D_FILE \ - ETC_MODPROBE_D "/nvidia-installer-disable-nouveau.conf" +static const char* modprobe_directories[] = { "/etc/modprobe.d", + "/usr/lib/modprobe.d" }; +#define DISABLE_NOUVEAU_FILE "/nvidia-installer-disable-nouveau.conf" /* * this checksum is the result of compute_crc() for the file contents * written in blacklist_nouveau() */ -#define ETC_MODPROBE_D_FILE_CKSUM 3728279991U +#define DISABLE_NOUVEAU_FILE_CKSUM 3728279991U + +/* + * blacklist_filename() - generate the filename of a blacklist file. The + * caller should ensure that the directory exists, or be able to handle + * failures correctly if the directory does not exist. + */ +static char *blacklist_filename(const char *directory) +{ + return nvstrcat(directory, DISABLE_NOUVEAU_FILE, NULL); +} -static int blacklist_nouveau(void) +static char *write_blacklist_file(const char *directory) { int ret; struct stat stat_buf; FILE *file; + char *filename; - ret = stat(ETC_MODPROBE_D, &stat_buf); + ret = stat(directory, &stat_buf); - if (ret != 0) { - return FALSE; + if (ret != 0 || !S_ISDIR(stat_buf.st_mode)) { + return NULL; } - file = fopen(ETC_MODPROBE_D_FILE, "w+"); + filename = blacklist_filename(directory); + file = fopen(filename, "w+"); if (!file) { - return FALSE; + nvfree(filename); + return NULL; } fprintf(file, "# generated by nvidia-installer\n"); @@ -2535,30 +2534,71 @@ static int blacklist_nouveau(void) ret = fclose(file); if (ret != 0) { - return FALSE; + nvfree(filename); + return NULL; } - return TRUE; + return filename; } - /* - * Check if the nouveau blacklist file is already present with the - * contents that we expect + * Write modprobe configuration fragments to disable loading of + * nouveau: + * + * for directory in /etc/modprobe.d /usr/lib/modprobe.d; do + * if [ -d $directory ]; then + * name=$directory/nvidia-installer-nouveau-blacklist.conf + * echo "# generated by nvidia-installer" > $name + * echo "blacklist nouveau" >> $name + * echo "options nouveau modeset=0" >> $name + * fi + * done + * + * Returns a list of written configuration files if successful; + * returns NULL if there was a failure. */ -static int nouveau_blacklist_file_is_present(Options *op) +static char *blacklist_nouveau(void) { - if (access(ETC_MODPROBE_D_FILE, R_OK) != 0) { - return FALSE; + int i; + char *filelist = NULL; + + for (i = 0; i < ARRAY_LEN(modprobe_directories); i++) { + char *filename = write_blacklist_file(modprobe_directories[i]); + if (filename) { + filelist = nv_prepend_to_string_list(filelist, filename, ", "); + nvfree(filename); + } } - if (compute_crc(op, ETC_MODPROBE_D_FILE) != ETC_MODPROBE_D_FILE_CKSUM) { - return FALSE; + return filelist; +} + + + +/* + * Check if any nouveau blacklist file is already present with the + * contents that we expect, and return the paths to any found files, + * or NULL if no matching files were found + */ + +static char *nouveau_blacklist_file_is_present(Options *op) +{ + int i; + char *filelist = NULL; + + for (i = 0; i < ARRAY_LEN(modprobe_directories); i++) { + char *filename = blacklist_filename(modprobe_directories[i]); + + if ((access(filename, R_OK) == 0) && + (compute_crc(op, filename) == DISABLE_NOUVEAU_FILE_CKSUM)) { + filelist = nv_prepend_to_string_list(filelist, filename, ", "); + } + nvfree(filename); } - return TRUE; + return filelist; } @@ -2575,6 +2615,7 @@ static int nouveau_blacklist_file_is_present(Options *op) int check_for_nouveau(Options *op) { int ret; + char *blacklist_files; #define NOUVEAU_POINTER_MESSAGE \ "Please consult the NVIDIA driver README and your Linux " \ @@ -2590,14 +2631,17 @@ int check_for_nouveau(Options *op) "driver, and must be disabled before proceeding. " NOUVEAU_POINTER_MESSAGE); - if (nouveau_blacklist_file_is_present(op)) { - ui_warn(op, "The modprobe configuration file to disable Nouveau, " - ETC_MODPROBE_D_FILE ", is already present. Please be " - "sure you have rebooted your system since that file was " + blacklist_files = nouveau_blacklist_file_is_present(op); + + if (blacklist_files) { + ui_warn(op, "One or more modprobe configuration files to disable " + "Nouveau are already present at: %s. Please be " + "sure you have rebooted your system since these files were " "written. If you have rebooted, then Nouveau may be enabled " "for other reasons, such as being included in the system " "initial ramdisk or in your X configuration file. " - NOUVEAU_POINTER_MESSAGE); + NOUVEAU_POINTER_MESSAGE, blacklist_files); + nvfree(blacklist_files); return FALSE; } @@ -2607,18 +2651,19 @@ int check_for_nouveau(Options *op) "to attempt to create this modprobe file for you?"); if (ret) { - ret = blacklist_nouveau(); + blacklist_files = blacklist_nouveau(); - if (ret) { - ui_message(op, "The modprobe configuration file to disable " - "Nouveau, " ETC_MODPROBE_D_FILE ", has been written. " + if (blacklist_files) { + ui_message(op, "One or more modprobe configuration files to " + "disable Nouveau, have been written. " "For some distributions, this may be sufficient to " "disable Nouveau; other distributions may require " "modification of the initial ramdisk. Please reboot " "your system and attempt NVIDIA driver installation " "again. Note if you later wish to reenable Nouveau, " - "you will need to delete the file " - ETC_MODPROBE_D_FILE "."); + "you will need to delete these files: %s", + blacklist_files); + nvfree(blacklist_files); } else { ui_warn(op, "Unable to alter the nouveau modprobe configuration. " NOUVEAU_POINTER_MESSAGE); diff --git a/nvidia-installer.c b/nvidia-installer.c index 0117c5a..8b2ddc1 100644 --- a/nvidia-installer.c +++ b/nvidia-installer.c @@ -402,6 +402,9 @@ static void parse_commandline(int argc, char *argv[], Options *op) case MODULE_SIGNING_HASH_OPTION: op->module_signing_hash = strval; break; + case MODULE_SIGNING_X509_HASH_OPTION: + op->module_signing_x509_hash = strval; + break; default: goto fail; } diff --git a/nvidia-installer.h b/nvidia-installer.h index ea95051..ee47562 100644 --- a/nvidia-installer.h +++ b/nvidia-installer.h @@ -202,6 +202,7 @@ typedef struct __options { char *module_signing_script; char *module_signing_key_path; char *module_signing_hash; + char *module_signing_x509_hash; int kernel_module_signed; diff --git a/option_table.h b/option_table.h index a5854ae..8ada92c 100644 --- a/option_table.h +++ b/option_table.h @@ -90,7 +90,8 @@ enum { MODULE_SIGNING_PUBLIC_KEY_OPTION, MODULE_SIGNING_SCRIPT_OPTION, MODULE_SIGNING_KEY_PATH_OPTION, - MODULE_SIGNING_HASH_OPTION + MODULE_SIGNING_HASH_OPTION, + MODULE_SIGNING_X509_HASH_OPTION, }; static const NVGetoptOption __options[] = { @@ -596,6 +597,13 @@ static const NVGetoptOption __options[] = { "be recognizable by the module signing tool. Default: select a hash " "algorithm automatically, based on the kernel's configuration." }, + { "module-signing-x509-hash", MODULE_SIGNING_X509_HASH_OPTION, + NVGETOPT_STRING_ARGUMENT, NULL, + "Specify a cryptographic hash algorithm to use for signing X.509 " + "certificates generated by nvidia-installer. The hash algorithm " + "name must be one of the message digest algorithms recognized by " + "the x509(1) command." }, + /* Orphaned options: These options were in the long_options table in * nvidia-installer.c but not in the help. */ { "debug", 'd', 0, NULL,NULL }, @@ -1 +1 @@ -NVIDIA_VERSION = 319.12 +NVIDIA_VERSION = 319.17 |