All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 0/2] fsverity: killswitch sysctl
@ 2022-04-28 22:19 Boris Burkov
  2022-04-28 22:19 ` [PATCH 1/2] fsverity: factor out sysctl from signature.c Boris Burkov
  2022-04-28 22:19 ` [PATCH 2/2] fsverity: add mode sysctl Boris Burkov
  0 siblings, 2 replies; 5+ messages in thread
From: Boris Burkov @ 2022-04-28 22:19 UTC (permalink / raw)
  To: linux-fscrypt, kernel-team

These patches add a new fs-verity sysctl that allows the administrator
to set verity in a log-only audit mode or disable it entirely.

Boris Burkov (2):
  fsverity: factor out sysctl from signature.c
  fsverity: add mode sysctl

 fs/verity/Makefile           |   2 +
 fs/verity/enable.c           |   3 +
 fs/verity/fsverity_private.h |  24 ++++++++
 fs/verity/init.c             |   7 ++-
 fs/verity/measure.c          |   3 +
 fs/verity/open.c             |  14 ++++-
 fs/verity/read_metadata.c    |   3 +
 fs/verity/signature.c        |  68 +++++-----------------
 fs/verity/sysctl.c           | 110 +++++++++++++++++++++++++++++++++++
 fs/verity/verify.c           |  34 ++++++++++-
 include/linux/fsverity.h     |   4 +-
 11 files changed, 210 insertions(+), 62 deletions(-)
 create mode 100644 fs/verity/sysctl.c

-- 
2.30.2


^ permalink raw reply	[flat|nested] 5+ messages in thread

* [PATCH 1/2] fsverity: factor out sysctl from signature.c
  2022-04-28 22:19 [PATCH 0/2] fsverity: killswitch sysctl Boris Burkov
