[PATCH RFC 101/104] crypto: fips140: add FIPS 140 module loader

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Add a mechanism for loading a FIPS module from a byte array compiled
into vmlinux.

fips140-module.o and fips140-digest.o are files generated during the build
of the FIPS cryptographic module; fips140-module.o provides the following
symbols:

 - _binary_fips140_ko_start
 - _binary_fips140_ko_end

while fips140-digest.o provides the following symbol:

 - _binary_fips140_hmac_start

fips140-loader.c then uses the _binary_fips140_ko_start/_end byte array
with load_module_mem() in arch_initcall_sync call to load the
precompiled FIPS 140 module.

This code is partly based on the existing crypto/fips.c code and partly on
the Android FIPS 140 module [1].

[1]: https://android.googlesource.com/kernel/common/+/1ca1130ec62d/crypto/fips140-module.c

Only arm64 and x86-64 are explicitly supported for now.

Co-developed-by: Saeed Mirzamohammadi <saeed.mirzamohammadi@xxxxxxxxxx>
Signed-off-by: Vegard Nossum <vegard.nossum@xxxxxxxxxx>
---
 arch/arm64/kernel/vmlinux.lds.S   |  1 +
 crypto/Makefile                   | 24 ++++++++
 crypto/fips140-loader.c           | 96 +++++++++++++++++++++++++++++++
 include/asm-generic/vmlinux.lds.h | 37 +++++++++++-
 include/linux/fips.h              | 17 ++++++
 5 files changed, 174 insertions(+), 1 deletion(-)
 create mode 100644 crypto/fips140-loader.c

diff --git a/arch/arm64/kernel/vmlinux.lds.S b/arch/arm64/kernel/vmlinux.lds.S
index ad6133b89e7a..58a99b2de237 100644
--- a/arch/arm64/kernel/vmlinux.lds.S
+++ b/arch/arm64/kernel/vmlinux.lds.S
@@ -271,6 +271,7 @@ SECTIONS
 		INIT_RAM_FS
 		*(.init.altinstructions .init.bss)	/* from the EFI stub */
 	}
+	FIPS140
 	.exit.data : {
 		EXIT_DATA
 	}
diff --git a/crypto/Makefile b/crypto/Makefile
index 6c5d59369dac..e07e93b189fe 100644
--- a/crypto/Makefile
+++ b/crypto/Makefile
@@ -7,7 +7,9 @@ obj-$(CONFIG_CRYPTO) += crypto.o
 crypto-y := api.o cipher.o
 
 obj-$(CONFIG_CRYPTO_ENGINE) += crypto_engine.o
+ifneq ($(CONFIG_CRYPTO_FIPS140_EXTMOD),y)
 obj-$(CONFIG_CRYPTO_FIPS) += fips.o
+endif
 
 crypto_algapi-$(CONFIG_PROC_FS) += proc.o
 crypto_algapi-y := algapi.o scatterwalk.o $(crypto_algapi-y)
@@ -211,3 +213,25 @@ obj-$(CONFIG_CRYPTO_SIMD) += crypto_simd.o
 obj-$(CONFIG_CRYPTO_KDF800108_CTR) += kdf_sp800108.o
 
 obj-$(CONFIG_CRYPTO_KRB5) += krb5/
