diff --git a/crypto/Kconfig b/crypto/Kconfig index 65f71ac48961..532c454f57ee 100644 --- a/crypto/Kconfig +++ b/crypto/Kconfig @@ -79,6 +79,33 @@ config CRYPTO_FIPS140_MOD_EVAL_TESTING errors and support for a userspace interface to some of the module's services. This option should not be enabled in production builds. +config CRYPTO_FIPS140_MOD_DEBUG_INTEGRITY_CHECK + bool "Debug the integrity check in FIPS 140 module" + depends on CRYPTO_FIPS140_MOD + help + This option makes the FIPS 140 module provide debugfs files containing + the text and rodata that were used for the integrity check, i.e. the + runtime text and rodata with relocations and code patches unapplied. + This option also makes the module load even if the integrity check + fails so that these files can be used to debug the failure. (A + possible failure mode is that the kernel has added a new type of code + patching and the module needs to be updated to disable or unapply it.) + + This option must not be enabled in production builds. + + Example commands for debugging an integrity check failure: + + adb root + adb shell mount debugfs -t debugfs /sys/kernel/debug + adb shell cp /sys/kernel/debug/fips140/{text,rodata} /data/local/tmp/ + adb pull /data/local/tmp/text text.checked + adb pull /data/local/tmp/rodata rodata.checked + llvm-objcopy -O binary --only-section=.text fips140.ko text.orig + llvm-objcopy -O binary --only-section=.rodata fips140.ko rodata.orig + for f in {text,rodata}.{orig,checked}; do xxd -g1 $f > $f.xxd; done + vimdiff text.{orig,checked}.xxd + vimdiff rodata.{orig,checked}.xxd + config CRYPTO_ALGAPI tristate select CRYPTO_ALGAPI2 diff --git a/crypto/fips140-module.c b/crypto/fips140-module.c index 5c2a594dd26b..5969a57dbb73 100644 --- a/crypto/fips140-module.c +++ b/crypto/fips140-module.c @@ -23,6 +23,7 @@ #undef __DISABLE_EXPORTS #include +#include #include #include #include @@ -357,6 +358,67 @@ static void __init unapply_rodata_relocations(void *section, int section_size, } } +#ifdef CONFIG_CRYPTO_FIPS140_MOD_DEBUG_INTEGRITY_CHECK +static struct { + const void *text; + int textsize; + const void *rodata; + int rodatasize; +} saved_integrity_check_info; + +static ssize_t fips140_text_read(struct file *file, char __user *to, + size_t count, loff_t *ppos) +{ + return simple_read_from_buffer(to, count, ppos, + saved_integrity_check_info.text, + saved_integrity_check_info.textsize); +} + +static ssize_t fips140_rodata_read(struct file *file, char __user *to, + size_t count, loff_t *ppos) +{ + return simple_read_from_buffer(to, count, ppos, + saved_integrity_check_info.rodata, + saved_integrity_check_info.rodatasize); +} + +static const struct file_operations fips140_text_fops = { + .read = fips140_text_read, +}; + +static const struct file_operations fips140_rodata_fops = { + .read = fips140_rodata_read, +}; + +static void fips140_init_integrity_debug_files(const void *text, int textsize, + const void *rodata, + int rodatasize) +{ + struct dentry *dir; + + dir = debugfs_create_dir("fips140", NULL); + + saved_integrity_check_info.text = kmemdup(text, textsize, GFP_KERNEL); + saved_integrity_check_info.textsize = textsize; + if (saved_integrity_check_info.text) + debugfs_create_file("text", 0400, dir, NULL, + &fips140_text_fops); + + saved_integrity_check_info.rodata = kmemdup(rodata, rodatasize, + GFP_KERNEL); + saved_integrity_check_info.rodatasize = rodatasize; + if (saved_integrity_check_info.rodata) + debugfs_create_file("rodata", 0400, dir, NULL, + &fips140_rodata_fops); +} +#else /* CONFIG_CRYPTO_FIPS140_MOD_DEBUG_INTEGRITY_CHECK */ +static void fips140_init_integrity_debug_files(const void *text, int textsize, + const void *rodata, + int rodatasize) +{ +} +#endif /* !CONFIG_CRYPTO_FIPS140_MOD_DEBUG_INTEGRITY_CHECK */ + extern struct { u32 offset; u32 count; @@ -398,6 +460,9 @@ static bool __init check_fips140_module_hmac(void) offset_to_ptr(&fips140_rela_rodata.offset), fips140_rela_rodata.count); + fips140_init_integrity_debug_files(textcopy, textsize, + rodatacopy, rodatasize); + fips140_inject_integrity_failure(textcopy); tfm = crypto_alloc_shash("hmac(sha256)", 0, 0); @@ -538,10 +603,14 @@ fips140_init(void) */ if (!check_fips140_module_hmac()) { - pr_crit("integrity check failed -- giving up!\n"); - goto panic; + if (!IS_ENABLED(CONFIG_CRYPTO_FIPS140_MOD_DEBUG_INTEGRITY_CHECK)) { + pr_crit("integrity check failed -- giving up!\n"); + goto panic; + } + pr_crit("ignoring integrity check failure due to debug mode\n"); + } else { + pr_info("integrity check passed\n"); } - pr_info("integrity check passed\n"); complete_all(&fips140_tests_done);