From: Jes Sorensen <jsorensen@xxxxxx> This moves the command line parsing, error reporting etc. to the command line tool, and turns fsverity_cmd_sign() into a call returning the digest and the signature. It is the responsibility of the caller to decide what to do with the returned objects. Signed-off-by: Jes Sorensen <jsorensen@xxxxxx> --- cmd_sign.c | 132 ++++++----------------------------------------------- commands.h | 23 +++++++++- fsverity.c | 129 ++++++++++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 163 insertions(+), 121 deletions(-) diff --git a/cmd_sign.c b/cmd_sign.c index dcb37ce..2d3fa54 100644 --- a/cmd_sign.c +++ b/cmd_sign.c @@ -8,7 +8,6 @@ */ #include <fcntl.h> -#include <getopt.h> #include <limits.h> #include <openssl/bio.h> #include <openssl/err.h> @@ -38,19 +37,6 @@ struct fsverity_descriptor { __u8 signature[]; /* optional PKCS#7 signature */ }; -/* - * Format in which verity file measurements are signed. This is the same as - * 'struct fsverity_digest', except here some magic bytes are prepended to - * provide some context about what is being signed in case the same key is used - * for non-fsverity purposes, and here the fields have fixed endianness. - */ -struct fsverity_signed_digest { - char magic[8]; /* must be "FSVerity" */ - __le16 digest_algorithm; - __le16 digest_size; - __u8 digest[]; -}; - static void __printf(1, 2) __cold error_msg_openssl(const char *format, ...) { @@ -340,18 +326,6 @@ out: return ok; } -static bool write_signature(const char *filename, const u8 *sig, u32 sig_size) -{ - struct filedes file; - bool ok; - - if (!open_file(&file, filename, O_WRONLY|O_CREAT|O_TRUNC, 0644)) - return false; - ok = full_write(&file, sig, sig_size); - ok &= filedes_close(&file); - return ok; -} - #define FS_VERITY_MAX_LEVELS 64 struct block_buffer { @@ -507,93 +481,27 @@ out: return ok; } -enum { - OPT_HASH_ALG, - OPT_BLOCK_SIZE, - OPT_SALT, - OPT_KEY, - OPT_CERT, -}; - -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}, - {NULL, 0, NULL, 0} -}; - /* Sign a file for fs-verity by computing its measurement, then signing it. */ -int fsverity_cmd_sign(const struct fsverity_command *cmd, - int argc, char *argv[]) +int fsverity_cmd_sign(char *filename, const struct fsverity_hash_alg *hash_alg, + u32 block_size, u8 *salt, u32 salt_size, + const char *keyfile, const char *certfile, + struct fsverity_signed_digest **retdigest, + u8 **sig, u32 *sig_size) { - const struct fsverity_hash_alg *hash_alg = NULL; - u32 block_size = 0; - u8 *salt = NULL; - u32 salt_size = 0; - const char *keyfile = NULL; - const char *certfile = NULL; struct fsverity_signed_digest *digest = NULL; - char digest_hex[FS_VERITY_MAX_DIGEST_SIZE * 2 + 1]; - u8 *sig = NULL; - u32 sig_size; int status; - int c; - - while ((c = getopt_long(argc, argv, "", longopts, NULL)) != -1) { - switch (c) { - case OPT_HASH_ALG: - if (hash_alg != NULL) { - error_msg("--hash-alg can only be specified once"); - goto out_usage; - } - hash_alg = find_hash_alg_by_name(optarg); - if (hash_alg == NULL) - goto out_usage; - break; - case OPT_BLOCK_SIZE: - if (!parse_block_size_option(optarg, &block_size)) - goto out_usage; - break; - case OPT_SALT: - if (!parse_salt_option(optarg, &salt, &salt_size)) - goto out_usage; - break; - case OPT_KEY: - if (keyfile != NULL) { - error_msg("--key can only be specified once"); - goto out_usage; - } - keyfile = optarg; - break; - case OPT_CERT: - if (certfile != NULL) { - error_msg("--cert can only be specified once"); - goto out_usage; - } - certfile = optarg; - break; - default: - goto out_usage; - } - } - argv += optind; - argc -= optind; - - if (argc != 2) - goto out_usage; - - if (hash_alg == NULL) - hash_alg = &fsverity_hash_algs[FS_VERITY_HASH_ALG_DEFAULT]; + if (hash_alg == NULL) { + status = -EINVAL; + goto out; + } if (block_size == 0) block_size = get_default_block_size(); if (keyfile == NULL) { - error_msg("Missing --key argument"); - goto out_usage; + status = -EINVAL; + goto out; } if (certfile == NULL) certfile = keyfile; @@ -603,33 +511,21 @@ int fsverity_cmd_sign(const struct fsverity_command *cmd, digest->digest_algorithm = cpu_to_le16(hash_alg - fsverity_hash_algs); digest->digest_size = cpu_to_le16(hash_alg->digest_size); - if (!compute_file_measurement(argv[0], hash_alg, block_size, + if (!compute_file_measurement(filename, hash_alg, block_size, salt, salt_size, digest->digest)) goto out_err; if (!sign_data(digest, sizeof(*digest) + hash_alg->digest_size, - keyfile, certfile, hash_alg, &sig, &sig_size)) - goto out_err; - - if (!write_signature(argv[1], sig, sig_size)) + keyfile, certfile, hash_alg, sig, sig_size)) goto out_err; - bin2hex(digest->digest, hash_alg->digest_size, digest_hex); - printf("Signed file '%s' (%s:%s)\n", argv[0], hash_alg->name, - digest_hex); + *retdigest = digest; status = 0; out: - free(salt); - free(digest); - free(sig); return status; out_err: status = 1; goto out; -out_usage: - usage(cmd, stderr); - status = 2; - goto out; } diff --git a/commands.h b/commands.h index 98f9745..c38fcea 100644 --- a/commands.h +++ b/commands.h @@ -5,17 +5,36 @@ #include <stdio.h> #include "util.h" +#include "hash_algs.h" +#include "fsverity_uapi.h" struct fsverity_command; +/* + * Format in which verity file measurements are signed. This is the same as + * 'struct fsverity_digest', except here some magic bytes are prepended to + * provide some context about what is being signed in case the same key is used + * for non-fsverity purposes, and here the fields have fixed endianness. + */ +struct fsverity_signed_digest { + char magic[8]; /* must be "FSVerity" */ + __le16 digest_algorithm; + __le16 digest_size; + __u8 digest[]; +}; + + void usage(const struct fsverity_command *cmd, FILE *fp); int fsverity_cmd_enable(const struct fsverity_command *cmd, int argc, char *argv[]); int fsverity_cmd_measure(const struct fsverity_command *cmd, int argc, char *argv[]); -int fsverity_cmd_sign(const struct fsverity_command *cmd, - int argc, char *argv[]); +int fsverity_cmd_sign(char *filename, const struct fsverity_hash_alg *hash_alg, + u32 block_size, u8 *salt, u32 salt_size, + const char *keyfile, const char *certfile, + struct fsverity_signed_digest **retdigest, + u8 **sig, u32 *sig_size); bool parse_block_size_option(const char *arg, u32 *size_ptr); u32 get_default_block_size(void); diff --git a/fsverity.c b/fsverity.c index 9a44df1..6246031 100644 --- a/fsverity.c +++ b/fsverity.c @@ -7,14 +7,141 @@ * Written by Eric Biggers. */ +#include <fcntl.h> #include <limits.h> #include <stdlib.h> #include <string.h> #include <unistd.h> +#include <getopt.h> +#include <errno.h> #include "commands.h" #include "hash_algs.h" +enum { + OPT_HASH_ALG, + OPT_BLOCK_SIZE, + OPT_SALT, + OPT_KEY, + OPT_CERT, +}; + +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}, + {NULL, 0, NULL, 0} +}; + +static bool write_signature(const char *filename, const u8 *sig, u32 sig_size) +{ + struct filedes file; + bool ok; + + if (!open_file(&file, filename, O_WRONLY|O_CREAT|O_TRUNC, 0644)) + return false; + ok = full_write(&file, sig, sig_size); + ok &= filedes_close(&file); + return ok; +} + +int wrap_cmd_sign(const struct fsverity_command *cmd, int argc, char *argv[]) +{ + struct fsverity_signed_digest *digest = NULL; + u8 *sig = NULL; + u32 sig_size; + const struct fsverity_hash_alg *hash_alg = NULL; + u32 block_size = 0; + u8 *salt = NULL; + u32 salt_size = 0; + const char *keyfile = NULL; + const char *certfile = NULL; + int c, status; + char digest_hex[FS_VERITY_MAX_DIGEST_SIZE * 2 + 1]; + + while ((c = getopt_long(argc, argv, "", longopts, NULL)) != -1) { + switch (c) { + case OPT_HASH_ALG: + if (hash_alg != NULL) { + error_msg("--hash-alg can only be specified once"); + goto out_usage; + } + hash_alg = find_hash_alg_by_name(optarg); + if (hash_alg == NULL) + goto out_usage; + break; + case OPT_BLOCK_SIZE: + if (!parse_block_size_option(optarg, &block_size)) + goto out_usage; + break; + case OPT_SALT: + if (!parse_salt_option(optarg, &salt, &salt_size)) + goto out_usage; + break; + case OPT_KEY: + if (keyfile != NULL) { + error_msg("--key can only be specified once"); + goto out_usage; + } + keyfile = optarg; + break; + case OPT_CERT: + if (certfile != NULL) { + error_msg("--cert can only be specified once"); + goto out_usage; + } + certfile = optarg; + break; + default: + goto out_usage; + } + } + + if (keyfile == NULL) { + status = -EINVAL; + error_msg("Missing --key argument"); + goto out_usage; + } + + argv += optind; + argc -= optind; + + if (hash_alg == NULL) + hash_alg = &fsverity_hash_algs[FS_VERITY_HASH_ALG_DEFAULT]; + + if (argc != 2) + goto out_usage; + + status = fsverity_cmd_sign(argv[0], hash_alg, block_size, salt, salt_size, + keyfile, certfile, &digest, &sig, &sig_size); + if (status == -EINVAL) + goto out_usage; + if (status != 0) + goto out; + + if (!write_signature(argv[1], sig, sig_size)) { + status = -EIO; + goto out; + } + + bin2hex(digest->digest, hash_alg->digest_size, digest_hex); + printf("Signed file '%s' (%s:%s)\n", argv[0], hash_alg->name, + digest_hex); + + out: + free(salt); + free(digest); + free(sig); + return status; + + out_usage: + usage(cmd, stderr); + status = 2; + goto out; +} + static const struct fsverity_command { const char *name; int (*func)(const struct fsverity_command *cmd, int argc, char *argv[]); @@ -38,7 +165,7 @@ static const struct fsverity_command { " fsverity measure FILE...\n" }, { .name = "sign", - .func = fsverity_cmd_sign, + .func = wrap_cmd_sign, .short_desc = "Sign a file for fs-verity", .usage_str = " fsverity sign FILE OUT_SIGFILE --key=KEYFILE\n" -- 2.24.1