diff mbox series

[fsverity-utils,RFC] Add libfsverity_enable() API

Message ID 20201113143527.1097499-1-luca.boccassi@gmail.com (mailing list archive)
State Superseded
Headers show
Series [fsverity-utils,RFC] Add libfsverity_enable() API | expand

Commit Message

Luca Boccassi Nov. 13, 2020, 2:35 p.m. UTC
From: Luca Boccassi <luca.boccassi@microsoft.com>

Factor out the 'fsverity enable' implementation in the library, to
give users a shortcut for reading signatures and enabling a file
with the default parameters.

Signed-off-by: Luca Boccassi <luca.boccassi@microsoft.com>
---
Marked as RFC to get guidance on how to deal with helper functions
duplication, that right now are part of the "programs" utility objects
and not usable from the "library" objects.
There's dozens of different ways to handle this, all equally valid, so
it's down to the preference of the maintainer (eg: new common helpers,
include helpers at build time, further splits of sources, etc).
Please provide a preference and I'll follow up.

 common/common_defs.h  |   9 ++
 include/libfsverity.h |  21 +++++
 lib/enable.c          | 191 ++++++++++++++++++++++++++++++++++++++++++
 programs/cmd_enable.c |  66 ++-------------
 programs/fsverity.h   |   9 --
 5 files changed, 227 insertions(+), 69 deletions(-)
 create mode 100644 lib/enable.c

Comments

Marcus Hüwe Nov. 13, 2020, 10:54 p.m. UTC | #1
On 2020-11-13 14:35:27 +0000, luca.boccassi@gmail.com wrote:
> diff --git a/lib/enable.c b/lib/enable.c
> new file mode 100644
> index 0000000..ad86cc5
> --- /dev/null
> +++ b/lib/enable.c

...

> +static bool open_file(struct filedes *file, const char *filename, int flags, int mode)
> +{
> +	file->fd = open(filename, flags, mode);
> +	if (file->fd < 0) {
> +		libfsverity_error_msg("can't open '%s' for %s", filename,
> +				(flags & O_ACCMODE) == O_RDONLY ? "reading" :
> +				(flags & O_ACCMODE) == O_WRONLY ? "writing" :
> +				"reading and writing");
> +		return false;
> +	}
> +	file->name = strdup(filename);

Hmm we should probably check for NULL.

> +	return true;
> +}

(Otherwise, I cannot really comment on the patch...)