+
+#
+# Loader for standalone FIPS 140 module
+#
+
+obj-$(CONFIG_CRYPTO_FIPS140_EXTMOD) += fips140-loader.o
+
+#
+# Standalone FIPS 140 module
+#
+
+quiet_cmd_ld_bin = LD      $@
+      cmd_ld_bin = (cd "$(dir $<)" && \
+                    $(LD) -r -b binary -o $(abspath $@) $(notdir $<) && \
+                    $(OBJCOPY) --rename-section .data=$(2) $(abspath $@))
+
+$(obj)/fips140-module.o: $(src)/fips140.ko FORCE
+	$(call if_changed,ld_bin,__fips140_module)
+$(obj)/fips140-digest.o: $(src)/fips140.hmac FORCE
+	$(call if_changed,ld_bin,__fips140_digest)
+
+obj-$(CONFIG_CRYPTO_FIPS140_EXTMOD) += fips140-module.o fips140-digest.o fips140-api.o
diff --git a/crypto/fips140-loader.c b/crypto/fips140-loader.c
new file mode 100644
index 000000000000..865d45d46786
--- /dev/null
+++ b/crypto/fips140-loader.c
@@ -0,0 +1,96 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * FIPS 140-3 module loader.
+ *
+ * Copyright (c) 2008 Neil Horman <nhorman@xxxxxxxxxxxxx>
+ * Copyright 2021 Google LLC
+ * Copyright (c) 2025, Oracle and/or its affiliates.
+ */
+
+#include <linux/fips.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/panic.h>
+#include <linux/printk.h>
+#include <linux/string.h>
+
+#include <crypto/api.h>
+#include <crypto/hash.h>
+
+int fips_enabled;
+EXPORT_SYMBOL_GPL(fips_enabled);
+
+ATOMIC_NOTIFIER_HEAD(fips_fail_notif_chain);
+EXPORT_SYMBOL_GPL(fips_fail_notif_chain);
+
+void fips_fail_notify(void)
+{
+	if (fips_enabled)
+		atomic_notifier_call_chain(&fips_fail_notif_chain, 0, NULL);
+}
+EXPORT_SYMBOL_GPL(fips_fail_notify);
+
+/* defined in crypto/fips140-{module,digest}.o -OR- vmlinux.lds */
+EXPORT_SYMBOL_GPL(_binary_fips140_ko_start);
+EXPORT_SYMBOL_GPL(_binary_fips140_ko_end);
+EXPORT_SYMBOL_GPL(_binary_fips140_hmac_start);
+
+/* Process kernel command-line parameter at boot time. fips=0 or fips=1 */
+static int fips_enable(char *str)
+{
+	fips_enabled = !!simple_strtol(str, NULL, 0);
+	if (!fips_enabled)
+		pr_info("FIPS 140-3 module: disabled\n");
+
+	return 1;
+}
+
+__setup("fips=", fips_enable);
+
+static struct ctl_table crypto_sysctl_table[] = {
+	{
+		.procname	= "fips_enabled",
+		.data		= &fips_enabled,
+		.maxlen		= sizeof(int),
+		.mode		= 0444,
+		.proc_handler	= proc_dointvec,
+	},
+};
+
+static int __init fips_loader_init(void)
+{
+	void *ko_mem;
+	int err;
+	struct ctl_table_header *crypto_sysctls;
+
+	if (!fips_enabled) {
+		/* Add crypto sysctl for nonfips mode */
+		crypto_sysctls = register_sysctl("crypto", crypto_sysctl_table);
+		if (!crypto_sysctls)
+			pr_err("fips 140: failed to register sysctl for nonfips mode");
+
+		return 0;
+	}
+
+	/*
+	 * Duplicate the memory as the kernel module loader will
+	 * modify it and mess up the integrity check.
+	 */
+	ko_mem = kvmemdup(_binary_fips140_ko_start, _binary_fips140_ko_size, GFP_KERNEL);
+	if (!ko_mem) {
+		err = -ENOMEM;
+		goto out;
+	}
+
+	err = load_module_mem(ko_mem, _binary_fips140_ko_size);
+	if (err)
+		goto out;
+
+	kvfree(ko_mem);
+
+out:
+	if (err)
+		panic("FIPS 140-3 module: loading error\n");
+	return err;
+}
+arch_initcall_sync(fips_loader_init);
diff --git a/include/asm-generic/vmlinux.lds.h b/include/asm-generic/vmlinux.lds.h
index 1881d9b6b3ae..b4aee570223c 100644
--- a/include/asm-generic/vmlinux.lds.h
+++ b/include/asm-generic/vmlinux.lds.h
@@ -454,6 +454,40 @@ defined(CONFIG_AUTOFDO_CLANG) || defined(CONFIG_PROPELLER_CLANG)
 #endif
 #endif
 
+#ifdef CONFIG_CRYPTO_FIPS140_EXTMOD
+/*
+ * We have an external module; include it and its digest in their own
+ * named sections so they are easy to extract from the vmlinux ELF file
+ * after the kernel has been built.
+ */
+#define FIPS140 \
+	. = ALIGN(PAGE_SIZE); \
+	__fips140_module : AT(ADDR(__fips140_module) - LOAD_OFFSET) {	\
+		*(__fips140_module)					\
+	}								\
+	. = ALIGN(PAGE_SIZE);						\
+	__fips140_digest : AT(ADDR(__fips140_digest) - LOAD_OFFSET) {	\
+		*(__fips140_digest)					\
+	}
+#else
+#ifdef CONFIG_CRYPTO_FIPS140_EXTMOD
+/*
+ * We don't have an external module (yet) but the kernel has been built
+ * with the loader, so this just defines an empty byte array where the
+ * module will go eventually.
+ */
+#define FIPS140 \
+	_binary_fips140_ko_start = .; \
+	_binary_fips140_ko_end = .; \
+	_binary_fips140_hmac_start = .; \
+	_binary_fips140_hmac_end = .;
+#else
+#define FIPS140
+#endif
+#endif
+
+
+
 /*
  * Read only Data
  */
@@ -1145,7 +1179,8 @@ defined(CONFIG_AUTOFDO_CLANG) || defined(CONFIG_PROPELLER_CLANG)
 		INIT_CALLS						\
 		CON_INITCALL						\
 		INIT_RAM_FS						\
-	}
+	}								\
+	FIPS140
 
 #define BSS_SECTION(sbss_align, bss_align, stop_align)			\
 	. = ALIGN(sbss_align);						\
diff --git a/include/linux/fips.h b/include/linux/fips.h
index c6961e932fef..81560dfc6cef 100644
--- a/include/linux/fips.h
+++ b/include/linux/fips.h
@@ -2,12 +2,29 @@
 #ifndef _FIPS_H
 #define _FIPS_H
 
+#include <linux/init.h>
+
+#include <crypto/sha2.h> /* SHA256_DIGEST_SIZE */
+
 #ifdef CONFIG_CRYPTO_FIPS
+/*
+ * fips_enabled = FIPS mode was requested on the command line
+ * fips_operational = FIPS module has run self-tests etc. and is operational
+ */
 extern int fips_enabled;
+extern int fips_operational;
+
 extern struct atomic_notifier_head fips_fail_notif_chain;
 
 void fips_fail_notify(void);
 
+/* FIPS-certified module blob and digest */
+extern const u8 __initconst _binary_fips140_ko_start[];
+extern const u8 __initconst _binary_fips140_ko_end[];
+extern const u8 __initconst _binary_fips140_hmac_start[SHA256_DIGEST_SIZE];
+
+#define _binary_fips140_ko_size (_binary_fips140_ko_end - _binary_fips140_ko_start)
+
 #else
 #define fips_enabled 0
 
-- 
2.39.3





[Index of Archives]     [Kernel]     [Gnu Classpath]     [Gnu Crypto]     [DM Crypt]     [Netfilter]     [Bugtraq]
  Powered by Linux