All of lore.kernel.org
 help / color / mirror / Atom feed
From: Eric Biggers <ebiggers@kernel.org>
To: linux-fscrypt@vger.kernel.org
Cc: Victor Hsieh <victorhsieh@google.com>
Subject: [fsverity-utils PATCH 4/4] programs/fsverity: add --out-merkle-tree and --out-descriptor options
Date: Thu,  3 Jun 2021 12:58:12 -0700	[thread overview]
Message-ID: <20210603195812.50838-5-ebiggers@kernel.org> (raw)
In-Reply-To: <20210603195812.50838-1-ebiggers@kernel.org>

From: Eric Biggers <ebiggers@google.com>

Make 'fsverity digest' and 'fsverity sign' support writing the Merkle
tree and fs-verity descriptor to files, using new options
'--out-merkle-tree=FILE' and '--out-descriptor=FILE'.

Normally these new options aren't useful, but they can be needed in
cases where the fs-verity metadata needs to be consumed by something
other than one of the native Linux kernel implementations of fs-verity.

This is different from 'fsverity dump_metadata' in that
'fsverity dump_metadata' only works on a file with fs-verity enabled,
whereas these new options are for the userspace file digest computation.

Signed-off-by: Eric Biggers <ebiggers@google.com>
---
 programs/cmd_digest.c |  7 +++-
 programs/cmd_sign.c   | 17 ++++++---
 programs/fsverity.c   | 88 ++++++++++++++++++++++++++++++++++++++++++-
 programs/fsverity.h   |  4 +-
 4 files changed, 107 insertions(+), 9 deletions(-)

