mirror of
https://github.com/openharmony/third_party_fsverity-utils.git
synced 2026-07-01 10:05:35 -04:00
Introduce libfsverity
From the 'fsverity' program, split out a library 'libfsverity'.
Currently it supports computing file measurements ("digests"), and
signing those file measurements for use with the fs-verity builtin
signature verification feature.
Rewritten from patches by Jes Sorensen <jsorensen@fb.com>.
I made a lot of improvements, e.g.:
- Separated library and program source into different directories.
- Drastically improved the Makefile.
- Added 'make check' target and rules to build test programs.
- In the shared lib, only export the functions intended to be public.
- Prefixed global functions with "libfsverity_" so that they don't cause
conflicts when the library is built as a static library.
- Made library error messages be sent to a user-specified callback
rather than always be printed to stderr.
- Keep showing OpenSSL error messages.
- Stopped abort()ing in library code, when possible.
- Made libfsverity_digest use native endianness.
- Moved file_size into the merkle_tree_params.
- Made libfsverity_get_hash_name() just return the static strings.
- Made some variables in the API uint32_t instead of uint16_t.
- Shared parse_hash_alg_option() between cmd_enable and cmd_sign.
- Lots of other fixes.
(Folded in a couple Makefile fixes from Jes.)
Reviewed-by: Jes Sorensen <jsorensen@fb.com>
Signed-off-by: Eric Biggers <ebiggers@google.com>
This commit is contained in:
@@ -0,0 +1,140 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
/*
|
||||
* The 'fsverity enable' command
|
||||
*
|
||||
* Copyright 2018 Google LLC
|
||||
*/
|
||||
|
||||
#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,
|
||||
OPT_BLOCK_SIZE,
|
||||
OPT_SALT,
|
||||
OPT_SIGNATURE,
|
||||
};
|
||||
|
||||
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},
|
||||
{"signature", required_argument, NULL, OPT_SIGNATURE},
|
||||
{NULL, 0, NULL, 0}
|
||||
};
|
||||
|
||||
/* Enable fs-verity on a file. */
|
||||
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;
|
||||
int status;
|
||||
int c;
|
||||
|
||||
while ((c = getopt_long(argc, argv, "", longopts, NULL)) != -1) {
|
||||
switch (c) {
|
||||
case OPT_HASH_ALG:
|
||||
if (!parse_hash_alg_option(optarg, &arg.hash_algorithm))
|
||||
goto out_usage;
|
||||
break;
|
||||
case OPT_BLOCK_SIZE:
|
||||
if (!parse_block_size_option(optarg, &arg.block_size))
|
||||
goto out_usage;
|
||||
break;
|
||||
case OPT_SALT:
|
||||
if (!parse_salt_option(optarg, &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))
|
||||
goto out_err;
|
||||
arg.sig_ptr = (uintptr_t)sig;
|
||||
break;
|
||||
default:
|
||||
goto out_usage;
|
||||
}
|
||||
}
|
||||
|
||||
argv += optind;
|
||||
argc -= optind;
|
||||
|
||||
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))
|
||||
goto out_err;
|
||||
|
||||
status = 0;
|
||||
out:
|
||||
free(salt);
|
||||
free(sig);
|
||||
return status;
|
||||
|
||||
out_err:
|
||||
status = 1;
|
||||
goto out;
|
||||
|
||||
out_usage:
|
||||
usage(cmd, stderr);
|
||||
status = 2;
|
||||
goto out;
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
/*
|
||||
* The 'fsverity measure' command
|
||||
*
|
||||
* Copyright 2018 Google LLC
|
||||
*/
|
||||
|
||||
#include "fsverity.h"
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <sys/ioctl.h>
|
||||
|
||||
/* Display the measurement of the given verity file(s). */
|
||||
int fsverity_cmd_measure(const struct fsverity_command *cmd,
|
||||
int argc, char *argv[])
|
||||
{
|
||||
struct fsverity_digest *d = NULL;
|
||||
struct filedes file;
|
||||
char digest_hex[FS_VERITY_MAX_DIGEST_SIZE * 2 + 1];
|
||||
char _hash_alg_name[32];
|
||||
const char *hash_alg_name;
|
||||
int status;
|
||||
int i;
|
||||
|
||||
if (argc < 2)
|
||||
goto out_usage;
|
||||
|
||||
d = xzalloc(sizeof(*d) + FS_VERITY_MAX_DIGEST_SIZE);
|
||||
|
||||
for (i = 1; i < argc; i++) {
|
||||
d->digest_size = FS_VERITY_MAX_DIGEST_SIZE;
|
||||
|
||||
if (!open_file(&file, argv[i], O_RDONLY, 0))
|
||||
goto out_err;
|
||||
if (ioctl(file.fd, FS_IOC_MEASURE_VERITY, d) != 0) {
|
||||
error_msg_errno("FS_IOC_MEASURE_VERITY failed on '%s'",
|
||||
file.name);
|
||||
filedes_close(&file);
|
||||
goto out_err;
|
||||
}
|
||||
filedes_close(&file);
|
||||
|
||||
ASSERT(d->digest_size <= FS_VERITY_MAX_DIGEST_SIZE);
|
||||
bin2hex(d->digest, d->digest_size, digest_hex);
|
||||
hash_alg_name = libfsverity_get_hash_name(d->digest_algorithm);
|
||||
if (!hash_alg_name) {
|
||||
sprintf(_hash_alg_name, "ALG_%u", d->digest_algorithm);
|
||||
hash_alg_name = _hash_alg_name;
|
||||
}
|
||||
printf("%s:%s %s\n", hash_alg_name, digest_hex, argv[i]);
|
||||
}
|
||||
status = 0;
|
||||
out:
|
||||
free(d);
|
||||
return status;
|
||||
|
||||
out_err:
|
||||
status = 1;
|
||||
goto out;
|
||||
|
||||
out_usage:
|
||||
usage(cmd, stderr);
|
||||
status = 2;
|
||||
goto out;
|
||||
}
|
||||
@@ -0,0 +1,163 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
/*
|
||||
* The 'fsverity sign' command
|
||||
*
|
||||
* Copyright 2018 Google LLC
|
||||
*/
|
||||
|
||||
#include "fsverity.h"
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <getopt.h>
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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 int read_callback(void *file, void *buf, size_t count)
|
||||
{
|
||||
errno = 0;
|
||||
if (!full_read(file, buf, count))
|
||||
return errno ? -errno : -EIO;
|
||||
return 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[])
|
||||
{
|
||||
struct filedes file = { .fd = -1 };
|
||||
u8 *salt = NULL;
|
||||
struct libfsverity_merkle_tree_params tree_params = { .version = 1 };
|
||||
struct libfsverity_signature_params sig_params = {};
|
||||
struct libfsverity_digest *digest = NULL;
|
||||
char digest_hex[FS_VERITY_MAX_DIGEST_SIZE * 2 + 1];
|
||||
u8 *sig = NULL;
|
||||
size_t sig_size;
|
||||
int status;
|
||||
int c;
|
||||
|
||||
while ((c = getopt_long(argc, argv, "", longopts, NULL)) != -1) {
|
||||
switch (c) {
|
||||
case OPT_HASH_ALG:
|
||||
if (!parse_hash_alg_option(optarg,
|
||||
&tree_params.hash_algorithm))
|
||||
goto out_usage;
|
||||
break;
|
||||
case OPT_BLOCK_SIZE:
|
||||
if (!parse_block_size_option(optarg,
|
||||
&tree_params.block_size))
|
||||
goto out_usage;
|
||||
break;
|
||||
case OPT_SALT:
|
||||
if (!parse_salt_option(optarg, &salt,
|
||||
&tree_params.salt_size))
|
||||
goto out_usage;
|
||||
tree_params.salt = salt;
|
||||
break;
|
||||
case OPT_KEY:
|
||||
if (sig_params.keyfile != NULL) {
|
||||
error_msg("--key can only be specified once");
|
||||
goto out_usage;
|
||||
}
|
||||
sig_params.keyfile = optarg;
|
||||
break;
|
||||
case OPT_CERT:
|
||||
if (sig_params.certfile != NULL) {
|
||||
error_msg("--cert can only be specified once");
|
||||
goto out_usage;
|
||||
}
|
||||
sig_params.certfile = optarg;
|
||||
break;
|
||||
default:
|
||||
goto out_usage;
|
||||
}
|
||||
}
|
||||
|
||||
argv += optind;
|
||||
argc -= optind;
|
||||
|
||||
if (argc != 2)
|
||||
goto out_usage;
|
||||
|
||||
if (tree_params.hash_algorithm == 0)
|
||||
tree_params.hash_algorithm = FS_VERITY_HASH_ALG_DEFAULT;
|
||||
|
||||
if (tree_params.block_size == 0)
|
||||
tree_params.block_size = get_default_block_size();
|
||||
|
||||
if (sig_params.keyfile == NULL) {
|
||||
error_msg("Missing --key argument");
|
||||
goto out_usage;
|
||||
}
|
||||
if (sig_params.certfile == NULL)
|
||||
sig_params.certfile = sig_params.keyfile;
|
||||
|
||||
if (!open_file(&file, argv[0], O_RDONLY, 0))
|
||||
goto out_err;
|
||||
|
||||
if (!get_file_size(&file, &tree_params.file_size))
|
||||
goto out_err;
|
||||
|
||||
if (libfsverity_compute_digest(&file, read_callback,
|
||||
&tree_params, &digest) != 0) {
|
||||
error_msg("failed to compute digest");
|
||||
goto out_err;
|
||||
}
|
||||
|
||||
if (libfsverity_sign_digest(digest, &sig_params,
|
||||
&sig, &sig_size) != 0) {
|
||||
error_msg("failed to sign digest");
|
||||
goto out_err;
|
||||
}
|
||||
|
||||
if (!write_signature(argv[1], sig, sig_size))
|
||||
goto out_err;
|
||||
|
||||
ASSERT(digest->digest_size <= FS_VERITY_MAX_DIGEST_SIZE);
|
||||
bin2hex(digest->digest, digest->digest_size, digest_hex);
|
||||
printf("Signed file '%s' (%s:%s)\n", argv[0],
|
||||
libfsverity_get_hash_name(tree_params.hash_algorithm),
|
||||
digest_hex);
|
||||
status = 0;
|
||||
out:
|
||||
filedes_close(&file);
|
||||
free(salt);
|
||||
free(digest);
|
||||
free(sig);
|
||||
return status;
|
||||
|
||||
out_err:
|
||||
status = 1;
|
||||
goto out;
|
||||
|
||||
out_usage:
|
||||
usage(cmd, stderr);
|
||||
status = 2;
|
||||
goto out;
|
||||
}
|
||||
@@ -0,0 +1,232 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
/*
|
||||
* fs-verity userspace tool
|
||||
*
|
||||
* Copyright 2018 Google LLC
|
||||
*/
|
||||
|
||||
#include "fsverity.h"
|
||||
|
||||
#include <limits.h>
|
||||
#include <unistd.h>
|
||||
|
||||
static const struct fsverity_command {
|
||||
const char *name;
|
||||
int (*func)(const struct fsverity_command *cmd, int argc, char *argv[]);
|
||||
const char *short_desc;
|
||||
const char *usage_str;
|
||||
} fsverity_commands[] = {
|
||||
{
|
||||
.name = "enable",
|
||||
.func = fsverity_cmd_enable,
|
||||
.short_desc = "Enable fs-verity on a file",
|
||||
.usage_str =
|
||||
" fsverity enable FILE\n"
|
||||
" [--hash-alg=HASH_ALG] [--block-size=BLOCK_SIZE] [--salt=SALT]\n"
|
||||
" [--signature=SIGFILE]\n"
|
||||
}, {
|
||||
.name = "measure",
|
||||
.func = fsverity_cmd_measure,
|
||||
.short_desc =
|
||||
"Display the measurement of the given verity file(s)",
|
||||
.usage_str =
|
||||
" fsverity measure FILE...\n"
|
||||
}, {
|
||||
.name = "sign",
|
||||
.func = fsverity_cmd_sign,
|
||||
.short_desc = "Sign a file for fs-verity",
|
||||
.usage_str =
|
||||
" fsverity sign FILE OUT_SIGFILE --key=KEYFILE\n"
|
||||
" [--hash-alg=HASH_ALG] [--block-size=BLOCK_SIZE] [--salt=SALT]\n"
|
||||
" [--cert=CERTFILE]\n"
|
||||
}
|
||||
};
|
||||
|
||||
static void show_all_hash_algs(FILE *fp)
|
||||
{
|
||||
u32 alg_num = 1;
|
||||
const char *name;
|
||||
|
||||
fprintf(fp, "Available hash algorithms:");
|
||||
while ((name = libfsverity_get_hash_name(alg_num++)) != NULL)
|
||||
fprintf(fp, " %s", name);
|
||||
putc('\n', fp);
|
||||
}
|
||||
|
||||
static void usage_all(FILE *fp)
|
||||
{
|
||||
int i;
|
||||
|
||||
fputs("Usage:\n", fp);
|
||||
for (i = 0; i < ARRAY_SIZE(fsverity_commands); i++)
|
||||
fprintf(fp, " %s:\n%s\n", fsverity_commands[i].short_desc,
|
||||
fsverity_commands[i].usage_str);
|
||||
fputs(
|
||||
" Standard options:\n"
|
||||
" fsverity --help\n"
|
||||
" fsverity --version\n"
|
||||
"\n", fp);
|
||||
show_all_hash_algs(fp);
|
||||
}
|
||||
|
||||
static void usage_cmd(const struct fsverity_command *cmd, FILE *fp)
|
||||
{
|
||||
fprintf(fp, "Usage:\n%s", cmd->usage_str);
|
||||
}
|
||||
|
||||
void usage(const struct fsverity_command *cmd, FILE *fp)
|
||||
{
|
||||
if (cmd)
|
||||
usage_cmd(cmd, fp);
|
||||
else
|
||||
usage_all(fp);
|
||||
}
|
||||
|
||||
#define PACKAGE_VERSION "v1.0"
|
||||
#define PACKAGE_BUGREPORT "linux-fscrypt@vger.kernel.org"
|
||||
|
||||
static void show_version(void)
|
||||
{
|
||||
static const char * const str =
|
||||
"fsverity " PACKAGE_VERSION "\n"
|
||||
"Copyright 2018 Google LLC\n"
|
||||
"License GPLv2+: GNU GPL version 2 or later <http://gnu.org/licenses/gpl.html>.\n"
|
||||
"This is free software: you are free to change and redistribute it.\n"
|
||||
"There is NO WARRANTY, to the extent permitted by law.\n"
|
||||
"\n"
|
||||
"Report bugs to " PACKAGE_BUGREPORT ".\n";
|
||||
fputs(str, stdout);
|
||||
}
|
||||
|
||||
static void handle_common_options(int argc, char *argv[],
|
||||
const struct fsverity_command *cmd)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 1; i < argc; i++) {
|
||||
const char *arg = argv[i];
|
||||
|
||||
if (*arg++ != '-')
|
||||
continue;
|
||||
if (*arg++ != '-')
|
||||
continue;
|
||||
if (!strcmp(arg, "help")) {
|
||||
usage(cmd, stdout);
|
||||
exit(0);
|
||||
} else if (!strcmp(arg, "version")) {
|
||||
show_version();
|
||||
exit(0);
|
||||
} else if (!*arg) /* reached "--", no more options */
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static const struct fsverity_command *find_command(const char *name)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(fsverity_commands); i++)
|
||||
if (!strcmp(name, fsverity_commands[i].name))
|
||||
return &fsverity_commands[i];
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bool parse_hash_alg_option(const char *arg, u32 *alg_ptr)
|
||||
{
|
||||
char *end;
|
||||
unsigned long n = strtoul(arg, &end, 10);
|
||||
|
||||
if (*alg_ptr != 0) {
|
||||
error_msg("--hash-alg can only be specified once");
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Specified by number? */
|
||||
if (n > 0 && n < INT32_MAX && *end == '\0') {
|
||||
*alg_ptr = n;
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Specified by name? */
|
||||
*alg_ptr = libfsverity_find_hash_alg_by_name(arg);
|
||||
if (*alg_ptr)
|
||||
return true;
|
||||
error_msg("unknown hash algorithm: '%s'", arg);
|
||||
show_all_hash_algs(stderr);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool parse_block_size_option(const char *arg, u32 *size_ptr)
|
||||
{
|
||||
char *end;
|
||||
unsigned long n = strtoul(arg, &end, 10);
|
||||
|
||||
if (*size_ptr != 0) {
|
||||
error_msg("--block-size can only be specified once");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (n <= 0 || n >= INT_MAX || !is_power_of_2(n) || *end != '\0') {
|
||||
error_msg("Invalid block size: %s. Must be power of 2", arg);
|
||||
return false;
|
||||
}
|
||||
*size_ptr = n;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool parse_salt_option(const char *arg, u8 **salt_ptr, u32 *salt_size_ptr)
|
||||
{
|
||||
if (*salt_ptr != NULL) {
|
||||
error_msg("--salt can only be specified once");
|
||||
return false;
|
||||
}
|
||||
*salt_size_ptr = strlen(arg) / 2;
|
||||
*salt_ptr = xmalloc(*salt_size_ptr);
|
||||
if (!hex2bin(arg, *salt_ptr, *salt_size_ptr)) {
|
||||
error_msg("salt is not a valid hex string");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
u32 get_default_block_size(void)
|
||||
{
|
||||
long n = sysconf(_SC_PAGESIZE);
|
||||
|
||||
if (n <= 0 || n >= INT_MAX || !is_power_of_2(n)) {
|
||||
fprintf(stderr,
|
||||
"Warning: invalid _SC_PAGESIZE (%ld). Assuming 4K blocks.\n",
|
||||
n);
|
||||
return 4096;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
static void print_libfsverity_error(const char *msg)
|
||||
{
|
||||
error_msg("%s", msg);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
const struct fsverity_command *cmd;
|
||||
|
||||
libfsverity_set_error_callback(print_libfsverity_error);
|
||||
|
||||
if (argc < 2) {
|
||||
error_msg("no command specified");
|
||||
usage_all(stderr);
|
||||
return 2;
|
||||
}
|
||||
|
||||
cmd = find_command(argv[1]);
|
||||
|
||||
handle_common_options(argc, argv, cmd);
|
||||
|
||||
if (!cmd) {
|
||||
error_msg("unrecognized command: '%s'", argv[1]);
|
||||
usage_all(stderr);
|
||||
return 2;
|
||||
}
|
||||
return cmd->func(cmd, argc - 1, argv + 1);
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
/*
|
||||
* Private header for the 'fsverity' program
|
||||
*
|
||||
* Copyright 2018 Google LLC
|
||||
*/
|
||||
#ifndef PROGRAMS_FSVERITY_H
|
||||
#define PROGRAMS_FSVERITY_H
|
||||
|
||||
#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_enable.c */
|
||||
int fsverity_cmd_enable(const struct fsverity_command *cmd,
|
||||
int argc, char *argv[]);
|
||||
|
||||
/* cmd_measure.c */
|
||||
int fsverity_cmd_measure(const struct fsverity_command *cmd,
|
||||
int argc, char *argv[]);
|
||||
|
||||
/* cmd_sign.c */
|
||||
int fsverity_cmd_sign(const struct fsverity_command *cmd,
|
||||
int argc, char *argv[]);
|
||||
|
||||
/* fsverity.c */
|
||||
void usage(const struct fsverity_command *cmd, FILE *fp);
|
||||
bool parse_hash_alg_option(const char *arg, u32 *alg_ptr);
|
||||
bool parse_block_size_option(const char *arg, u32 *size_ptr);
|
||||
bool parse_salt_option(const char *arg, u8 **salt_ptr, u32 *salt_size_ptr);
|
||||
u32 get_default_block_size(void);
|
||||
|
||||
#endif /* PROGRAMS_FSVERITY_H */
|
||||
@@ -0,0 +1,210 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
/*
|
||||
* Utility functions for the 'fsverity' program
|
||||
*
|
||||
* Copyright 2018 Google LLC
|
||||
*/
|
||||
|
||||
#include "utils.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <limits.h>
|
||||
#include <stdarg.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
|
||||
/* ========== Memory allocation ========== */
|
||||
|
||||
void *xmalloc(size_t size)
|
||||
{
|
||||
void *p = malloc(size);
|
||||
|
||||
if (!p)
|
||||
fatal_error("out of memory");
|
||||
return p;
|
||||
}
|
||||
|
||||
void *xzalloc(size_t size)
|
||||
{
|
||||
return memset(xmalloc(size), 0, size);
|
||||
}
|
||||
|
||||
void *xmemdup(const void *mem, size_t size)
|
||||
{
|
||||
return memcpy(xmalloc(size), mem, size);
|
||||
}
|
||||
|
||||
char *xstrdup(const char *s)
|
||||
{
|
||||
return xmemdup(s, strlen(s) + 1);
|
||||
}
|
||||
|
||||
/* ========== Error messages and assertions ========== */
|
||||
|
||||
static void do_error_msg(const char *format, va_list va, int err)
|
||||
{
|
||||
fputs("ERROR: ", stderr);
|
||||
vfprintf(stderr, format, va);
|
||||
if (err)
|
||||
fprintf(stderr, ": %s", strerror(err));
|
||||
putc('\n', stderr);
|
||||
}
|
||||
|
||||
void error_msg(const char *format, ...)
|
||||
{
|
||||
va_list va;
|
||||
|
||||
va_start(va, format);
|
||||
do_error_msg(format, va, 0);
|
||||
va_end(va);
|
||||
}
|
||||
|
||||
void error_msg_errno(const char *format, ...)
|
||||
{
|
||||
va_list va;
|
||||
|
||||
va_start(va, format);
|
||||
do_error_msg(format, va, errno);
|
||||
va_end(va);
|
||||
}
|
||||
|
||||
__noreturn void fatal_error(const char *format, ...)
|
||||
{
|
||||
va_list va;
|
||||
|
||||
va_start(va, format);
|
||||
do_error_msg(format, va, 0);
|
||||
va_end(va);
|
||||
abort();
|
||||
}
|
||||
|
||||
__noreturn void assertion_failed(const char *expr, const char *file, int line)
|
||||
{
|
||||
fatal_error("Assertion failed: %s at %s:%d", expr, file, line);
|
||||
}
|
||||
|
||||
/* ========== File utilities ========== */
|
||||
|
||||
bool open_file(struct filedes *file, const char *filename, int flags, int mode)
|
||||
{
|
||||
file->fd = open(filename, flags, mode);
|
||||
if (file->fd < 0) {
|
||||
error_msg_errno("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 = xstrdup(filename);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool get_file_size(struct filedes *file, u64 *size_ret)
|
||||
{
|
||||
struct stat stbuf;
|
||||
|
||||
if (fstat(file->fd, &stbuf) != 0) {
|
||||
error_msg_errno("can't stat file '%s'", file->name);
|
||||
return false;
|
||||
}
|
||||
*size_ret = stbuf.st_size;
|
||||
return true;
|
||||
}
|
||||
|
||||
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) {
|
||||
error_msg_errno("reading from '%s'", file->name);
|
||||
return false;
|
||||
}
|
||||
if (n == 0) {
|
||||
error_msg("unexpected end-of-file on '%s'", file->name);
|
||||
return false;
|
||||
}
|
||||
buf += n;
|
||||
count -= n;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool full_write(struct filedes *file, const void *buf, size_t count)
|
||||
{
|
||||
while (count) {
|
||||
int n = write(file->fd, buf, min(count, INT_MAX));
|
||||
|
||||
if (n < 0) {
|
||||
error_msg_errno("writing to '%s'", file->name);
|
||||
return false;
|
||||
}
|
||||
buf += n;
|
||||
count -= n;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool filedes_close(struct filedes *file)
|
||||
{
|
||||
int res;
|
||||
|
||||
if (file->fd < 0)
|
||||
return true;
|
||||
res = close(file->fd);
|
||||
if (res != 0)
|
||||
error_msg_errno("closing '%s'", file->name);
|
||||
file->fd = -1;
|
||||
free(file->name);
|
||||
file->name = NULL;
|
||||
return res == 0;
|
||||
}
|
||||
|
||||
/* ========== String utilities ========== */
|
||||
|
||||
static int hex2bin_char(char c)
|
||||
{
|
||||
if (c >= '0' && c <= '9')
|
||||
return c - '0';
|
||||
if (c >= 'a' && c <= 'f')
|
||||
return 10 + (c - 'a');
|
||||
if (c >= 'A' && c <= 'F')
|
||||
return 10 + (c - 'A');
|
||||
return -1;
|
||||
}
|
||||
|
||||
bool hex2bin(const char *hex, u8 *bin, size_t bin_len)
|
||||
{
|
||||
if (strlen(hex) != 2 * bin_len)
|
||||
return false;
|
||||
|
||||
while (bin_len--) {
|
||||
int hi = hex2bin_char(*hex++);
|
||||
int lo = hex2bin_char(*hex++);
|
||||
|
||||
if (hi < 0 || lo < 0)
|
||||
return false;
|
||||
*bin++ = (hi << 4) | lo;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static char bin2hex_char(u8 nibble)
|
||||
{
|
||||
ASSERT(nibble <= 0xf);
|
||||
|
||||
if (nibble < 10)
|
||||
return '0' + nibble;
|
||||
return 'a' + (nibble - 10);
|
||||
}
|
||||
|
||||
void bin2hex(const u8 *bin, size_t bin_len, char *hex)
|
||||
{
|
||||
while (bin_len--) {
|
||||
*hex++ = bin2hex_char(*bin >> 4);
|
||||
*hex++ = bin2hex_char(*bin & 0xf);
|
||||
bin++;
|
||||
}
|
||||
*hex = '\0';
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
/*
|
||||
* Utility functions for programs
|
||||
*
|
||||
* Copyright 2018 Google LLC
|
||||
*/
|
||||
#ifndef PROGRAMS_UTILS_H
|
||||
#define PROGRAMS_UTILS_H
|
||||
|
||||
#include "../common/libfsverity.h"
|
||||
#include "../common/common_defs.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
void *xmalloc(size_t size);
|
||||
void *xzalloc(size_t size);
|
||||
void *xmemdup(const void *mem, size_t size);
|
||||
char *xstrdup(const char *s);
|
||||
|
||||
__printf(1, 2) __cold void error_msg(const char *format, ...);
|
||||
__printf(1, 2) __cold void error_msg_errno(const char *format, ...);
|
||||
__printf(1, 2) __cold __noreturn void fatal_error(const char *format, ...);
|
||||
__cold __noreturn void assertion_failed(const char *expr,
|
||||
const char *file, int line);
|
||||
|
||||
#define ASSERT(e) ({ if (!(e)) assertion_failed(#e, __FILE__, __LINE__); })
|
||||
|
||||
struct filedes {
|
||||
int fd;
|
||||
char *name; /* filename, for logging or error messages */
|
||||
};
|
||||
|
||||
bool open_file(struct filedes *file, const char *filename, int flags, int mode);
|
||||
bool get_file_size(struct filedes *file, u64 *size_ret);
|
||||
bool full_read(struct filedes *file, void *buf, size_t count);
|
||||
bool full_write(struct filedes *file, const void *buf, size_t count);
|
||||
bool filedes_close(struct filedes *file);
|
||||
|
||||
bool hex2bin(const char *hex, u8 *bin, size_t bin_len);
|
||||
void bin2hex(const u8 *bin, size_t bin_len, char *hex);
|
||||
|
||||
#endif /* PROGRAMS_UTILS_H */
|
||||
Reference in New Issue
Block a user