@ 2022-04-28 22:19 ` Boris Burkov
  2022-05-17 22:24   ` Eric Biggers
  2022-04-28 22:19 ` [PATCH 2/2] fsverity: add mode sysctl Boris Burkov
  1 sibling, 1 reply; 5+ messages in thread
From: Boris Burkov @ 2022-04-28 22:19 UTC (permalink / raw)
  To: linux-fscrypt, kernel-team

This is a preparation patch for adding a second sysctl to fs-verity.
Move the sysctl logic into its own file, so we can add more sysctls
unrelated to signatures.

Signed-off-by: Boris Burkov <boris@bur.io>
---
 fs/verity/Makefile           |  2 ++
 fs/verity/fsverity_private.h | 14 ++++++++++
 fs/verity/init.c             |  7 ++++-
 fs/verity/signature.c        | 54 +-----------------------------------
 fs/verity/sysctl.c           | 51 ++++++++++++++++++++++++++++++++++
 5 files changed, 74 insertions(+), 54 deletions(-)
 create mode 100644 fs/verity/sysctl.c

diff --git a/fs/verity/Makefile b/fs/verity/Makefile
index 435559a4fa9e..81a468ca0131 100644
--- a/fs/verity/Makefile
+++ b/fs/verity/Makefile
@@ -9,3 +9,5 @@ obj-$(CONFIG_FS_VERITY) += enable.o \
 			   verify.o
 
 obj-$(CONFIG_FS_VERITY_BUILTIN_SIGNATURES) += signature.o
+
+obj-$(CONFIG_SYSCTL) += sysctl.o
diff --git a/fs/verity/fsverity_private.h b/fs/verity/fsverity_private.h
index a7920434bae5..c416c1cd9371 100644
--- a/fs/verity/fsverity_private.h
+++ b/fs/verity/fsverity_private.h
@@ -136,6 +136,20 @@ int fsverity_get_descriptor(struct inode *inode,
 int __init fsverity_init_info_cache(void);
 void __init fsverity_exit_info_cache(void);
 
+/* sysctl.c */
+#ifdef CONFIG_SYSCTL
+int __init fsverity_sysctl_init(void);
+void __init fsverity_exit_sysctl(void);
+#else /* !CONFIG_SYSCTL */
+static inline int __init fsverity_sysctl_init(void)
+{
+	return 0;
+}
+static inline void __init fsverity_exit_sysctl(void)
+{
+}
+#endif /* !CONFIG_SYSCTL */
+
 /* signature.c */
 
 #ifdef CONFIG_FS_VERITY_BUILTIN_SIGNATURES
diff --git a/fs/verity/init.c b/fs/verity/init.c
index c98b7016f446..bd16495e8adf 100644
--- a/fs/verity/init.c
+++ b/fs/verity/init.c
@@ -45,13 +45,18 @@ static int __init fsverity_init(void)
 	if (err)
 		goto err_exit_info_cache;
 
-	err = fsverity_init_signature();
+	err = fsverity_sysctl_init();
 	if (err)
 		goto err_exit_workqueue;
+	err = fsverity_init_signature();
+	if (err)
+		goto err_exit_sysctl;
 
 	pr_debug("Initialized fs-verity\n");
 	return 0;
 
+err_exit_sysctl:
+	fsverity_exit_sysctl();
 err_exit_workqueue:
 	fsverity_exit_workqueue();
 err_exit_info_cache:
diff --git a/fs/verity/signature.c b/fs/verity/signature.c
index 143a530a8008..67a471e4b570 100644
--- a/fs/verity/signature.c
+++ b/fs/verity/signature.c
@@ -12,11 +12,7 @@
 #include <linux/slab.h>
 #include <linux/verification.h>
 
-/*
- * /proc/sys/fs/verity/require_signatures
- * If 1, all verity files must have a valid builtin signature.
- */
-static int fsverity_require_signatures;
+extern int fsverity_require_signatures;
 
 /*
  * Keyring that contains the trusted X.509 certificates.
@@ -87,49 +83,9 @@ int fsverity_verify_signature(const struct fsverity_info *vi,
 	return 0;
 }
 
-#ifdef CONFIG_SYSCTL
-static struct ctl_table_header *fsverity_sysctl_header;
-
-static const struct ctl_path fsverity_sysctl_path[] = {
-	{ .procname = "fs", },
-	{ .procname = "verity", },
-	{ }
-};
-
-static struct ctl_table fsverity_sysctl_table[] = {
-	{
-		.procname       = "require_signatures",
-		.data           = &fsverity_require_signatures,
-		.maxlen         = sizeof(int),
-		.mode           = 0644,
-		.proc_handler   = proc_dointvec_minmax,
-		.extra1         = SYSCTL_ZERO,
-		.extra2         = SYSCTL_ONE,
-	},
-	{ }
-};
-
-static int __init fsverity_sysctl_init(void)
-{
-	fsverity_sysctl_header = register_sysctl_paths(fsverity_sysctl_path,
-						       fsverity_sysctl_table);
-	if (!fsverity_sysctl_header) {
-		pr_err("sysctl registration failed!\n");
-		return -ENOMEM;
-	}
-	return 0;
-}
-#else /* !CONFIG_SYSCTL */
-static inline int __init fsverity_sysctl_init(void)
-{
-	return 0;
-}
-#endif /* !CONFIG_SYSCTL */
-
 int __init fsverity_init_signature(void)
 {
 	struct key *ring;
-	int err;
 
 	ring = keyring_alloc(".fs-verity", KUIDT_INIT(0), KGIDT_INIT(0),
 			     current_cred(), KEY_POS_SEARCH |
@@ -139,14 +95,6 @@ int __init fsverity_init_signature(void)
 	if (IS_ERR(ring))
 		return PTR_ERR(ring);
 
-	err = fsverity_sysctl_init();
-	if (err)
-		goto err_put_ring;
-
 	fsverity_keyring = ring;
 	return 0;
-
-err_put_ring:
-	key_put(ring);
-	return err;
 }
diff --git a/fs/verity/sysctl.c b/fs/verity/sysctl.c
new file mode 100644
index 000000000000..3ba7b02282db
--- /dev/null
+++ b/fs/verity/sysctl.c
@@ -0,0 +1,51 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include "fsverity_private.h"
+
+#include <linux/sysctl.h>
+
+/*
+ * /proc/sys/fs/verity/require_signatures
+ * If 1, all verity files must have a valid builtin signature.
+ */
+int fsverity_require_signatures;
+
+#ifdef CONFIG_SYSCTL
+static struct ctl_table_header *fsverity_sysctl_header;
+
+static const struct ctl_path fsverity_sysctl_path[] = {
+	{ .procname = "fs", },
+	{ .procname = "verity", },
+	{ }
+};
+
+static struct ctl_table fsverity_sysctl_table[] = {
+	{
+		.procname       = "require_signatures",
+		.data           = &fsverity_require_signatures,
+		.maxlen         = sizeof(int),
+		.mode           = 0644,
+		.proc_handler   = proc_dointvec_minmax,
+		.extra1         = SYSCTL_ZERO,
+		.extra2         = SYSCTL_ONE,
+	},
+	{ }
+};
+
+int __init fsverity_sysctl_init(void)
+{
+	fsverity_sysctl_header = register_sysctl_paths(fsverity_sysctl_path,
+						       fsverity_sysctl_table);
+	if (!fsverity_sysctl_header) {
+		pr_err("sysctl registration failed!\n");
+		return -ENOMEM;
+	}
+	return 0;
+}
+
+void __init fsverity_exit_sysctl(void)
+{
+	unregister_sysctl_table(fsverity_sysctl_header);
+	fsverity_sysctl_header = NULL;
+}
+#endif /* !CONFIG_SYSCTL */
-- 
2.30.2


^ permalink raw reply related	[flat|nested] 5+ messages in thread

* [PATCH 2/2] fsverity: add mode sysctl
  2022-04-28 22:19 [PATCH 0/2] fsverity: killswitch sysctl Boris Burkov
  2022-04-28 22:19 ` [PATCH 1/2] fsverity: factor out sysctl from signature.c Boris Burkov