Marcus
Eric Biggers Nov. 14, 2020, 12:21 a.m. UTC | #2
On Fri, Nov 13, 2020 at 02:35:27PM +0000, luca.boccassi@gmail.com wrote:
> From: Luca Boccassi <luca.boccassi@microsoft.com>
> 
> Factor out the 'fsverity enable' implementation in the library, to
> give users a shortcut for reading signatures and enabling a file
> with the default parameters.
> 
> Signed-off-by: Luca Boccassi <luca.boccassi@microsoft.com>
> ---
> Marked as RFC to get guidance on how to deal with helper functions
> duplication, that right now are part of the "programs" utility objects
> and not usable from the "library" objects.
> There's dozens of different ways to handle this, all equally valid, so
> it's down to the preference of the maintainer (eg: new common helpers,
> include helpers at build time, further splits of sources, etc).
> Please provide a preference and I'll follow up.
> 
>  common/common_defs.h  |   9 ++
>  include/libfsverity.h |  21 +++++
>  lib/enable.c          | 191 ++++++++++++++++++++++++++++++++++++++++++
>  programs/cmd_enable.c |  66 ++-------------
>  programs/fsverity.h   |   9 --
>  5 files changed, 227 insertions(+), 69 deletions(-)
>  create mode 100644 lib/enable.c
> 
> diff --git a/common/common_defs.h b/common/common_defs.h
> index 279385a..871db2c 100644
> --- a/common/common_defs.h
> +++ b/common/common_defs.h
> @@ -90,4 +90,13 @@ static inline int ilog2(unsigned long n)
>  #  define le64_to_cpu(v)	(__builtin_bswap64((__force u64)(v)))
>  #endif
>  
> +/* The hash algorithm that 'fsverity' assumes when none is specified */
> +#define FS_VERITY_HASH_ALG_DEFAULT	FS_VERITY_HASH_ALG_SHA256
> +
> +/*
> + * Largest digest size among all hash algorithms supported by fs-verity.
> + * This can be increased if needed.
> + */
> +#define FS_VERITY_MAX_DIGEST_SIZE	64
> +
>  #endif /* COMMON_COMMON_DEFS_H */
> diff --git a/include/libfsverity.h b/include/libfsverity.h
> index 8f78a13..8d1f93b 100644
> --- a/include/libfsverity.h
> +++ b/include/libfsverity.h
> @@ -112,6 +112,27 @@ libfsverity_sign_digest(const struct libfsverity_digest *digest,
>  			const struct libfsverity_signature_params *sig_params,
>  			uint8_t **sig_ret, size_t *sig_size_ret);
>  
> +/**
> + * libfsverity_enable() - Enable fs-verity on a file
> + *          An fsverity_digest (also called a "file measurement") is the root of
> + *          a file's Merkle tree.  Not to be confused with a traditional file
> + *          digest computed over the entire file.
> + * @file: path to the file to enable
> + * @signature: (optional) path to signature for @file
> + * @params: struct libfsverity_merkle_tree_params specifying the fs-verity
> + *	    version, the hash algorithm, the block size, and
> + *	    optionally a salt.  Reserved fields must be zero.
> + *      All fields bar the version are optional, and defaults will be used
> + *      if set to zero.
> + *
> + * Returns:
> + * * 0 for success, -EINVAL for invalid input arguments, or a generic error
> + *   if the FS_IOC_ENABLE_VERITY ioctl fails.
> + */
> +int
> +libfsverity_enable(const char *file, const char *signature,
> +			struct libfsverity_merkle_tree_params *params);
> +
>  /**
>   * libfsverity_find_hash_alg_by_name() - Find hash algorithm by name
>   * @name: Pointer to name of hash algorithm

Hi Luca, can you consider
https://lkml.kernel.org/linux-fscrypt/20201114001529.185751-1-ebiggers@kernel.org/T/#u
instead?

It's somewhat useful to have a wrapper around FS_IOC_ENABLE_VERITY that takes
'struct libfsverity_merkle_tree_params', so that library users can deal with one
common struct.  (And I took advantage of that to simplify the code that parses
the parameters.)

But I think we should keep it as a thin wrapper, and not have file path
parameters or set defaults in the libfsverity_merkle_tree_params.  The library
user is better suited to deal with those, like they already do for
libfsverity_compute_digest().

- Eric
Luca Boccassi Nov. 16, 2020, 11:53 a.m. UTC | #3
On Fri, 2020-11-13 at 16:21 -0800, Eric Biggers wrote:
> On Fri, Nov 13, 2020 at 02:35:27PM +0000, luca.boccassi@gmail.com wrote:
> > From: Luca Boccassi <luca.boccassi@microsoft.com>
> > 
> > Factor out the 'fsverity enable' implementation in the library, to
> > give users a shortcut for reading signatures and enabling a file
> > with the default parameters.
> > 
> > Signed-off-by: Luca Boccassi <luca.boccassi@microsoft.com>
> > ---
> > Marked as RFC to get guidance on how to deal with helper functions
> > duplication, that right now are part of the "programs" utility objects
> > and not usable from the "library" objects.
> > There's dozens of different ways to handle this, all equally valid, so
> > it's down to the preference of the maintainer (eg: new common helpers,
> > include helpers at build time, further splits of sources, etc).
> > Please provide a preference and I'll follow up.
> > 
> >  common/common_defs.h  |   9 ++
> >  include/libfsverity.h |  21 +++++
> >  lib/enable.c          | 191 ++++++++++++++++++++++++++++++++++++++++++
> >  programs/cmd_enable.c |  66 ++-------------
> >  programs/fsverity.h   |   9 --
> >  5 files changed, 227 insertions(+), 69 deletions(-)
> >  create mode 100644 lib/enable.c
> > 
> > diff --git a/common/common_defs.h b/common/common_defs.h
> > index 279385a..871db2c 100644
> > --- a/common/common_defs.h
> > +++ b/common/common_defs.h
> > @@ -90,4 +90,13 @@ static inline int ilog2(unsigned long n)
> >  #  define le64_to_cpu(v)	(__builtin_bswap64((__force u64)(v)))
> >  #endif
> >  
> > +/* The hash algorithm that 'fsverity' assumes when none is specified */
> > +#define FS_VERITY_HASH_ALG_DEFAULT	FS_VERITY_HASH_ALG_SHA256
> > +
> > +/*
> > + * Largest digest size among all hash algorithms supported by fs-verity.
> > + * This can be increased if needed.
> > + */
> > +#define FS_VERITY_MAX_DIGEST_SIZE	64
> > +
> >  #endif /* COMMON_COMMON_DEFS_H */
> > diff --git a/include/libfsverity.h b/include/libfsverity.h
> > index 8f78a13..8d1f93b 100644
> > --- a/include/libfsverity.h
> > +++ b/include/libfsverity.h
> > @@ -112,6 +112,27 @@ libfsverity_sign_digest(const struct libfsverity_digest *digest,
> >  			const struct libfsverity_signature_params *sig_params,
> >  			uint8_t **sig_ret, size_t *sig_size_ret);
> >  
> > +/**
> > + * libfsverity_enable() - Enable fs-verity on a file
> > + *          An fsverity_digest (also called a "file measurement") is the root of
> > + *          a file's Merkle tree.  Not to be confused with a traditional file
> > + *          digest computed over the entire file.
> > + * @file: path to the file to enable
> > + * @signature: (optional) path to signature for @file
> > + * @params: struct libfsverity_merkle_tree_params specifying the fs-verity
> > + *	    version, the hash algorithm, the block size, and
> > + *	    optionally a salt.  Reserved fields must be zero.
> > + *      All fields bar the version are optional, and defaults will be used
> > + *      if set to zero.
> > + *
> > + * Returns:
> > + * * 0 for success, -EINVAL for invalid input arguments, or a generic error
> > + *   if the FS_IOC_ENABLE_VERITY ioctl fails.
> > + */
> > +int
> > +libfsverity_enable(const char *file, const char *signature,
> > +			struct libfsverity_merkle_tree_params *params);
> > +
> >  /**
> >   * libfsverity_find_hash_alg_by_name() - Find hash algorithm by name
> >   * @name: Pointer to name of hash algorithm
> 
> Hi Luca, can you consider
> https://lkml.kernel.org/linux-fscrypt/20201114001529.185751-1-ebiggers@kernel.org/T/#u
> instead?
> 
> It's somewhat useful to have a wrapper around FS_IOC_ENABLE_VERITY that takes
> 'struct libfsverity_merkle_tree_params', so that library users can deal with one
> common struct.  (And I took advantage of that to simplify the code that parses
> the parameters.)
> 
> But I think we should keep it as a thin wrapper, and not have file path
> parameters or set defaults in the libfsverity_merkle_tree_params.  The library
> user is better suited to deal with those, like they already do for
> libfsverity_compute_digest().
> 
> - Eric

Hi,

Sure, no problem, we can drop this, thanks for sending those series.
Left a comment in the other thread.
diff mbox series

Patch

diff --git a/common/common_defs.h b/common/common_defs.h
index 279385a..871db2c 100644
--- a/common/common_defs.h
+++ b/common/common_defs.h
@@ -90,4 +90,13 @@  static inline int ilog2(unsigned long n)
 #  define le64_to_cpu(v)	(__builtin_bswap64((__force u64)(v)))
 #endif
 
+/* The hash algorithm that 'fsverity' assumes when none is specified */
+#define FS_VERITY_HASH_ALG_DEFAULT	FS_VERITY_HASH_ALG_SHA256
+
+/*
+ * Largest digest size among all hash algorithms supported by fs-verity.
+ * This can be increased if needed.
+ */
+#define FS_VERITY_MAX_DIGEST_SIZE	64
+
 #endif /* COMMON_COMMON_DEFS_H */