diff --git a/programs/cmd_digest.c b/programs/cmd_digest.c
index 1a3c769..fd9f4de 100644
--- a/programs/cmd_digest.c
+++ b/programs/cmd_digest.c
@@ -18,6 +18,8 @@ static const struct option longopts[] = {
 	{"hash-alg",		required_argument, NULL, OPT_HASH_ALG},
 	{"block-size",		required_argument, NULL, OPT_BLOCK_SIZE},
 	{"salt",		required_argument, NULL, OPT_SALT},
+	{"out-merkle-tree",     required_argument, NULL, OPT_OUT_MERKLE_TREE},
+	{"out-descriptor",      required_argument, NULL, OPT_OUT_DESCRIPTOR},
 	{"compact",		no_argument,	   NULL, OPT_COMPACT},
 	{"for-builtin-sig",	no_argument,	   NULL, OPT_FOR_BUILTIN_SIG},
 	{NULL, 0, NULL, 0}
@@ -40,6 +42,8 @@ int fsverity_cmd_digest(const struct fsverity_command *cmd,
 		case OPT_HASH_ALG:
 		case OPT_BLOCK_SIZE:
 		case OPT_SALT:
+		case OPT_OUT_MERKLE_TREE:
+		case OPT_OUT_DESCRIPTOR:
 			if (!parse_tree_param(c, optarg, &tree_params))
 				goto out_usage;
 			break;
@@ -114,7 +118,8 @@ int fsverity_cmd_digest(const struct fsverity_command *cmd,
 	}
 	status = 0;
 out:
-	destroy_tree_params(&tree_params);
+	if (!destroy_tree_params(&tree_params) && status == 0)
+		status = 1;
 	return status;
 
 out_err:
diff --git a/programs/cmd_sign.c b/programs/cmd_sign.c
index 47ba6a2..81a4ddc 100644
--- a/programs/cmd_sign.c
+++ b/programs/cmd_sign.c
@@ -27,11 +27,13 @@ static bool write_signature(const char *filename, const u8 *sig, u32 sig_size)
 }
 
 static const struct option longopts[] = {
-	{"hash-alg",	required_argument, NULL, OPT_HASH_ALG},
-	{"block-size",	required_argument, NULL, OPT_BLOCK_SIZE},
-	{"salt",	required_argument, NULL, OPT_SALT},
-	{"key",		required_argument, NULL, OPT_KEY},
-	{"cert",	required_argument, NULL, OPT_CERT},
+	{"hash-alg",	    required_argument, NULL, OPT_HASH_ALG},
+	{"block-size",	    required_argument, NULL, OPT_BLOCK_SIZE},
+	{"salt",	    required_argument, NULL, OPT_SALT},
+	{"out-merkle-tree", required_argument, NULL, OPT_OUT_MERKLE_TREE},
+	{"out-descriptor",  required_argument, NULL, OPT_OUT_DESCRIPTOR},
+	{"key",		    required_argument, NULL, OPT_KEY},
+	{"cert",	    required_argument, NULL, OPT_CERT},
 	{NULL, 0, NULL, 0}
 };
 
@@ -54,6 +56,8 @@ int fsverity_cmd_sign(const struct fsverity_command *cmd,
 		case OPT_HASH_ALG:
 		case OPT_BLOCK_SIZE:
 		case OPT_SALT:
+		case OPT_OUT_MERKLE_TREE:
+		case OPT_OUT_DESCRIPTOR:
 			if (!parse_tree_param(c, optarg, &tree_params))
 				goto out_usage;
 			break;
@@ -117,7 +121,8 @@ int fsverity_cmd_sign(const struct fsverity_command *cmd,
 	status = 0;
 out:
 	filedes_close(&file);
-	destroy_tree_params(&tree_params);
+	if (!destroy_tree_params(&tree_params) && status == 0)
+		status = 1;
 	free(digest);
 	free(sig);
 	return status;
diff --git a/programs/fsverity.c b/programs/fsverity.c
index 1168430..f6aff3a 100644
--- a/programs/fsverity.c
+++ b/programs/fsverity.c
@@ -11,6 +11,7 @@
 
 #include "fsverity.h"
 
+#include <fcntl.h>
 #include <limits.h>
 
 static const struct fsverity_command {
@@ -27,6 +28,7 @@ static const struct fsverity_command {
 		.usage_str =
 "    fsverity digest FILE...\n"
 "               [--hash-alg=HASH_ALG] [--block-size=BLOCK_SIZE] [--salt=SALT]\n"
+"               [--out-merkle-tree=FILE] [--out-descriptor=FILE]\n"
 "               [--compact] [--for-builtin-sig]\n"
 #ifndef _WIN32
 	}, {
@@ -58,6 +60,7 @@ static const struct fsverity_command {
 		.usage_str =
 "    fsverity sign FILE OUT_SIGFILE --key=KEYFILE\n"
 "               [--hash-alg=HASH_ALG] [--block-size=BLOCK_SIZE] [--salt=SALT]\n"
+"               [--out-merkle-tree=FILE] [--out-descriptor=FILE]\n"
 "               [--cert=CERTFILE]\n"
 	}
 };
@@ -200,6 +203,74 @@ static bool parse_salt_option(const char *arg, u8 **salt_ptr,
 	return true;
 }
 
+struct metadata_callback_ctx {
+	struct filedes merkle_tree_file;
+	struct filedes descriptor_file;
+	struct libfsverity_metadata_callbacks callbacks;
+};
+
+static int handle_merkle_tree_size(void *_ctx, u64 size)
+{
+	struct metadata_callback_ctx *ctx = _ctx;
+
+	if (!preallocate_file(&ctx->merkle_tree_file, size))
+		return -EIO;
+	return 0;
+}
+
+static int handle_merkle_tree_block(void *_ctx, const void *block, size_t size,
+				    u64 offset)
+{
+	struct metadata_callback_ctx *ctx = _ctx;
+
+	if (!full_pwrite(&ctx->merkle_tree_file, block, size, offset))
+		return -EIO;
+	return 0;
+}
+
+static int handle_descriptor(void *_ctx, const void *descriptor, size_t size)
+{
+	struct metadata_callback_ctx *ctx = _ctx;
+
+	if (!full_write(&ctx->descriptor_file, descriptor, size))
+		return -EIO;
+	return 0;
+}
+
+static bool parse_out_metadata_option(int opt_char, const char *arg,
+				      const struct libfsverity_metadata_callbacks **cbs)
+{
+	struct metadata_callback_ctx *ctx;
+	struct filedes *file;
+	const char *opt_name;
+
+	if (*cbs) {
+		ctx = (*cbs)->ctx;
+	} else {
+		ctx = xzalloc(sizeof(*ctx));
+		ctx->merkle_tree_file.fd = -1;
+		ctx->descriptor_file.fd = -1;
+		ctx->callbacks.ctx = ctx;
+		*cbs = &ctx->callbacks;
+	}
+
+	if (opt_char == OPT_OUT_MERKLE_TREE) {
+		file = &ctx->merkle_tree_file;
+		opt_name = "--out-merkle-tree";
+		ctx->callbacks.merkle_tree_size = handle_merkle_tree_size;
+		ctx->callbacks.merkle_tree_block = handle_merkle_tree_block;
+	} else {
+		file = &ctx->descriptor_file;
+		opt_name = "--out-descriptor";
+		ctx->callbacks.descriptor = handle_descriptor;
+	}
+	if (file->fd >= 0) {
+		error_msg("%s can only be specified once", opt_name);
+		return false;
+	}
+	return open_file(file, arg, O_WRONLY|O_CREAT|O_TRUNC, 0644);
+}
+
 bool parse_tree_param(int opt_char, const char *arg,
 		      struct libfsverity_merkle_tree_params *params)
 {
@@ -211,15 +282,30 @@ bool parse_tree_param(int opt_char, const char *arg,
 	case OPT_SALT:
 		return parse_salt_option(arg, (u8 **)&params->salt,
 					 &params->salt_size);
+	case OPT_OUT_MERKLE_TREE:
+	case OPT_OUT_DESCRIPTOR:
+		return parse_out_metadata_option(opt_char, arg,
+						 &params->metadata_callbacks);
 	default:
 		ASSERT(0);
 	}
 }
 
-void destroy_tree_params(struct libfsverity_merkle_tree_params *params)
+bool destroy_tree_params(struct libfsverity_merkle_tree_params *params)
 {
+	bool ok = true;
+
 	free((u8 *)params->salt);
+	if (params->metadata_callbacks) {
+		struct metadata_callback_ctx *ctx =
+			params->metadata_callbacks->ctx;
+
+		ok &= filedes_close(&ctx->merkle_tree_file);
+		ok &= filedes_close(&ctx->descriptor_file);
+		free(ctx);
+	}
 	memset(params, 0, sizeof(*params));
+	return ok;
 }
 
 int main(int argc, char *argv[])
diff --git a/programs/fsverity.h b/programs/fsverity.h
index 9785013..fe24087 100644
--- a/programs/fsverity.h
+++ b/programs/fsverity.h
@@ -29,6 +29,8 @@ enum {
 	OPT_KEY,
 	OPT_LENGTH,
 	OPT_OFFSET,
+	OPT_OUT_DESCRIPTOR,
+	OPT_OUT_MERKLE_TREE,
 	OPT_SALT,
 	OPT_SIGNATURE,
 };
@@ -59,6 +61,6 @@ int fsverity_cmd_sign(const struct fsverity_command *cmd,
 void usage(const struct fsverity_command *cmd, FILE *fp);
 bool parse_tree_param(int opt_char, const char *arg,
 		      struct libfsverity_merkle_tree_params *params);
-void destroy_tree_params(struct libfsverity_merkle_tree_params *params);
+bool destroy_tree_params(struct libfsverity_merkle_tree_params *params);
 
 #endif /* PROGRAMS_FSVERITY_H */
-- 
2.31.1


  parent reply	other threads:[~2021-06-03 20:00 UTC|newest]

Thread overview: 11+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2021-06-03 19:58 [fsverity-utils PATCH 0/4] Add option to write Merkle tree to a file Eric Biggers
2021-06-03 19:58 ` [fsverity-utils PATCH 1/4] lib/compute_digest: add callbacks for getting the verity metadata Eric Biggers
2021-06-03 19:58 ` [fsverity-utils PATCH 2/4] programs/test_compute_digest: test the metadata callbacks Eric Biggers
2021-06-03 19:58 ` [fsverity-utils PATCH 3/4] programs/utils: add full_pwrite() and preallocate_file() Eric Biggers
2021-06-04  0:33   ` Victor Hsieh
2021-06-04  0:57     ` Eric Biggers
2021-06-04 15:24       ` Victor Hsieh
2021-06-04 16:55         ` Eric Biggers
2021-06-03 19:58 ` Eric Biggers [this message]
2021-06-04 15:25 ` [fsverity-utils PATCH 0/4] Add option to write Merkle tree to a file Victor Hsieh
2021-06-09  6:48 ` Eric Biggers

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20210603195812.50838-5-ebiggers@kernel.org \
    --to=ebiggers@kernel.org \
    --cc=linux-fscrypt@vger.kernel.org \
    --cc=victorhsieh@google.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
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.