@ 2022-04-28 22:19 ` Boris Burkov
  2022-05-17 23:03   ` Eric Biggers
  1 sibling, 1 reply; 5+ messages in thread
From: Boris Burkov @ 2022-04-28 22:19 UTC (permalink / raw)
  To: linux-fscrypt, kernel-team

Add a "mode" sysctl to act as a killswitch for fs-verity. The motivation
is that in case there is an unforeseen performance regression or bug in
verity as one attempts to use it in production, being able to disable it
live gives a lot of peace of mind. One could also run in audit mode for
a while before switching to the enforcing mode.

The modes are:
disable: verity has no effect, even if configured or used
audit: run the verity logic, permit ioctls, but only log on failures
enforce: fully enabled; the default and the behavior before this patch

This also precipitated re-organizing sysctls for verity as previously
the only sysctl was for signatures so sysctls and signatures were
coupled.

One slightly subtle issue is what errors should "audit" swallow.
Certainly verification or signature errors, but there is a slippery
slope leading to inconsistent states with half set up verity if you try
to ignore errors in enabling verity on a file. The intent of audit mode
is to still fail normally in verity specific ioctls, but to leave the
file be from the perspective of normal filesystem APIs. However, we must
still disallow writes in audit mode, or else it would immediately lead
to invalid verity state.

Signed-off-by: Boris Burkov <boris@bur.io>
---
 fs/verity/enable.c           |  3 ++
 fs/verity/fsverity_private.h | 10 ++++++
 fs/verity/measure.c          |  3 ++
 fs/verity/open.c             | 14 +++++++--
 fs/verity/read_metadata.c    |  3 ++
 fs/verity/signature.c        | 14 +++++++--
 fs/verity/sysctl.c           | 59 ++++++++++++++++++++++++++++++++++++
 fs/verity/verify.c           | 34 +++++++++++++++++++--
 include/linux/fsverity.h     |  4 +--
 9 files changed, 136 insertions(+), 8 deletions(-)

diff --git a/fs/verity/enable.c b/fs/verity/enable.c
index 60a4372aa4d7..dae21c09e518 100644
--- a/fs/verity/enable.c
+++ b/fs/verity/enable.c
@@ -343,6 +343,9 @@ int fsverity_ioctl_enable(struct file *filp, const void __user *uarg)
 	struct fsverity_enable_arg arg;
 	int err;
 
+	if (fsverity_disabled())
+		return -EPERM;
+
 	if (copy_from_user(&arg, uarg, sizeof(arg)))
 		return -EFAULT;
 
diff --git a/fs/verity/fsverity_private.h b/fs/verity/fsverity_private.h
index c416c1cd9371..05faaa827a9d 100644
--- a/fs/verity/fsverity_private.h
+++ b/fs/verity/fsverity_private.h
@@ -140,6 +140,8 @@ void __init fsverity_exit_info_cache(void);
 #ifdef CONFIG_SYSCTL
 int __init fsverity_sysctl_init(void);
 void __init fsverity_exit_sysctl(void);