diff --git a/include/libfsverity.h b/include/libfsverity.h
index 8f78a13..8d1f93b 100644
--- a/include/libfsverity.h
+++ b/include/libfsverity.h
@@ -112,6 +112,27 @@  libfsverity_sign_digest(const struct libfsverity_digest *digest,
 			const struct libfsverity_signature_params *sig_params,
 			uint8_t **sig_ret, size_t *sig_size_ret);
 
+/**
+ * libfsverity_enable() - Enable fs-verity on a file
+ *          An fsverity_digest (also called a "file measurement") is the root of
+ *          a file's Merkle tree.  Not to be confused with a traditional file
+ *          digest computed over the entire file.
+ * @file: path to the file to enable
+ * @signature: (optional) path to signature for @file
+ * @params: struct libfsverity_merkle_tree_params specifying the fs-verity
+ *	    version, the hash algorithm, the block size, and
+ *	    optionally a salt.  Reserved fields must be zero.
+ *      All fields bar the version are optional, and defaults will be used
+ *      if set to zero.
+ *
+ * Returns:
+ * * 0 for success, -EINVAL for invalid input arguments, or a generic error
+ *   if the FS_IOC_ENABLE_VERITY ioctl fails.
+ */
+int
+libfsverity_enable(const char *file, const char *signature,
+			struct libfsverity_merkle_tree_params *params);
+
 /**
  * libfsverity_find_hash_alg_by_name() - Find hash algorithm by name
  * @name: Pointer to name of hash algorithm
diff --git a/lib/enable.c b/lib/enable.c
new file mode 100644
index 0000000..ad86cc5
--- /dev/null
+++ b/lib/enable.c
@@ -0,0 +1,191 @@ 
+// SPDX-License-Identifier: MIT
+/*
+ * Implementation of libfsverity_enable().
+ *
+ * Copyright 2020 Microsoft
+ *
+ * Use of this source code is governed by an MIT-style
+ * license that can be found in the LICENSE file or at
+ * https://opensource.org/licenses/MIT.
+ */
+
+#include "lib_private.h"
+
+#include <fcntl.h>
+#include <getopt.h>
+#include <limits.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <string.h>
+
+
+struct filedes {
+	int fd;
+	char *name;		/* filename, for logging or error messages */
+};
+
+static u32 get_default_block_size(void)
+{
+	long n = sysconf(_SC_PAGESIZE);
+
+	if (n <= 0 || n >= INT_MAX || !is_power_of_2(n)) {
+		libfsverity_error_msg("Warning: invalid _SC_PAGESIZE (%ld).  Assuming 4K blocks.\n",
+			n);
+		return 4096;
+	}
+	return n;
+}
+
+static bool open_file(struct filedes *file, const char *filename, int flags, int mode)
+{
+	file->fd = open(filename, flags, mode);
+	if (file->fd < 0) {
+		libfsverity_error_msg("can't open '%s' for %s", filename,
+				(flags & O_ACCMODE) == O_RDONLY ? "reading" :
+				(flags & O_ACCMODE) == O_WRONLY ? "writing" :
+				"reading and writing");
+		return false;
+	}
+	file->name = strdup(filename);
+	return true;
+}
+
+static bool get_file_size(struct filedes *file, u64 *size_ret)
+{
+	struct stat stbuf;
+
+	if (fstat(file->fd, &stbuf) != 0) {
+		libfsverity_error_msg("can't stat file '%s'", file->name);
+		return false;
+	}
+	*size_ret = stbuf.st_size;
+	return true;
+}
+
+static bool full_read(struct filedes *file, void *buf, size_t count)
+{
+	while (count) {
+		int n = read(file->fd, buf, min(count, INT_MAX));
+
+		if (n < 0) {
+			libfsverity_error_msg("reading from '%s'", file->name);
+			return false;
+		}
+		if (n == 0) {
+			libfsverity_error_msg("unexpected end-of-file on '%s'", file->name);
+			return false;
+		}
+		buf += n;
+		count -= n;
+	}
+	return true;
+}
+
+static bool filedes_close(struct filedes *file)
+{
+	int res;
+
+	if (file->fd < 0)
+		return true;
+	res = close(file->fd);
+	if (res != 0)
+		libfsverity_error_msg("closing '%s'", file->name);
+	file->fd = -1;
+	free(file->name);
+	file->name = NULL;
+	return res == 0;
+}
+
+static bool read_signature(const char *filename, u8 **sig_ret,
+			   u32 *sig_size_ret)
+{
+	struct filedes file = { .fd = -1 };
+	u64 file_size;
+	u8 *sig = NULL;
+	bool ok = false;
+
+	if (!open_file(&file, filename, O_RDONLY, 0))
+		goto out;
+	if (!get_file_size(&file, &file_size))
+		goto out;
+	if (file_size <= 0) {
+		libfsverity_error_msg("signature file '%s' is empty", filename);
+		goto out;
+	}
+	if (file_size > 1000000) {
+		libfsverity_error_msg("signature file '%s' is too large", filename);
+		goto out;
+	}
+	sig = libfsverity_zalloc(file_size);
+	if (!full_read(&file, sig, file_size))
+		goto out;
+	*sig_ret = sig;
+	*sig_size_ret = file_size;
+	sig = NULL;
+	ok = true;
+out:
+	filedes_close(&file);
+	free(sig);
+	return ok;
+}
+
+LIBEXPORT int
+libfsverity_enable(const char *file, const char *signature,
+			struct libfsverity_merkle_tree_params *params)
+{
+	struct fsverity_enable_arg arg;
+	u8 *sig = NULL;
+	struct filedes f;
+	int status;
+
+	if (!file || !params)  {
+		libfsverity_error_msg("missing required parameters for enable");
+		return -EINVAL;
+	}
+
+	arg = (struct fsverity_enable_arg) {
+			.version = params->version,
+			.hash_algorithm = params->hash_algorithm,
+			.block_size = params->block_size,
+			.salt_size = params->salt_size,
+			.salt_ptr = (uintptr_t)params->salt,
+	};
+
+	if (signature && !arg.sig_ptr) {
+		if (!read_signature(signature, &sig, &arg.sig_size))
+			return -EINVAL;
+		arg.sig_ptr = (uintptr_t)sig;
+	}
+
+	if (arg.hash_algorithm == 0)
+		arg.hash_algorithm = FS_VERITY_HASH_ALG_DEFAULT;
+
+	if (arg.block_size == 0)
+		arg.block_size = get_default_block_size();
+
+	if (!open_file(&f, file, O_RDONLY, 0)) {
+		status = -EINVAL;
+		goto out;
+	}
+	if (ioctl(f.fd, FS_IOC_ENABLE_VERITY, &arg) != 0) {
+		libfsverity_error_msg("FS_IOC_ENABLE_VERITY failed on '%s'",
+				f.name);
+		filedes_close(&f);
+		goto out_err;
+	}
+	if (!filedes_close(&f))
+		goto out_err;
+
+	status = 0;
+out:
+	free(sig);
+	return status;
+
+out_err:
+	status = 1;
+	goto out;
+}
diff --git a/programs/cmd_enable.c b/programs/cmd_enable.c
index d90d208..085b6a3 100644
--- a/programs/cmd_enable.c
+++ b/programs/cmd_enable.c
@@ -11,43 +11,7 @@ 
 
 #include "fsverity.h"
 
