summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2022-08-02 23:38:59 +0200
committerLinus Torvalds <torvalds@linux-foundation.org>2022-08-02 23:38:59 +0200
commit6991a564f59742a0926be7421dff370135c44a97 (patch)
treea3faa053ada66b81be66516e1f0a824a284164af
parentMerge tag 'execve-v5.20-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git... (diff)
parentdm: verity-loadpin: Drop use of dm_table_get_num_targets() (diff)
downloadlinux-6991a564f59742a0926be7421dff370135c44a97.tar.xz
linux-6991a564f59742a0926be7421dff370135c44a97.zip
Merge tag 'hardening-v5.20-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/kees/linux
Pull hardening updates from Kees Cook: - Fix Sparse warnings with randomizd kstack (GONG, Ruiqi) - Replace uintptr_t with unsigned long in usercopy (Jason A. Donenfeld) - Fix Clang -Wforward warning in LKDTM (Justin Stitt) - Fix comment to correctly refer to STRICT_DEVMEM (Lukas Bulwahn) - Introduce dm-verity binding logic to LoadPin LSM (Matthias Kaehlcke) - Clean up warnings and overflow and KASAN tests (Kees Cook) * tag 'hardening-v5.20-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/kees/linux: dm: verity-loadpin: Drop use of dm_table_get_num_targets() kasan: test: Silence GCC 12 warnings drivers: lkdtm: fix clang -Wformat warning x86: mm: refer to the intended config STRICT_DEVMEM in a comment dm: verity-loadpin: Use CONFIG_SECURITY_LOADPIN_VERITY for conditional compilation LoadPin: Enable loading from trusted dm-verity devices dm: Add verity helpers for LoadPin stack: Declare {randomize_,}kstack_offset to fix Sparse warnings lib: overflow: Do not define 64-bit tests on 32-bit MAINTAINERS: Add a general "kernel hardening" section usercopy: use unsigned long instead of uintptr_t
-rw-r--r--MAINTAINERS21
-rw-r--r--drivers/md/Makefile1
-rw-r--r--drivers/md/dm-verity-loadpin.c75
-rw-r--r--drivers/md/dm-verity-target.c33
-rw-r--r--drivers/md/dm-verity.h4
-rw-r--r--drivers/misc/lkdtm/bugs.c2
-rw-r--r--include/linux/dm-verity-loadpin.h27
-rw-r--r--include/uapi/linux/loadpin.h22
-rw-r--r--init/main.c1
-rw-r--r--lib/overflow_kunit.c6
-rw-r--r--lib/test_kasan.c10
-rw-r--r--mm/usercopy.c2
-rw-r--r--security/loadpin/Kconfig16
-rw-r--r--security/loadpin/loadpin.c167
14 files changed, 379 insertions, 8 deletions
diff --git a/MAINTAINERS b/MAINTAINERS
index 44e966f03136..dbba9d832389 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -4998,7 +4998,7 @@ R: Nick Desaulniers <ndesaulniers@google.com>
L: llvm@lists.linux.dev
S: Supported
B: https://github.com/ClangBuiltLinux/linux/issues
-T: git git://git.kernel.org/pub/scm/linux/kernel/git/kees/linux.git for-next/clang/features
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/kees/linux.git for-next/hardening
F: include/linux/cfi.h
F: kernel/cfi.c
@@ -7909,6 +7909,7 @@ FORTIFY_SOURCE
M: Kees Cook <keescook@chromium.org>
L: linux-hardening@vger.kernel.org
S: Supported
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/kees/linux.git for-next/hardening
F: include/linux/fortify-string.h
F: lib/test_fortify/*
F: scripts/test_fortify.sh
@@ -8351,6 +8352,7 @@ GCC PLUGINS
M: Kees Cook <keescook@chromium.org>
L: linux-hardening@vger.kernel.org
S: Maintained
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/kees/linux.git for-next/hardening
F: Documentation/kbuild/gcc-plugins.rst
F: scripts/Makefile.gcc-plugins
F: scripts/gcc-plugins/
@@ -10878,6 +10880,17 @@ F: scripts/mk*
F: scripts/mod/
F: scripts/package/
+KERNEL HARDENING (not covered by other areas)
+M: Kees Cook <keescook@chromium.org>
+L: linux-hardening@vger.kernel.org
+S: Supported
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/kees/linux.git for-next/hardening
+F: include/linux/overflow.h
+F: include/linux/randomize_kstack.h
+F: mm/usercopy.c
+K: \b(add|choose)_random_kstack_offset\b
+K: \b__check_(object_size|heap_object)\b
+
KERNEL JANITORS
L: kernel-janitors@vger.kernel.org
S: Odd Fixes
@@ -11688,7 +11701,7 @@ F: drivers/media/usb/dvb-usb-v2/lmedm04*
LOADPIN SECURITY MODULE
M: Kees Cook <keescook@chromium.org>
S: Supported
-T: git git://git.kernel.org/pub/scm/linux/kernel/git/kees/linux.git lsm/loadpin
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/kees/linux.git for-next/hardening
F: Documentation/admin-guide/LSM/LoadPin.rst
F: security/loadpin/
@@ -18026,7 +18039,7 @@ M: Kees Cook <keescook@chromium.org>
R: Andy Lutomirski <luto@amacapital.net>
R: Will Drewry <wad@chromium.org>
S: Supported
-T: git git://git.kernel.org/pub/scm/linux/kernel/git/kees/linux.git seccomp
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/kees/linux.git for-next/seccomp
F: Documentation/userspace-api/seccomp_filter.rst
F: include/linux/seccomp.h
F: include/uapi/linux/seccomp.h
@@ -22174,7 +22187,7 @@ F: include/linux/yam.h
YAMA SECURITY MODULE
M: Kees Cook <keescook@chromium.org>
S: Supported
-T: git git://git.kernel.org/pub/scm/linux/kernel/git/kees/linux.git yama/tip
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/kees/linux.git for-next/hardening
F: Documentation/admin-guide/LSM/Yama.rst
F: security/yama/
diff --git a/drivers/md/Makefile b/drivers/md/Makefile
index 270f694850ec..84291e38dca8 100644
--- a/drivers/md/Makefile
+++ b/drivers/md/Makefile
@@ -83,6 +83,7 @@ obj-$(CONFIG_DM_LOG_WRITES) += dm-log-writes.o
obj-$(CONFIG_DM_INTEGRITY) += dm-integrity.o
obj-$(CONFIG_DM_ZONED) += dm-zoned.o
obj-$(CONFIG_DM_WRITECACHE) += dm-writecache.o
+obj-$(CONFIG_SECURITY_LOADPIN_VERITY) += dm-verity-loadpin.o
ifeq ($(CONFIG_DM_INIT),y)
dm-mod-objs += dm-init.o
diff --git a/drivers/md/dm-verity-loadpin.c b/drivers/md/dm-verity-loadpin.c
new file mode 100644
index 000000000000..387ec43aef72
--- /dev/null
+++ b/drivers/md/dm-verity-loadpin.c
@@ -0,0 +1,75 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+#include <linux/list.h>
+#include <linux/kernel.h>
+#include <linux/dm-verity-loadpin.h>
+
+#include "dm.h"
+#include "dm-core.h"
+#include "dm-verity.h"
+
+#define DM_MSG_PREFIX "verity-loadpin"
+
+LIST_HEAD(dm_verity_loadpin_trusted_root_digests);
+
+static bool is_trusted_verity_target(struct dm_target *ti)
+{
+ u8 *root_digest;
+ unsigned int digest_size;
+ struct dm_verity_loadpin_trusted_root_digest *trd;
+ bool trusted = false;
+
+ if (!dm_is_verity_target(ti))
+ return false;
+
+ if (dm_verity_get_root_digest(ti, &root_digest, &digest_size))
+ return false;
+
+ list_for_each_entry(trd, &dm_verity_loadpin_trusted_root_digests, node) {
+ if ((trd->len == digest_size) &&
+ !memcmp(trd->data, root_digest, digest_size)) {
+ trusted = true;
+ break;
+ }
+ }
+
+ kfree(root_digest);
+
+ return trusted;
+}
+
+/*
+ * Determines whether the file system of a superblock is located on
+ * a verity device that is trusted by LoadPin.
+ */
+bool dm_verity_loadpin_is_bdev_trusted(struct block_device *bdev)
+{
+ struct mapped_device *md;
+ struct dm_table *table;
+ struct dm_target *ti;
+ int srcu_idx;
+ bool trusted = false;
+
+ if (list_empty(&dm_verity_loadpin_trusted_root_digests))
+ return false;
+
+ md = dm_get_md(bdev->bd_dev);
+ if (!md)
+ return false;
+
+ table = dm_get_live_table(md, &srcu_idx);
+
+ if (table->num_targets != 1)
+ goto out;
+
+ ti = dm_table_get_target(table, 0);
+
+ if (is_trusted_verity_target(ti))
+ trusted = true;
+
+out:
+ dm_put_live_table(md, srcu_idx);
+ dm_put(md);
+
+ return trusted;
+}
diff --git a/drivers/md/dm-verity-target.c b/drivers/md/dm-verity-target.c
index 75b66dd67633..4fd853a56b1a 100644
--- a/drivers/md/dm-verity-target.c
+++ b/drivers/md/dm-verity-target.c
@@ -19,6 +19,7 @@
#include <linux/module.h>
#include <linux/reboot.h>
#include <linux/scatterlist.h>
+#include <linux/string.h>
#define DM_MSG_PREFIX "verity"
@@ -1309,10 +1310,40 @@ bad:
return r;
}
+/*
+ * Check whether a DM target is a verity target.
+ */
+bool dm_is_verity_target(struct dm_target *ti)
+{
+ return ti->type->module == THIS_MODULE;
+}
+
+/*
+ * Get the root digest of a verity target.
+ *
+ * Returns a copy of the root digest, the caller is responsible for
+ * freeing the memory of the digest.
+ */
+int dm_verity_get_root_digest(struct dm_target *ti, u8 **root_digest, unsigned int *digest_size)
+{
+ struct dm_verity *v = ti->private;
+
+ if (!dm_is_verity_target(ti))
+ return -EINVAL;
+
+ *root_digest = kmemdup(v->root_digest, v->digest_size, GFP_KERNEL);
+ if (*root_digest == NULL)
+ return -ENOMEM;
+
+ *digest_size = v->digest_size;
+
+ return 0;
+}
+
static struct target_type verity_target = {
.name = "verity",
.features = DM_TARGET_IMMUTABLE,
- .version = {1, 8, 0},
+ .version = {1, 8, 1},
.module = THIS_MODULE,
.ctr = verity_ctr,
.dtr = verity_dtr,
diff --git a/drivers/md/dm-verity.h b/drivers/md/dm-verity.h
index 4e769d13473a..c832cc3e3d24 100644
--- a/drivers/md/dm-verity.h
+++ b/drivers/md/dm-verity.h
@@ -129,4 +129,8 @@ extern int verity_hash(struct dm_verity *v, struct ahash_request *req,
extern int verity_hash_for_block(struct dm_verity *v, struct dm_verity_io *io,
sector_t block, u8 *digest, bool *is_zero);
+extern bool dm_is_verity_target(struct dm_target *ti);
+extern int dm_verity_get_root_digest(struct dm_target *ti, u8 **root_digest,
+ unsigned int *digest_size);
+
#endif /* DM_VERITY_H */
diff --git a/drivers/misc/lkdtm/bugs.c b/drivers/misc/lkdtm/bugs.c
index 009239ad1d8a..48821f4c2b21 100644
--- a/drivers/misc/lkdtm/bugs.c
+++ b/drivers/misc/lkdtm/bugs.c
@@ -29,7 +29,7 @@ struct lkdtm_list {
#if defined(CONFIG_FRAME_WARN) && (CONFIG_FRAME_WARN > 0)
#define REC_STACK_SIZE (_AC(CONFIG_FRAME_WARN, UL) / 2)
#else
-#define REC_STACK_SIZE (THREAD_SIZE / 8)
+#define REC_STACK_SIZE (THREAD_SIZE / 8UL)
#endif
#define REC_NUM_DEFAULT ((THREAD_SIZE / REC_STACK_SIZE) * 2)
diff --git a/include/linux/dm-verity-loadpin.h b/include/linux/dm-verity-loadpin.h
new file mode 100644
index 000000000000..552b817ab102
--- /dev/null
+++ b/include/linux/dm-verity-loadpin.h
@@ -0,0 +1,27 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+#ifndef __LINUX_DM_VERITY_LOADPIN_H
+#define __LINUX_DM_VERITY_LOADPIN_H
+
+#include <linux/list.h>
+
+struct block_device;
+
+extern struct list_head dm_verity_loadpin_trusted_root_digests;
+
+struct dm_verity_loadpin_trusted_root_digest {
+ struct list_head node;
+ unsigned int len;
+ u8 data[];
+};
+
+#if IS_ENABLED(CONFIG_SECURITY_LOADPIN_VERITY)
+bool dm_verity_loadpin_is_bdev_trusted(struct block_device *bdev);
+#else
+static inline bool dm_verity_loadpin_is_bdev_trusted(struct block_device *bdev)
+{
+ return false;
+}
+#endif
+
+#endif /* __LINUX_DM_VERITY_LOADPIN_H */
diff --git a/include/uapi/linux/loadpin.h b/include/uapi/linux/loadpin.h
new file mode 100644
index 000000000000..daa6dbb8bb02
--- /dev/null
+++ b/include/uapi/linux/loadpin.h
@@ -0,0 +1,22 @@
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
+/*
+ * Copyright (c) 2022, Google LLC
+ */
+
+#ifndef _UAPI_LINUX_LOOP_LOADPIN_H
+#define _UAPI_LINUX_LOOP_LOADPIN_H
+
+#define LOADPIN_IOC_MAGIC 'L'
+
+/**
+ * LOADPIN_IOC_SET_TRUSTED_VERITY_DIGESTS - Set up the root digests of verity devices
+ * that loadpin should trust.
+ *
+ * Takes a file descriptor from which to read the root digests of trusted verity devices. The file
+ * is expected to contain a list of digests in ASCII format, with one line per digest. The ioctl
+ * must be issued on the securityfs attribute 'loadpin/dm-verity' (which can be typically found
+ * under /sys/kernel/security/loadpin/dm-verity).
+ */
+#define LOADPIN_IOC_SET_TRUSTED_VERITY_DIGESTS _IOW(LOADPIN_IOC_MAGIC, 0x00, unsigned int)
+
+#endif /* _UAPI_LINUX_LOOP_LOADPIN_H */
diff --git a/init/main.c b/init/main.c
index 0ee39cdcfcac..91642a4e69be 100644
--- a/init/main.c
+++ b/init/main.c
@@ -99,6 +99,7 @@
#include <linux/kcsan.h>
#include <linux/init_syscalls.h>
#include <linux/stackdepot.h>
+#include <linux/randomize_kstack.h>
#include <net/net_namespace.h>
#include <asm/io.h>
diff --git a/lib/overflow_kunit.c b/lib/overflow_kunit.c
index 475f0c064bf6..7e3e43679b73 100644
--- a/lib/overflow_kunit.c
+++ b/lib/overflow_kunit.c
@@ -91,6 +91,7 @@ DEFINE_TEST_ARRAY(u32) = {
{-4U, 5U, 1U, -9U, -20U, true, false, true},
};
+#if BITS_PER_LONG == 64
DEFINE_TEST_ARRAY(u64) = {
{0, 0, 0, 0, 0, false, false, false},
{1, 1, 2, 0, 1, false, false, false},
@@ -114,6 +115,7 @@ DEFINE_TEST_ARRAY(u64) = {
false, true, false},
{-15ULL, 10ULL, -5ULL, -25ULL, -150ULL, false, false, true},
};
+#endif
DEFINE_TEST_ARRAY(s8) = {
{0, 0, 0, 0, 0, false, false, false},
@@ -188,6 +190,8 @@ DEFINE_TEST_ARRAY(s32) = {
{S32_MIN, S32_MIN, 0, 0, 0, true, false, true},
{S32_MAX, S32_MAX, -2, 0, 1, true, false, true},
};
+
+#if BITS_PER_LONG == 64
DEFINE_TEST_ARRAY(s64) = {
{0, 0, 0, 0, 0, false, false, false},
@@ -216,6 +220,7 @@ DEFINE_TEST_ARRAY(s64) = {
{-128, -1, -129, -127, 128, false, false, false},
{0, -S64_MAX, -S64_MAX, S64_MAX, 0, false, false, false},
};
+#endif
#define check_one_op(t, fmt, op, sym, a, b, r, of) do { \
t _r; \
@@ -650,6 +655,7 @@ static struct kunit_case overflow_test_cases[] = {
KUNIT_CASE(s16_overflow_test),
KUNIT_CASE(u32_overflow_test),
KUNIT_CASE(s32_overflow_test),
+/* Clang 13 and earlier generate unwanted libcalls on 32-bit. */
#if BITS_PER_LONG == 64
KUNIT_CASE(u64_overflow_test),
KUNIT_CASE(s64_overflow_test),
diff --git a/lib/test_kasan.c b/lib/test_kasan.c
index c233b1a4e984..58c1b01ccfe2 100644
--- a/lib/test_kasan.c
+++ b/lib/test_kasan.c
@@ -131,6 +131,7 @@ static void kmalloc_oob_right(struct kunit *test)
ptr = kmalloc(size, GFP_KERNEL);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ptr);
+ OPTIMIZER_HIDE_VAR(ptr);
/*
* An unaligned access past the requested kmalloc size.
* Only generic KASAN can precisely detect these.
@@ -159,6 +160,7 @@ static void kmalloc_oob_left(struct kunit *test)
ptr = kmalloc(size, GFP_KERNEL);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ptr);
+ OPTIMIZER_HIDE_VAR(ptr);
KUNIT_EXPECT_KASAN_FAIL(test, *ptr = *(ptr - 1));
kfree(ptr);
}
@@ -171,6 +173,7 @@ static void kmalloc_node_oob_right(struct kunit *test)
ptr = kmalloc_node(size, GFP_KERNEL, 0);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ptr);
+ OPTIMIZER_HIDE_VAR(ptr);
KUNIT_EXPECT_KASAN_FAIL(test, ptr[0] = ptr[size]);
kfree(ptr);
}
@@ -191,6 +194,7 @@ static void kmalloc_pagealloc_oob_right(struct kunit *test)
ptr = kmalloc(size, GFP_KERNEL);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ptr);
+ OPTIMIZER_HIDE_VAR(ptr);
KUNIT_EXPECT_KASAN_FAIL(test, ptr[size + OOB_TAG_OFF] = 0);
kfree(ptr);
@@ -271,6 +275,7 @@ static void kmalloc_large_oob_right(struct kunit *test)
ptr = kmalloc(size, GFP_KERNEL);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ptr);
+ OPTIMIZER_HIDE_VAR(ptr);
KUNIT_EXPECT_KASAN_FAIL(test, ptr[size] = 0);
kfree(ptr);
}
@@ -410,6 +415,8 @@ static void kmalloc_oob_16(struct kunit *test)
ptr2 = kmalloc(sizeof(*ptr2), GFP_KERNEL);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ptr2);
+ OPTIMIZER_HIDE_VAR(ptr1);
+ OPTIMIZER_HIDE_VAR(ptr2);
KUNIT_EXPECT_KASAN_FAIL(test, *ptr1 = *ptr2);
kfree(ptr1);
kfree(ptr2);
@@ -756,6 +763,8 @@ static void ksize_unpoisons_memory(struct kunit *test)
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ptr);
real_size = ksize(ptr);
+ OPTIMIZER_HIDE_VAR(ptr);
+
/* This access shouldn't trigger a KASAN report. */
ptr[size] = 'x';
@@ -778,6 +787,7 @@ static void ksize_uaf(struct kunit *test)
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ptr);
kfree(ptr);
+ OPTIMIZER_HIDE_VAR(ptr);
KUNIT_EXPECT_KASAN_FAIL(test, ksize(ptr));
KUNIT_EXPECT_KASAN_FAIL(test, ((volatile char *)ptr)[0]);
KUNIT_EXPECT_KASAN_FAIL(test, ((volatile char *)ptr)[size]);
diff --git a/mm/usercopy.c b/mm/usercopy.c
index 4e1da708699b..c1ee15a98633 100644
--- a/mm/usercopy.c
+++ b/mm/usercopy.c
@@ -161,7 +161,7 @@ static inline void check_bogus_address(const unsigned long ptr, unsigned long n,
static inline void check_heap_object(const void *ptr, unsigned long n,
bool to_user)
{
- uintptr_t addr = (uintptr_t)ptr;
+ unsigned long addr = (unsigned long)ptr;
unsigned long offset;
struct folio *folio;
diff --git a/security/loadpin/Kconfig b/security/loadpin/Kconfig
index 91be65dec2ab..70e7985b2561 100644
--- a/security/loadpin/Kconfig
+++ b/security/loadpin/Kconfig
@@ -18,3 +18,19 @@ config SECURITY_LOADPIN_ENFORCE
If selected, LoadPin will enforce pinning at boot. If not
selected, it can be enabled at boot with the kernel parameter
"loadpin.enforce=1".
+
+config SECURITY_LOADPIN_VERITY
+ bool "Allow reading files from certain other filesystems that use dm-verity"
+ depends on SECURITY_LOADPIN && DM_VERITY=y && SECURITYFS
+ help
+ If selected LoadPin can allow reading files from filesystems
+ that use dm-verity. LoadPin maintains a list of verity root
+ digests it considers trusted. A verity backed filesystem is
+ considered trusted if its root digest is found in the list
+ of trusted digests.
+
+ The list of trusted verity can be populated through an ioctl
+ on the LoadPin securityfs entry 'dm-verity'. The ioctl
+ expects a file descriptor of a file with verity digests as
+ parameter. The file must be located on the pinned root and
+ contain a comma separated list of digests.
diff --git a/security/loadpin/loadpin.c b/security/loadpin/loadpin.c
index ad4e6756c038..6ab5f2bbf41f 100644
--- a/security/loadpin/loadpin.c
+++ b/security/loadpin/loadpin.c
@@ -18,6 +18,8 @@
#include <linux/path.h>
#include <linux/sched.h> /* current */
#include <linux/string_helpers.h>
+#include <linux/dm-verity-loadpin.h>
+#include <uapi/linux/loadpin.h>
static void report_load(const char *origin, struct file *file, char *operation)
{
@@ -43,6 +45,9 @@ static char *exclude_read_files[READING_MAX_ID];
static int ignore_read_file_id[READING_MAX_ID] __ro_after_init;
static struct super_block *pinned_root;
static DEFINE_SPINLOCK(pinned_root_spinlock);
+#ifdef CONFIG_SECURITY_LOADPIN_VERITY
+static bool deny_reading_verity_digests;
+#endif
#ifdef CONFIG_SYSCTL
@@ -171,7 +176,8 @@ static int loadpin_read_file(struct file *file, enum kernel_read_file_id id,
spin_unlock(&pinned_root_spinlock);
}
- if (IS_ERR_OR_NULL(pinned_root) || load_root != pinned_root) {
+ if (IS_ERR_OR_NULL(pinned_root) ||
+ ((load_root != pinned_root) && !dm_verity_loadpin_is_bdev_trusted(load_root->s_bdev))) {
if (unlikely(!enforce)) {
report_load(origin, file, "pinning-ignored");
return 0;
@@ -237,6 +243,7 @@ static int __init loadpin_init(void)
enforce ? "" : "not ");
parse_exclude();
security_add_hooks(loadpin_hooks, ARRAY_SIZE(loadpin_hooks), "loadpin");
+
return 0;
}
@@ -245,6 +252,164 @@ DEFINE_LSM(loadpin) = {
.init = loadpin_init,
};
+#ifdef CONFIG_SECURITY_LOADPIN_VERITY
+
+enum loadpin_securityfs_interface_index {
+ LOADPIN_DM_VERITY,
+};
+
+static int read_trusted_verity_root_digests(unsigned int fd)
+{
+ struct fd f;
+ void *data;
+ int rc;
+ char *p, *d;
+
+ if (deny_reading_verity_digests)
+ return -EPERM;
+
+ /* The list of trusted root digests can only be set up once */
+ if (!list_empty(&dm_verity_loadpin_trusted_root_digests))
+ return -EPERM;
+
+ f = fdget(fd);
+ if (!f.file)
+ return -EINVAL;
+
+ data = kzalloc(SZ_4K, GFP_KERNEL);
+ if (!data) {
+ rc = -ENOMEM;
+ goto err;
+ }
+
+ rc = kernel_read_file(f.file, 0, (void **)&data, SZ_4K - 1, NULL, READING_POLICY);
+ if (rc < 0)
+ goto err;
+
+ p = data;
+ p[rc] = '\0';
+ p = strim(p);
+
+ p = strim(data);
+ while ((d = strsep(&p, "\n")) != NULL) {
+ int len = strlen(d);
+ struct dm_verity_loadpin_trusted_root_digest *trd;
+
+ if (len % 2) {
+ rc = -EPROTO;
+ goto err;
+ }
+
+ len /= 2;
+
+ trd = kzalloc(struct_size(trd, data, len), GFP_KERNEL);
+ if (!trd) {
+ rc = -ENOMEM;
+ goto err;
+ }
+
+ if (hex2bin(trd->data, d, len)) {
+ kfree(trd);
+ rc = -EPROTO;
+ goto err;
+ }
+
+ trd->len = len;
+
+ list_add_tail(&trd->node, &dm_verity_loadpin_trusted_root_digests);
+ }
+
+ if (list_empty(&dm_verity_loadpin_trusted_root_digests)) {
+ rc = -EPROTO;
+ goto err;
+ }
+
+ kfree(data);
+ fdput(f);
+
+ return 0;
+
+err:
+ kfree(data);
+
+ /* any failure in loading/parsing invalidates the entire list */
+ {
+ struct dm_verity_loadpin_trusted_root_digest *trd, *tmp;
+
+ list_for_each_entry_safe(trd, tmp, &dm_verity_loadpin_trusted_root_digests, node) {
+ list_del(&trd->node);
+ kfree(trd);
+ }
+ }
+
+ /* disallow further attempts after reading a corrupt/invalid file */
+ deny_reading_verity_digests = true;
+
+ fdput(f);
+
+ return rc;
+}
+
+/******************************** securityfs ********************************/
+
+static long dm_verity_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
+{
+ void __user *uarg = (void __user *)arg;
+ unsigned int fd;
+ int rc;
+
+ switch (cmd) {
+ case LOADPIN_IOC_SET_TRUSTED_VERITY_DIGESTS:
+ rc = copy_from_user(&fd, uarg, sizeof(fd));
+ if (rc)
+ return rc;
+
+ return read_trusted_verity_root_digests(fd);
+
+ default:
+ return -EINVAL;
+ }
+}
+
+static const struct file_operations loadpin_dm_verity_ops = {
+ .unlocked_ioctl = dm_verity_ioctl,
+ .compat_ioctl = compat_ptr_ioctl,
+};
+
+/**
+ * init_loadpin_securityfs - create the securityfs directory for LoadPin
+ *
+ * We can not put this method normally under the loadpin_init() code path since
+ * the security subsystem gets initialized before the vfs caches.
+ *
+ * Returns 0 if the securityfs directory creation was successful.
+ */
+static int __init init_loadpin_securityfs(void)
+{
+ struct dentry *loadpin_dir, *dentry;
+
+ loadpin_dir = securityfs_create_dir("loadpin", NULL);
+ if (IS_ERR(loadpin_dir)) {
+ pr_err("LoadPin: could not create securityfs dir: %ld\n",
+ PTR_ERR(loadpin_dir));
+ return PTR_ERR(loadpin_dir);
+ }
+
+ dentry = securityfs_create_file("dm-verity", 0600, loadpin_dir,
+ (void *)LOADPIN_DM_VERITY, &loadpin_dm_verity_ops);
+ if (IS_ERR(dentry)) {
+ pr_err("LoadPin: could not create securityfs entry 'dm-verity': %ld\n",
+ PTR_ERR(dentry));
+ return PTR_ERR(dentry);
+ }
+
+ return 0;
+}
+
+fs_initcall(init_loadpin_securityfs);
+
+#endif /* CONFIG_SECURITY_LOADPIN_VERITY */
+
/* Should not be mutable after boot, so not listed in sysfs (perm == 0). */
module_param(enforce, int, 0);
MODULE_PARM_DESC(enforce, "Enforce module/firmware pinning");