+bool fsverity_disabled(void);
+bool fsverity_enforced(void);
 #else /* !CONFIG_SYSCTL */
 static inline int __init fsverity_sysctl_init(void)
 {
@@ -148,6 +150,14 @@ static inline int __init fsverity_sysctl_init(void)
 static inline void __init fsverity_exit_sysctl(void)
 {
 }
+static inline bool fsverity_disabled(void)
+{
+	return true;
+}
+static inline bool fsverity_enforced(void)
+{
+	return false;
+}
 #endif /* !CONFIG_SYSCTL */
 
 /* signature.c */
diff --git a/fs/verity/measure.c b/fs/verity/measure.c
index f0d7b30c62db..f17efaa919e3 100644
--- a/fs/verity/measure.c
+++ b/fs/verity/measure.c
@@ -28,6 +28,9 @@ int fsverity_ioctl_measure(struct file *filp, void __user *_uarg)
 	const struct fsverity_hash_alg *hash_alg;
 	struct fsverity_digest arg;
 
+	if (fsverity_disabled())
+		return -EPERM;
+
 	vi = fsverity_get_info(inode);
 	if (!vi)
 		return -ENODATA; /* not a verity file */
diff --git a/fs/verity/open.c b/fs/verity/open.c
index 92df87f5fa38..840ad62bf6ac 100644
--- a/fs/verity/open.c
+++ b/fs/verity/open.c
@@ -177,7 +177,7 @@ struct fsverity_info *fsverity_create_info(const struct inode *inode,
 		fsverity_err(inode, "Error %d computing file digest", err);
 		goto out;
 	}
-	pr_debug("Computed file digest: %s:%*phN\n",
+	pr_info("Computed file digest: %s:%*phN\n",
 		 vi->tree_params.hash_alg->name,
 		 vi->tree_params.digest_size, vi->file_digest);
 
@@ -344,6 +344,11 @@ static int ensure_verity_info(struct inode *inode)
  */
 int fsverity_file_open(struct inode *inode, struct file *filp)
 {
+	int ret;
+
+	if (fsverity_disabled())
+		return 0;
+
 	if (!IS_VERITY(inode))
 		return 0;
 
@@ -353,7 +358,12 @@ int fsverity_file_open(struct inode *inode, struct file *filp)
 		return -EPERM;
 	}
 
-	return ensure_verity_info(inode);
+	ret = ensure_verity_info(inode);
+	if (!fsverity_enforced()) {
+		fsverity_warn(inode, "AUDIT ONLY: ignore missing verity info");
+		return 0;
+	}
+	return ret;
 }
 EXPORT_SYMBOL_GPL(fsverity_file_open);
 
diff --git a/fs/verity/read_metadata.c b/fs/verity/read_metadata.c
index 7e2d0c7bdf0d..5773fbaabf1e 100644
--- a/fs/verity/read_metadata.c
+++ b/fs/verity/read_metadata.c
@@ -157,6 +157,9 @@ int fsverity_ioctl_read_metadata(struct file *filp, const void __user *uarg)
 	int length;
 	void __user *buf;
 
+	if (fsverity_disabled())
+		return -EPERM;
+
 	vi = fsverity_get_info(inode);
 	if (!vi)
 		return -ENODATA; /* not a verity file */
diff --git a/fs/verity/signature.c b/fs/verity/signature.c
index 67a471e4b570..b10515817d8a 100644
--- a/fs/verity/signature.c
+++ b/fs/verity/signature.c
@@ -42,12 +42,18 @@ int fsverity_verify_signature(const struct fsverity_info *vi,
 	int err;
 
 	if (sig_size == 0) {
+		err = 0;
 		if (fsverity_require_signatures) {
 			fsverity_err(inode,
 				     "require_signatures=1, rejecting unsigned file!");
-			return -EPERM;
+			if (fsverity_enforced()) {
+				err = -EPERM;
+			} else {
+				fsverity_warn(vi->inode, "AUDIT ONLY. ignore unsigned");
+				err = 0;
+			}
 		}
-		return 0;
+		return err;
 	}
 
 	d = kzalloc(sizeof(*d) + hash_alg->digest_size, GFP_KERNEL);
@@ -75,6 +81,10 @@ int fsverity_verify_signature(const struct fsverity_info *vi,
 		else
 			fsverity_err(inode, "Error %d verifying file signature",
 				     err);
+		if (!fsverity_enforced()) {
+			fsverity_warn(vi->inode, "AUDIT ONLY. ignore signature error");
+			err = 0;
+		}
 		return err;
 	}
 
diff --git a/fs/verity/sysctl.c b/fs/verity/sysctl.c
index 3ba7b02282db..94ffb78f745c 100644
--- a/fs/verity/sysctl.c
+++ b/fs/verity/sysctl.c
@@ -9,6 +9,21 @@
  * If 1, all verity files must have a valid builtin signature.
  */
 int fsverity_require_signatures;
+/*
+ * /proc/sys/fs/verity/mode
+ * If "disable": disable verity, don't verify, fail all ioctls.
+ * If "audit": allow ioctls, and run verification logic, but only log on verification failure.
+ * If "enforce": fully enforce, verification failure returns errors.
+ * default: "enforce"
+ */
+#define FSVERITY_MODE_LEN 10
+static const char * const fsverity_modes[] = {
+	"disable",
+	"audit",
+	"enforce",
+	NULL
+};
+static char fsverity_mode[FSVERITY_MODE_LEN] = "enforce";
 
 #ifdef CONFIG_SYSCTL
 static struct ctl_table_header *fsverity_sysctl_header;
@@ -19,6 +34,34 @@ static const struct ctl_path fsverity_sysctl_path[] = {
 	{ }
 };
 
+static int proc_do_fsverity_mode(struct ctl_table *table, int write,
+				 void *buffer, size_t *lenp, loff_t *ppos)
+{
+	char tmp_mode[FSVERITY_MODE_LEN];
+	const char *const *mode = fsverity_modes;
+	struct ctl_table tmp = {
+		.data = tmp_mode,
+		.maxlen = FSVERITY_MODE_LEN,
+		.mode = table->mode,
+	};
+	int ret;
+
+	strncpy(tmp_mode, fsverity_mode, FSVERITY_MODE_LEN);
+	ret = proc_dostring(&tmp, write, buffer, lenp, ppos);
+	if (write) {
+		while (*mode) {
+			if (!strcmp(*mode, tmp_mode))
+				break;
+			++mode;
+		}
+		if (!*mode)
+			ret = -EINVAL;
+		else
+			strncpy(fsverity_mode, *mode, FSVERITY_MODE_LEN);
+	}
+	return ret;
+}
+
 static struct ctl_table fsverity_sysctl_table[] = {
 	{
 		.procname       = "require_signatures",
@@ -29,6 +72,13 @@ static struct ctl_table fsverity_sysctl_table[] = {
 		.extra1         = SYSCTL_ZERO,
 		.extra2         = SYSCTL_ONE,
 	},
+	{
+		.procname       = "mode",
+		.data           = fsverity_mode,
+		.maxlen         = FSVERITY_MODE_LEN,
+		.mode           = 0644,
+		.proc_handler   = proc_do_fsverity_mode,
+	},
 	{ }
 };
 
@@ -48,4 +98,13 @@ void __init fsverity_exit_sysctl(void)
 	unregister_sysctl_table(fsverity_sysctl_header);
 	fsverity_sysctl_header = NULL;
 }
+
+bool fsverity_disabled(void)
+{
+	return !strcmp(fsverity_mode, "disable");
+}
+bool fsverity_enforced(void)
+{
+	return !strcmp(fsverity_mode, "enforce");
+}
 #endif /* !CONFIG_SYSCTL */
diff --git a/fs/verity/verify.c b/fs/verity/verify.c
index 14e2fb49cff5..aedd8f8d864f 100644
--- a/fs/verity/verify.c
+++ b/fs/verity/verify.c
@@ -7,6 +7,7 @@
 
 #include "fsverity_private.h"
 
+#include "linux/export.h"
 #include <crypto/hash.h>
 #include <linux/bio.h>
 #include <linux/ratelimit.h>
@@ -63,6 +64,10 @@ static inline int cmp_hashes(const struct fsverity_info *vi,
 		     index, level,
 		     vi->tree_params.hash_alg->name, hsize, want_hash,
 		     vi->tree_params.hash_alg->name, hsize, real_hash);
+	if (!fsverity_enforced()) {
+		fsverity_warn(vi->inode, "AUDIT ONLY. ignore corruption");
+		return 0;
+	}
 	return -EBADMSG;
 }
 
@@ -98,8 +103,10 @@ static bool verify_page(struct inode *inode, const struct fsverity_info *vi,
 	unsigned int hoffsets[FS_VERITY_MAX_LEVELS];
 	int err;
 
-	if (WARN_ON_ONCE(!PageLocked(data_page) || PageUptodate(data_page)))
-		return false;
+	if (WARN_ON_ONCE(!PageLocked(data_page) || PageUptodate(data_page))) {
+		err = -EINVAL;
+		goto out;
+	}
 
 	pr_debug_ratelimited("Verifying data page %lu...\n", index);
 
@@ -193,6 +200,8 @@ bool fsverity_verify_page(struct page *page)
 	struct ahash_request *req;
 	bool valid;
 
+	if (fsverity_disabled())
+		return true;
 	/* This allocation never fails, since it's mempool-backed. */
 	req = fsverity_alloc_hash_request(vi->tree_params.hash_alg, GFP_NOFS);
 
@@ -276,6 +285,27 @@ void fsverity_enqueue_verify_work(struct work_struct *work)
 }
 EXPORT_SYMBOL_GPL(fsverity_enqueue_verify_work);
 
+/**
+ * fsverity_active() - do reads from the inode need to go through fs-verity?
+ * @inode: inode to check
+ *
+ * This checks whether ->i_verity_info has been set.
+ *
+ * Filesystems call this from ->readahead() to check whether the pages need to
+ * be verified or not.  Don't use IS_VERITY() for this purpose; it's subject to
+ * a race condition where the file is being read concurrently with
+ * FS_IOC_ENABLE_VERITY completing.  (S_VERITY is set before ->i_verity_info.)
+ *
+ * Return: true if reads need to go through fs-verity, otherwise false
+ */
+bool fsverity_active(const struct inode *inode)
+{
+	if (fsverity_disabled())
+		return false;
+	return fsverity_get_info(inode) != NULL;
+}
+EXPORT_SYMBOL_GPL(fsverity_active);
+
 int __init fsverity_init_workqueue(void)
 {
 	/*
diff --git a/include/linux/fsverity.h b/include/linux/fsverity.h
index a7afc800bd8d..51ba4adf7b73 100644
--- a/include/linux/fsverity.h
+++ b/include/linux/fsverity.h
@@ -147,6 +147,7 @@ int fsverity_ioctl_read_metadata(struct file *filp, const void __user *uarg);
 bool fsverity_verify_page(struct page *page);
 void fsverity_verify_bio(struct bio *bio);
 void fsverity_enqueue_verify_work(struct work_struct *work);
+bool fsverity_active(const struct inode *inode);
 
 #else /* !CONFIG_FS_VERITY */
 
@@ -213,8 +214,6 @@ static inline void fsverity_enqueue_verify_work(struct work_struct *work)
 	WARN_ON(1);
 }
 
-#endif	/* !CONFIG_FS_VERITY */
-
 /**
  * fsverity_active() - do reads from the inode need to go through fs-verity?
  * @inode: inode to check
@@ -232,5 +231,6 @@ static inline bool fsverity_active(const struct inode *inode)
 {
 	return fsverity_get_info(inode) != NULL;
 }
+#endif	/* !CONFIG_FS_VERITY */
 
 #endif	/* _LINUX_FSVERITY_H */
-- 
2.30.2


^ permalink raw reply related	[flat|nested] 5+ messages in thread

* Re: [PATCH 1/2] fsverity: factor out sysctl from signature.c
  2022-04-28 22:19 ` [PATCH 1/2] fsverity: factor out sysctl from signature.c Boris Burkov
@ 2022-05-17 22:24   ` Eric Biggers
  0 siblings, 0 replies; 5+ messages in thread
From: Eric Biggers @ 2022-05-17 22:24 UTC (permalink / raw)
  To: Boris Burkov; +Cc: linux-fscrypt, kernel-team

On Thu, Apr 28, 2022 at 03:19:19PM -0700, Boris Burkov wrote:
> diff --git a/fs/verity/signature.c b/fs/verity/signature.c
> index 143a530a8008..67a471e4b570 100644
> --- a/fs/verity/signature.c
> +++ b/fs/verity/signature.c
> @@ -12,11 +12,7 @@
>  #include <linux/slab.h>
>  #include <linux/verification.h>
>  
> -/*
> - * /proc/sys/fs/verity/require_signatures
> - * If 1, all verity files must have a valid builtin signature.
> - */
> -static int fsverity_require_signatures;
> +extern int fsverity_require_signatures;

This forward declaration should go in fsverity_private.h so that it is also
visible at the definition site.  Otherwise it causes a compiler warning:

fs/verity/sysctl.c:11:5: warning: symbol 'fsverity_require_signatures' was not declared. Should it be static?

> diff --git a/fs/verity/sysctl.c b/fs/verity/sysctl.c
> new file mode 100644
> index 000000000000..3ba7b02282db
> --- /dev/null
> +++ b/fs/verity/sysctl.c
> @@ -0,0 +1,51 @@
> +// SPDX-License-Identifier: GPL-2.0
> +

Please keep the existing copyright statements when moving code.

- Eric

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [PATCH 2/2] fsverity: add mode sysctl
  2022-04-28 22:19 ` [PATCH 2/2] fsverity: add mode sysctl Boris Burkov
@ 2022-05-17 23:03   ` Eric Biggers
  0 siblings, 0 replies; 5+ messages in thread
From: Eric Biggers @ 2022-05-17 23:03 UTC (permalink / raw)
  To: Boris Burkov; +Cc: linux-fscrypt, kernel-team

Sorry for the slow review.  Some comments:

On Thu, Apr 28, 2022 at 03:19:20PM -0700, Boris Burkov wrote:
> Add a "mode" sysctl to act as a killswitch for fs-verity. The motivation
> is that in case there is an unforeseen performance regression or bug in
> verity as one attempts to use it in production, being able to disable it
> live gives a lot of peace of mind. One could also run in audit mode for
> a while before switching to the enforcing mode.
> 
> The modes are:
> disable: verity has no effect, even if configured or used
> audit: run the verity logic, permit ioctls, but only log on failures
> enforce: fully enabled; the default and the behavior before this patch
> 
> This also precipitated re-organizing sysctls for verity as previously
> the only sysctl was for signatures so sysctls and signatures were
> coupled.
> 
> One slightly subtle issue is what errors should "audit" swallow.
> Certainly verification or signature errors, but there is a slippery
> slope leading to inconsistent states with half set up verity if you try
> to ignore errors in enabling verity on a file. The intent of audit mode
> is to still fail normally in verity specific ioctls, but to leave the
> file be from the perspective of normal filesystem APIs. However, we must
> still disallow writes in audit mode, or else it would immediately lead
> to invalid verity state.
> 
> Signed-off-by: Boris Burkov <boris@bur.io>
> ---
>  fs/verity/enable.c           |  3 ++
>  fs/verity/fsverity_private.h | 10 ++++++
>  fs/verity/measure.c          |  3 ++
>  fs/verity/open.c             | 14 +++++++--
>  fs/verity/read_metadata.c    |  3 ++
>  fs/verity/signature.c        | 14 +++++++--
>  fs/verity/sysctl.c           | 59 ++++++++++++++++++++++++++++++++++++
>  fs/verity/verify.c           | 34 +++++++++++++++++++--
>  include/linux/fsverity.h     |  4 +--
>  9 files changed, 136 insertions(+), 8 deletions(-)

The new sysctl needs to be documented.

Also, the documentation for any ioctls that now can fail in a new way needs to
be updated.

Also, can you please Cc this to the filesystem mailing lists (ext4, f2fs, and
btrfs) too?  As this is a new UAPI, we need to make sure that enough people have
an opportunity to review it before it's set in stone.

> diff --git a/fs/verity/measure.c b/fs/verity/measure.c
> index f0d7b30c62db2..f17efaa919e37 100644
> --- a/fs/verity/measure.c
> +++ b/fs/verity/measure.c
> @@ -28,6 +28,9 @@ int fsverity_ioctl_measure(struct file *filp, void __user *_uarg)
>  	const struct fsverity_hash_alg *hash_alg;
>  	struct fsverity_digest arg;
>  
> +	if (fsverity_disabled())
> +		return -EPERM;
> +
>  	vi = fsverity_get_info(inode);
>  	if (!vi)
>  		return -ENODATA; /* not a verity file */

Perhaps the error code should be ENODATA, the same as the file not having
fs-verity enabled?  Would userspace ever have any reason to handle the cases
differently?

Also, shouldn't audit mode be handled the same way as disabled mode here?  The
whole point of using this ioctl is that it gives you the correct hash.  In audit
mode, it's not guaranteed to do that.

> diff --git a/fs/verity/open.c b/fs/verity/open.c
> index 92df87f5fa38..840ad62bf6ac 100644
> --- a/fs/verity/open.c
> +++ b/fs/verity/open.c
> @@ -177,7 +177,7 @@ struct fsverity_info *fsverity_create_info(const struct inode *inode,
>  		fsverity_err(inode, "Error %d computing file digest", err);
>  		goto out;
>  	}
> -	pr_debug("Computed file digest: %s:%*phN\n",
> +	pr_info("Computed file digest: %s:%*phN\n",
>  		 vi->tree_params.hash_alg->name,
>  		 vi->tree_params.digest_size, vi->file_digest);

This would be far too noisy.

> @@ -353,7 +358,12 @@ int fsverity_file_open(struct inode *inode, struct file *filp)
>  		return -EPERM;
>  	}
>  
> -	return ensure_verity_info(inode);
> +	ret = ensure_verity_info(inode);
> +	if (!fsverity_enforced()) {
> +		fsverity_warn(inode, "AUDIT ONLY: ignore missing verity info");
> +		return 0;
> +	}
> +	return ret;

Is the above intended to check for failure of ensure_verity_info()?  That's not
what it does.  Also, the warning message is not going to be clear to anyone
reading it.  What does it mean, and if someone gets that warning message what
are they supposed to do about it?

> @@ -157,6 +157,9 @@ int fsverity_ioctl_read_metadata(struct file *filp, const void __user *uarg)
>  	int length;
>  	void __user *buf;
>  
> +	if (fsverity_disabled())
> +		return -EPERM;
> +
>  	vi = fsverity_get_info(inode);
>  	if (!vi)
>  		return -ENODATA; /* not a verity file */

As for FS_IOC_MEASURE_VERITY, perhaps the error code here should be ENODATA?

> diff --git a/fs/verity/signature.c b/fs/verity/signature.c
> index 67a471e4b570..b10515817d8a 100644
> --- a/fs/verity/signature.c
> +++ b/fs/verity/signature.c
> @@ -42,12 +42,18 @@ int fsverity_verify_signature(const struct fsverity_info *vi,
>  	int err;
>  
>  	if (sig_size == 0) {
> +		err = 0;
>  		if (fsverity_require_signatures) {
>  			fsverity_err(inode,
>  				     "require_signatures=1, rejecting unsigned file!");
> -			return -EPERM;
> +			if (fsverity_enforced()) {
> +				err = -EPERM;
> +			} else {
> +				fsverity_warn(vi->inode, "AUDIT ONLY. ignore unsigned");
> +				err = 0;
> +			}
>  		}
> -		return 0;
> +		return err;
>  	}
>  
>  	d = kzalloc(sizeof(*d) + hash_alg->digest_size, GFP_KERNEL);
> @@ -75,6 +81,10 @@ int fsverity_verify_signature(const struct fsverity_info *vi,
>  		else
>  			fsverity_err(inode, "Error %d verifying file signature",
>  				     err);
> +		if (!fsverity_enforced()) {
> +			fsverity_warn(vi->inode, "AUDIT ONLY. ignore signature error");
> +			err = 0;
> +		}
>  		return err;
>  	}

In "audit" mode these errors already get swallowed at a higher level in
fsverity_file_open().  So it's unclear what the above changes are intended for.

> @@ -98,8 +103,10 @@ static bool verify_page(struct inode *inode, const struct fsverity_info *vi,
>  	unsigned int hoffsets[FS_VERITY_MAX_LEVELS];
>  	int err;
>  
> -	if (WARN_ON_ONCE(!PageLocked(data_page) || PageUptodate(data_page)))
> -		return false;
> +	if (WARN_ON_ONCE(!PageLocked(data_page) || PageUptodate(data_page))) {
> +		err = -EINVAL;
> +		goto out;
> +	}

This change introduces an uninitialized variable bug, and otherwise has no
effect.

>  
>  	pr_debug_ratelimited("Verifying data page %lu...\n", index);
>  
> @@ -193,6 +200,8 @@ bool fsverity_verify_page(struct page *page)
>  	struct ahash_request *req;
>  	bool valid;
>  
> +	if (fsverity_disabled())
> +		return true;
>  	/* This allocation never fails, since it's mempool-backed. */
>  	req = fsverity_alloc_hash_request(vi->tree_params.hash_alg, GFP_NOFS);

Isn't this redundant with the check of fsverity_disabled() in fsverity_active()?

And if not, why doesn't fsverity_verify_bio() get this check too?

> +/**
> + * fsverity_active() - do reads from the inode need to go through fs-verity?
> + * @inode: inode to check
> + *
> + * This checks whether ->i_verity_info has been set.
> + *
> + * Filesystems call this from ->readahead() to check whether the pages need to
> + * be verified or not.  Don't use IS_VERITY() for this purpose; it's subject to
> + * a race condition where the file is being read concurrently with
> + * FS_IOC_ENABLE_VERITY completing.  (S_VERITY is set before ->i_verity_info.)
> + *
> + * Return: true if reads need to go through fs-verity, otherwise false
> + */

Please don't copy-and-paste long comments like this.  When a function has two
versions, a stub (!CONFIG_FS_VERITY) and a non-stub (CONFIG_FS_VERITY), to avoid
redundancy the comment should go above the non-stub version only.

> +bool fsverity_active(const struct inode *inode)
> +{
> +	if (fsverity_disabled())
> +		return false;
> +	return fsverity_get_info(inode) != NULL;
> +}
> +EXPORT_SYMBOL_GPL(fsverity_active);

Please add a comment that describes why the check of fsverity_get_info() alone
is not sufficient here.

- Eric

^ permalink raw reply	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2022-05-17 23:03 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-04-28 22:19 [PATCH 0/2] fsverity: killswitch sysctl Boris Burkov
2022-04-28 22:19 ` [PATCH 1/2] fsverity: factor out sysctl from signature.c Boris Burkov
2022-05-17 22:24   ` Eric Biggers
2022-04-28 22:19 ` [PATCH 2/2] fsverity: add mode sysctl Boris Burkov
2022-05-17 23:03   ` Eric Biggers

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.