-#include <fcntl.h>
 #include <getopt.h>
-#include <limits.h>
-#include <sys/ioctl.h>
-
-static bool read_signature(const char *filename, u8 **sig_ret,
-			   u32 *sig_size_ret)
-{
-	struct filedes file = { .fd = -1 };
-	u64 file_size;
-	u8 *sig = NULL;
-	bool ok = false;
-
-	if (!open_file(&file, filename, O_RDONLY, 0))
-		goto out;
-	if (!get_file_size(&file, &file_size))
-		goto out;
-	if (file_size <= 0) {
-		error_msg("signature file '%s' is empty", filename);
-		goto out;
-	}
-	if (file_size > 1000000) {
-		error_msg("signature file '%s' is too large", filename);
-		goto out;
-	}
-	sig = xmalloc(file_size);
-	if (!full_read(&file, sig, file_size))
-		goto out;
-	*sig_ret = sig;
-	*sig_size_ret = file_size;
-	sig = NULL;
-	ok = true;
-out:
-	filedes_close(&file);
-	free(sig);
-	return ok;
-}
 
 enum {
 	OPT_HASH_ALG,
@@ -68,10 +32,8 @@  static const struct option longopts[] = {
 int fsverity_cmd_enable(const struct fsverity_command *cmd,
 			int argc, char *argv[])
 {
-	struct fsverity_enable_arg arg = { .version = 1 };
-	u8 *salt = NULL;
-	u8 *sig = NULL;
-	struct filedes file;
+	struct libfsverity_merkle_tree_params arg = { .version = 1 };
+	char *sig = NULL;
 	int status;
 	int c;
 
@@ -86,18 +48,17 @@  int fsverity_cmd_enable(const struct fsverity_command *cmd,
 				goto out_usage;
 			break;
 		case OPT_SALT:
-			if (!parse_salt_option(optarg, &salt, &arg.salt_size))
+			if (!parse_salt_option(optarg, (uint8_t **)&arg.salt, &arg.salt_size))
 				goto out_usage;
-			arg.salt_ptr = (uintptr_t)salt;
 			break;
 		case OPT_SIGNATURE:
 			if (sig != NULL) {
 				error_msg("--signature can only be specified once");
 				goto out_usage;
 			}
-			if (!read_signature(optarg, &sig, &arg.sig_size))
+			sig = strdup(optarg);
+			if (!sig)
 				goto out_err;
-			arg.sig_ptr = (uintptr_t)sig;
 			break;
 		default:
 			goto out_usage;
@@ -110,26 +71,11 @@  int fsverity_cmd_enable(const struct fsverity_command *cmd,
 	if (argc != 1)
 		goto out_usage;
 
-	if (arg.hash_algorithm == 0)
-		arg.hash_algorithm = FS_VERITY_HASH_ALG_DEFAULT;
-
-	if (arg.block_size == 0)
-		arg.block_size = get_default_block_size();
-
-	if (!open_file(&file, argv[0], O_RDONLY, 0))
-		goto out_err;
-	if (ioctl(file.fd, FS_IOC_ENABLE_VERITY, &arg) != 0) {
-		error_msg_errno("FS_IOC_ENABLE_VERITY failed on '%s'",
-				file.name);
-		filedes_close(&file);
-		goto out_err;
-	}
-	if (!filedes_close(&file))
+	if (libfsverity_enable(argv[0], sig, &arg))
 		goto out_err;
 
 	status = 0;
 out:
-	free(salt);
 	free(sig);
 	return status;
 
diff --git a/programs/fsverity.h b/programs/fsverity.h
index 669fef2..5e33eee 100644
--- a/programs/fsverity.h
+++ b/programs/fsverity.h
@@ -14,15 +14,6 @@ 
 #include "utils.h"
 #include "../common/fsverity_uapi.h"
 
-/* The hash algorithm that 'fsverity' assumes when none is specified */
-#define FS_VERITY_HASH_ALG_DEFAULT	FS_VERITY_HASH_ALG_SHA256
-
-/*
- * Largest digest size among all hash algorithms supported by fs-verity.
- * This can be increased if needed.
- */
-#define FS_VERITY_MAX_DIGEST_SIZE	64
-
 struct fsverity_command;
 
 /* cmd_digest.c */