For FIPS certification of a standalone FIPS module we need to be able to switch out the entire kernel crypto API during boot (if booting with fips=1 on the kernel command line). This is not easy, as it requires all users/callers of the crypto API to be updated. We've settled on using the "static call" functionality for this. Benefits include no speculative attack gadgets and no performance impact from indirect calls. The way it works is that all exported crypto API functions need to be annotated with DECLARE_CRYPTO_API() in headers and DEFINE_CRYPTO_API() in the implementations. This will essentially allocate a static call key for the given function and a static inline wrapper with the actual static call itself -- any references to the crypto API functions will be dynamically patched when the FIPS module is enabled. The static calls are recorded in a new ELF section, __crypto_api_keys, and are automatically updated when a module providing their targets is loaded. The macro FIPS_MODULE is not yet defined anywhere, but will be defined in a future patch for any code compiled as part of the FIPS module. Also define wrappers for things like module_init() so that we can record these in .fips_initcall and link what would usually be a bunch of modules together without running into errors because the resulting module has more than one init function. Signed-off-by: Vegard Nossum <vegard.nossum@xxxxxxxxxx> --- crypto/Kconfig | 16 ++++ include/asm-generic/vmlinux.lds.h | 1 + include/crypto/api.h | 154 ++++++++++++++++++++++++++++++ kernel/module/main.c | 26 ++++- 4 files changed, 195 insertions(+), 2 deletions(-) create mode 100644 include/crypto/api.h diff --git a/crypto/Kconfig b/crypto/Kconfig index 23bd98981ae8..a2696ea30bde 100644 --- a/crypto/Kconfig +++ b/crypto/Kconfig @@ -54,6 +54,22 @@ config CRYPTO_FIPS_VERSION This option provides the ability to override the FIPS Module Version. By default the KERNELRELEASE value is used. +config CRYPTO_FIPS140_EXTMOD + bool "FIPS 140-3 external module" + depends on CRYPTO_FIPS + help + Support loading a prebuilt FIPS 140-3 cryptographic module. + +config CRYPTO_FIPS140_HMAC_KEY + string "FIPS 140-3 external module HMAC key" + depends on CRYPTO_FIPS140_EXTMOD + default "Sphinx of black quartz, judge my vow" + help + This is the HMAC key used to build and verify the integrity of + the FIPS module. + + Must be at least 14 characters. + config CRYPTO_ALGAPI tristate select CRYPTO_ALGAPI2 diff --git a/include/asm-generic/vmlinux.lds.h b/include/asm-generic/vmlinux.lds.h index ae2d2359b79e..1881d9b6b3ae 100644 --- a/include/asm-generic/vmlinux.lds.h +++ b/include/asm-generic/vmlinux.lds.h @@ -707,6 +707,7 @@ defined(CONFIG_AUTOFDO_CLANG) || defined(CONFIG_PROPELLER_CLANG) KERNEL_CTORS() \ MCOUNT_REC() \ *(.init.rodata .init.rodata.*) \ + BOUNDED_SECTION(__crypto_api_keys) \ FTRACE_EVENTS() \ TRACE_SYSCALLS() \ KPROBE_BLACKLIST() \ diff --git a/include/crypto/api.h b/include/crypto/api.h new file mode 100644 index 000000000000..cda29e71ebef --- /dev/null +++ b/include/crypto/api.h @@ -0,0 +1,154 @@ +#ifndef _CRYPTO_API_H +#define _CRYPTO_API_H + +#include <linux/static_call.h> + +#if !defined(CONFIG_CRYPTO_FIPS140_EXTMOD) + +#define CRYPTO_API(name) name + +/* + * These are the definitions that get used when no standalone FIPS module + * is used: we simply forward everything to normal functions and function + * calls. + */ + +#define DECLARE_CRYPTO_API(name, ret_type, args_decl, args_call) \ + ret_type name args_decl; + +#define DEFINE_CRYPTO_API(name) \ + EXPORT_SYMBOL_GPL(name) + +#define crypto_module_init(fn) module_init(fn) +#define crypto_module_exit(fn) module_exit(fn) + +#define crypto_arch_initcall(fn) arch_initcall(fn) +#define crypto_subsys_initcall(fn) subsys_initcall(fn) +#define crypto_late_initcall(fn) late_initcall(fn) + +#define CRYPTO_MODULE_DEVICE_TABLE(type, name) MODULE_DEVICE_TABLE(type, name) + +#define crypto_module_cpu_feature_match(x, __initfunc) \ + module_cpu_feature_match(x, __initfunc) + +#else + +struct crypto_api_key { + struct static_call_key *key; + void *tramp; + void *func; +}; + +#ifndef FIPS_MODULE + +/* + * These are the definitions that get used for vmlinux and in-tree + * kernel modules. + * + * In this case, all references to the kernel crypto API functions will + * be replaced by wrappers that perform a call using the kernel's static_call + * functionality. + */ + +#define CRYPTO_API(name) nonfips_##name + +/* Consolidated version of different DECLARE_CRYPTO_API versions */ +#define DECLARE_CRYPTO_API(name, ret_type, args_decl, args_call) \ + ret_type nonfips_##name args_decl; \ + DECLARE_STATIC_CALL(crypto_##name##_key, nonfips_##name); \ + static inline ret_type name args_decl \ + { \ + return static_call(crypto_##name##_key) args_call; \ + } + +#define DEFINE_CRYPTO_API(name) \ + DEFINE_STATIC_CALL(crypto_##name##_key, nonfips_##name); \ + EXPORT_STATIC_CALL(crypto_##name##_key) + +#define DEFINE_CRYPTO_API_STUB(name) \ + DEFINE_STATIC_CALL_NULL(crypto_##name##_key, name); \ + EXPORT_STATIC_CALL(crypto_##name##_key) + +#define crypto_module_init(fn) module_init(fn) +#define crypto_module_exit(fn) module_exit(fn) + +#define crypto_arch_initcall(fn) arch_initcall(fn) +#define crypto_subsys_initcall(fn) subsys_initcall(fn) +#define crypto_late_initcall(fn) late_initcall(fn) + +#define CRYPTO_MODULE_DEVICE_TABLE(type, name) MODULE_DEVICE_TABLE(type, name) + +#define crypto_module_cpu_feature_match(x, __initfunc) \ + module_cpu_feature_match(x, __initfunc) + +#else /* defined(FIPS_MODULE) */ + +/* + * These are the definitions that get used for the FIPS module and + * its kernel modules. + * + * In this case, all crypto API functions resolve directly to their + * implementations, since they are all part of the FIPS module. + * + * We still need to declare the static call keys so we can update + * them when the FIPS modules have all been loaded. + */ + +#define CRYPTO_API(name) fips_##name + +/* Consolidated version of different DECLARE_CRYPTO_API versions */ +#define DECLARE_CRYPTO_API(name, ret_type, args_decl, args_call) \ + ret_type fips_##name args_decl; \ + DECLARE_STATIC_CALL(crypto_##name##_key, fips_##name); \ + static inline ret_type name args_decl \ + { \ + return fips_##name args_call; \ + } + +/* + * Create an entry for the static call key so we can initialize it + * in the FIPS module. + */ +// TODO: make this const initdata, probably +#define DEFINE_CRYPTO_API(name) \ + EXPORT_SYMBOL_GPL(fips_##name); \ + static struct crypto_api_key __##name##_key \ + __used \ + __section("__crypto_api_keys") \ + __aligned(__alignof__(struct crypto_api_key)) = \ + { \ + .key = &STATIC_CALL_KEY(crypto_##name##_key), \ + .tramp = STATIC_CALL_TRAMP_ADDR(crypto_##name##_key), \ + .func = &fips_##name, \ + }; + +#define crypto_module_init(fn) \ + static unsigned long __used __section(".fips_initcall") \ + __fips_##fn = (unsigned long) &fn; +#define crypto_module_exit(fn) \ + static unsigned long __used __section(".fips_exitcall") \ + __fips_##fn = (unsigned long) &fn; + +#define crypto_arch_initcall(fn) crypto_module_init(fn) +#define crypto_subsys_initcall(fn) crypto_module_init(fn) +#define crypto_late_initcall(fn) crypto_module_init(fn) + +/* + * We don't need to emit device tables or module aliases for the FIPS module, + * since it will all be loaded at once anyway. + */ +#define CRYPTO_MODULE_DEVICE_TABLE(type, name) + +#define crypto_module_cpu_feature_match(x, __initfunc) \ +static int __init cpu_feature_match_ ## x ## _init(void) \ +{ \ + if (!cpu_have_feature(cpu_feature(x))) \ + return -ENODEV; \ + return __initfunc(); \ +} \ +crypto_module_init(cpu_feature_match_ ## x ## _init) + +#endif /* defined(FIPS_MODULE) */ +#endif /* defined(CONFIG_CRYPTO_FIPS140_EXTMOD) */ + +#endif /* !_CRYPTO_API_H */ diff --git a/kernel/module/main.c b/kernel/module/main.c index 12ce4bad29ca..19a03c8659e2 100644 --- a/kernel/module/main.c +++ b/kernel/module/main.c @@ -7,6 +7,7 @@ #define INCLUDE_VERMAGIC +#include <crypto/api.h> #include <linux/export.h> #include <linux/extable.h> #include <linux/moduleloader.h> @@ -2956,6 +2957,24 @@ static int post_relocation(struct module *mod, const struct load_info *info) return module_finalize(info->hdr, info->sechdrs, mod); } +static void do_crypto_api(struct load_info *info) +{ +#ifdef CONFIG_CRYPTO_FIPS140_EXTMOD + struct crypto_api_key *crypto_api_keys; + unsigned int num_crypto_api_keys; + + unsigned int i; + + crypto_api_keys = section_objs(info, "__crypto_api_keys", + sizeof(*crypto_api_keys), &num_crypto_api_keys); + + for (i = 0; i < num_crypto_api_keys; ++i) { + struct crypto_api_key *key = &crypto_api_keys[i]; + __static_call_update(key->key, key->tramp, key->func); + } +#endif +} + /* Call module constructors. */ static void do_mod_ctors(struct module *mod) { @@ -3010,7 +3029,7 @@ module_param(async_probe, bool, 0644); * Keep it uninlined to provide a reliable breakpoint target, e.g. for the gdb * helper command 'lx-symbols'. */ -static noinline int do_init_module(struct module *mod, int flags) +static noinline int do_init_module(struct load_info *info, struct module *mod, int flags) { int ret = 0; struct mod_initfree *freeinit; @@ -3036,6 +3055,9 @@ static noinline int do_init_module(struct module *mod, int flags) freeinit->init_data = mod->mem[MOD_INIT_DATA].base; freeinit->init_rodata = mod->mem[MOD_INIT_RODATA].base; + if (flags & MODULE_INIT_MEM) + do_crypto_api(info); + do_mod_ctors(mod); /* Start the module */ if (mod->init != NULL) @@ -3498,7 +3520,7 @@ static int _load_module(struct load_info *info, const char __user *uargs, /* Done! */ trace_module_load(mod); - return do_init_module(mod, flags); + return do_init_module(info, mod, flags); sysfs_cleanup: mod_sysfs_teardown(mod); -- 2.39.3