1ced56a00Sopenharmony_ci// SPDX-License-Identifier: MIT 2ced56a00Sopenharmony_ci/* 3ced56a00Sopenharmony_ci * fs-verity userspace tool 4ced56a00Sopenharmony_ci * 5ced56a00Sopenharmony_ci * Copyright 2018 Google LLC 6ced56a00Sopenharmony_ci * 7ced56a00Sopenharmony_ci * Use of this source code is governed by an MIT-style 8ced56a00Sopenharmony_ci * license that can be found in the LICENSE file or at 9ced56a00Sopenharmony_ci * https://opensource.org/licenses/MIT. 10ced56a00Sopenharmony_ci */ 11ced56a00Sopenharmony_ci 12ced56a00Sopenharmony_ci#include "fsverity.h" 13ced56a00Sopenharmony_ci 14ced56a00Sopenharmony_ci#include <fcntl.h> 15ced56a00Sopenharmony_ci#include <limits.h> 16ced56a00Sopenharmony_ci 17ced56a00Sopenharmony_cistatic const struct fsverity_command { 18ced56a00Sopenharmony_ci const char *name; 19ced56a00Sopenharmony_ci int (*func)(const struct fsverity_command *cmd, int argc, char *argv[]); 20ced56a00Sopenharmony_ci const char *short_desc; 21ced56a00Sopenharmony_ci const char *usage_str; 22ced56a00Sopenharmony_ci} fsverity_commands[] = { 23ced56a00Sopenharmony_ci { 24ced56a00Sopenharmony_ci .name = "digest", 25ced56a00Sopenharmony_ci .func = fsverity_cmd_digest, 26ced56a00Sopenharmony_ci .short_desc = 27ced56a00Sopenharmony_ci"Compute the fs-verity digest of the given file(s), for offline signing", 28ced56a00Sopenharmony_ci .usage_str = 29ced56a00Sopenharmony_ci" fsverity digest FILE...\n" 30ced56a00Sopenharmony_ci" [--hash-alg=HASH_ALG] [--block-size=BLOCK_SIZE] [--salt=SALT]\n" 31ced56a00Sopenharmony_ci" [--out-merkle-tree=FILE] [--out-descriptor=FILE]\n" 32ced56a00Sopenharmony_ci" [--compact] [--for-builtin-sig]\n" 33ced56a00Sopenharmony_ci#ifndef _WIN32 34ced56a00Sopenharmony_ci }, { 35ced56a00Sopenharmony_ci .name = "dump_metadata", 36ced56a00Sopenharmony_ci .func = fsverity_cmd_dump_metadata, 37ced56a00Sopenharmony_ci .short_desc = "Dump the fs-verity metadata of the given file", 38ced56a00Sopenharmony_ci .usage_str = 39ced56a00Sopenharmony_ci" fsverity dump_metadata TYPE FILE [--offset=OFFSET] [--length=LENGTH]\n" 40ced56a00Sopenharmony_ci }, { 41ced56a00Sopenharmony_ci .name = "enable", 42ced56a00Sopenharmony_ci .func = fsverity_cmd_enable, 43ced56a00Sopenharmony_ci .short_desc = "Enable fs-verity on a file", 44ced56a00Sopenharmony_ci .usage_str = 45ced56a00Sopenharmony_ci" fsverity enable FILE\n" 46ced56a00Sopenharmony_ci" [--hash-alg=HASH_ALG] [--block-size=BLOCK_SIZE] [--salt=SALT]\n" 47ced56a00Sopenharmony_ci" [--signature=SIGFILE]\n" 48ced56a00Sopenharmony_ci }, { 49ced56a00Sopenharmony_ci .name = "measure", 50ced56a00Sopenharmony_ci .func = fsverity_cmd_measure, 51ced56a00Sopenharmony_ci .short_desc = 52ced56a00Sopenharmony_ci"Display the fs-verity digest of the given verity file(s)", 53ced56a00Sopenharmony_ci .usage_str = 54ced56a00Sopenharmony_ci" fsverity measure FILE...\n" 55ced56a00Sopenharmony_ci#endif /* !_WIN32 */ 56ced56a00Sopenharmony_ci }, { 57ced56a00Sopenharmony_ci .name = "sign", 58ced56a00Sopenharmony_ci .func = fsverity_cmd_sign, 59ced56a00Sopenharmony_ci .short_desc = "Sign a file for fs-verity built-in signature verification", 60ced56a00Sopenharmony_ci .usage_str = 61ced56a00Sopenharmony_ci" fsverity sign FILE OUT_SIGFILE\n" 62ced56a00Sopenharmony_ci" [--key=KEYFILE] [--cert=CERTFILE] [--pkcs11-engine=SOFILE]\n" 63ced56a00Sopenharmony_ci" [--pkcs11-module=SOFILE] [--pkcs11-keyid=KEYID]\n" 64ced56a00Sopenharmony_ci" [--hash-alg=HASH_ALG] [--block-size=BLOCK_SIZE] [--salt=SALT]\n" 65ced56a00Sopenharmony_ci" [--out-merkle-tree=FILE] [--out-descriptor=FILE]\n" 66ced56a00Sopenharmony_ci } 67ced56a00Sopenharmony_ci}; 68ced56a00Sopenharmony_ci 69ced56a00Sopenharmony_cistatic void show_all_hash_algs(FILE *fp) 70ced56a00Sopenharmony_ci{ 71ced56a00Sopenharmony_ci u32 alg_num = 1; 72ced56a00Sopenharmony_ci const char *name; 73ced56a00Sopenharmony_ci 74ced56a00Sopenharmony_ci fprintf(fp, "Available hash algorithms:"); 75ced56a00Sopenharmony_ci while ((name = libfsverity_get_hash_name(alg_num++)) != NULL) 76ced56a00Sopenharmony_ci fprintf(fp, " %s", name); 77ced56a00Sopenharmony_ci putc('\n', fp); 78ced56a00Sopenharmony_ci} 79ced56a00Sopenharmony_ci 80ced56a00Sopenharmony_cistatic void usage_all(FILE *fp) 81ced56a00Sopenharmony_ci{ 82ced56a00Sopenharmony_ci int i; 83ced56a00Sopenharmony_ci 84ced56a00Sopenharmony_ci fputs("Usage:\n", fp); 85ced56a00Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(fsverity_commands); i++) 86ced56a00Sopenharmony_ci fprintf(fp, " %s:\n%s\n", fsverity_commands[i].short_desc, 87ced56a00Sopenharmony_ci fsverity_commands[i].usage_str); 88ced56a00Sopenharmony_ci fputs( 89ced56a00Sopenharmony_ci" Standard options:\n" 90ced56a00Sopenharmony_ci" fsverity --help\n" 91ced56a00Sopenharmony_ci" fsverity --version\n" 92ced56a00Sopenharmony_ci"\n", fp); 93ced56a00Sopenharmony_ci show_all_hash_algs(fp); 94ced56a00Sopenharmony_ci} 95ced56a00Sopenharmony_ci 96ced56a00Sopenharmony_cistatic void usage_cmd(const struct fsverity_command *cmd, FILE *fp) 97ced56a00Sopenharmony_ci{ 98ced56a00Sopenharmony_ci fprintf(fp, "Usage:\n%s", cmd->usage_str); 99ced56a00Sopenharmony_ci} 100ced56a00Sopenharmony_ci 101ced56a00Sopenharmony_civoid usage(const struct fsverity_command *cmd, FILE *fp) 102ced56a00Sopenharmony_ci{ 103ced56a00Sopenharmony_ci if (cmd) 104ced56a00Sopenharmony_ci usage_cmd(cmd, fp); 105ced56a00Sopenharmony_ci else 106ced56a00Sopenharmony_ci usage_all(fp); 107ced56a00Sopenharmony_ci} 108ced56a00Sopenharmony_ci 109ced56a00Sopenharmony_cistatic void show_version(void) 110ced56a00Sopenharmony_ci{ 111ced56a00Sopenharmony_ci printf("fsverity v%d.%d\n", FSVERITY_UTILS_MAJOR_VERSION, 112ced56a00Sopenharmony_ci FSVERITY_UTILS_MINOR_VERSION); 113ced56a00Sopenharmony_ci} 114ced56a00Sopenharmony_ci 115ced56a00Sopenharmony_cistatic void handle_common_options(int argc, char *argv[], 116ced56a00Sopenharmony_ci const struct fsverity_command *cmd) 117ced56a00Sopenharmony_ci{ 118ced56a00Sopenharmony_ci int i; 119ced56a00Sopenharmony_ci 120ced56a00Sopenharmony_ci for (i = 1; i < argc; i++) { 121ced56a00Sopenharmony_ci const char *arg = argv[i]; 122ced56a00Sopenharmony_ci 123ced56a00Sopenharmony_ci if (*arg++ != '-') 124ced56a00Sopenharmony_ci continue; 125ced56a00Sopenharmony_ci if (*arg++ != '-') 126ced56a00Sopenharmony_ci continue; 127ced56a00Sopenharmony_ci if (!strcmp(arg, "help")) { 128ced56a00Sopenharmony_ci usage(cmd, stdout); 129ced56a00Sopenharmony_ci exit(0); 130ced56a00Sopenharmony_ci } else if (!strcmp(arg, "version")) { 131ced56a00Sopenharmony_ci show_version(); 132ced56a00Sopenharmony_ci exit(0); 133ced56a00Sopenharmony_ci } else if (!*arg) /* reached "--", no more options */ 134ced56a00Sopenharmony_ci return; 135ced56a00Sopenharmony_ci } 136ced56a00Sopenharmony_ci} 137ced56a00Sopenharmony_ci 138ced56a00Sopenharmony_cistatic const struct fsverity_command *find_command(const char *name) 139ced56a00Sopenharmony_ci{ 140ced56a00Sopenharmony_ci int i; 141ced56a00Sopenharmony_ci 142ced56a00Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(fsverity_commands); i++) 143ced56a00Sopenharmony_ci if (!strcmp(name, fsverity_commands[i].name)) 144ced56a00Sopenharmony_ci return &fsverity_commands[i]; 145ced56a00Sopenharmony_ci return NULL; 146ced56a00Sopenharmony_ci} 147ced56a00Sopenharmony_ci 148ced56a00Sopenharmony_cistatic bool parse_hash_alg_option(const char *arg, u32 *alg_ptr) 149ced56a00Sopenharmony_ci{ 150ced56a00Sopenharmony_ci char *end; 151ced56a00Sopenharmony_ci unsigned long n = strtoul(arg, &end, 10); 152ced56a00Sopenharmony_ci 153ced56a00Sopenharmony_ci if (*alg_ptr != 0) { 154ced56a00Sopenharmony_ci error_msg("--hash-alg can only be specified once"); 155ced56a00Sopenharmony_ci return false; 156ced56a00Sopenharmony_ci } 157ced56a00Sopenharmony_ci 158ced56a00Sopenharmony_ci /* Specified by number? */ 159ced56a00Sopenharmony_ci if (n > 0 && n < INT32_MAX && *end == '\0') { 160ced56a00Sopenharmony_ci *alg_ptr = n; 161ced56a00Sopenharmony_ci return true; 162ced56a00Sopenharmony_ci } 163ced56a00Sopenharmony_ci 164ced56a00Sopenharmony_ci /* Specified by name? */ 165ced56a00Sopenharmony_ci *alg_ptr = libfsverity_find_hash_alg_by_name(arg); 166ced56a00Sopenharmony_ci if (*alg_ptr) 167ced56a00Sopenharmony_ci return true; 168ced56a00Sopenharmony_ci error_msg("unknown hash algorithm: '%s'", arg); 169ced56a00Sopenharmony_ci show_all_hash_algs(stderr); 170ced56a00Sopenharmony_ci return false; 171ced56a00Sopenharmony_ci} 172ced56a00Sopenharmony_ci 173ced56a00Sopenharmony_cistatic bool parse_block_size_option(const char *arg, u32 *size_ptr) 174ced56a00Sopenharmony_ci{ 175ced56a00Sopenharmony_ci char *end; 176ced56a00Sopenharmony_ci unsigned long n = strtoul(arg, &end, 10); 177ced56a00Sopenharmony_ci 178ced56a00Sopenharmony_ci if (*size_ptr != 0) { 179ced56a00Sopenharmony_ci error_msg("--block-size can only be specified once"); 180ced56a00Sopenharmony_ci return false; 181ced56a00Sopenharmony_ci } 182ced56a00Sopenharmony_ci 183ced56a00Sopenharmony_ci if (n <= 0 || n >= INT_MAX || !is_power_of_2(n) || *end != '\0') { 184ced56a00Sopenharmony_ci error_msg("Invalid block size: %s. Must be power of 2", arg); 185ced56a00Sopenharmony_ci return false; 186ced56a00Sopenharmony_ci } 187ced56a00Sopenharmony_ci *size_ptr = n; 188ced56a00Sopenharmony_ci return true; 189ced56a00Sopenharmony_ci} 190ced56a00Sopenharmony_ci 191ced56a00Sopenharmony_cistatic bool parse_salt_option(const char *arg, u8 **salt_ptr, 192ced56a00Sopenharmony_ci u32 *salt_size_ptr) 193ced56a00Sopenharmony_ci{ 194ced56a00Sopenharmony_ci if (*salt_ptr != NULL) { 195ced56a00Sopenharmony_ci error_msg("--salt can only be specified once"); 196ced56a00Sopenharmony_ci return false; 197ced56a00Sopenharmony_ci } 198ced56a00Sopenharmony_ci *salt_size_ptr = strlen(arg) / 2; 199ced56a00Sopenharmony_ci *salt_ptr = xmalloc(*salt_size_ptr); 200ced56a00Sopenharmony_ci if (!hex2bin(arg, *salt_ptr, *salt_size_ptr)) { 201ced56a00Sopenharmony_ci error_msg("salt is not a valid hex string"); 202ced56a00Sopenharmony_ci return false; 203ced56a00Sopenharmony_ci } 204ced56a00Sopenharmony_ci return true; 205ced56a00Sopenharmony_ci} 206ced56a00Sopenharmony_ci 207ced56a00Sopenharmony_cistruct metadata_callback_ctx { 208ced56a00Sopenharmony_ci struct filedes merkle_tree_file; 209ced56a00Sopenharmony_ci struct filedes descriptor_file; 210ced56a00Sopenharmony_ci struct libfsverity_metadata_callbacks callbacks; 211ced56a00Sopenharmony_ci}; 212ced56a00Sopenharmony_ci 213ced56a00Sopenharmony_cistatic int handle_merkle_tree_size(void *_ctx, u64 size) 214ced56a00Sopenharmony_ci{ 215ced56a00Sopenharmony_ci struct metadata_callback_ctx *ctx = _ctx; 216ced56a00Sopenharmony_ci 217ced56a00Sopenharmony_ci if (!preallocate_file(&ctx->merkle_tree_file, size)) 218ced56a00Sopenharmony_ci return -EIO; 219ced56a00Sopenharmony_ci return 0; 220ced56a00Sopenharmony_ci} 221ced56a00Sopenharmony_ci 222ced56a00Sopenharmony_cistatic int handle_merkle_tree_block(void *_ctx, const void *block, size_t size, 223ced56a00Sopenharmony_ci u64 offset) 224ced56a00Sopenharmony_ci{ 225ced56a00Sopenharmony_ci struct metadata_callback_ctx *ctx = _ctx; 226ced56a00Sopenharmony_ci 227ced56a00Sopenharmony_ci if (!full_pwrite(&ctx->merkle_tree_file, block, size, offset)) 228ced56a00Sopenharmony_ci return -EIO; 229ced56a00Sopenharmony_ci return 0; 230ced56a00Sopenharmony_ci} 231ced56a00Sopenharmony_ci 232ced56a00Sopenharmony_cistatic int handle_descriptor(void *_ctx, const void *descriptor, size_t size) 233ced56a00Sopenharmony_ci{ 234ced56a00Sopenharmony_ci struct metadata_callback_ctx *ctx = _ctx; 235ced56a00Sopenharmony_ci 236ced56a00Sopenharmony_ci if (!full_write(&ctx->descriptor_file, descriptor, size)) 237ced56a00Sopenharmony_ci return -EIO; 238ced56a00Sopenharmony_ci return 0; 239ced56a00Sopenharmony_ci} 240ced56a00Sopenharmony_ci 241ced56a00Sopenharmony_cistatic bool parse_out_metadata_option(int opt_char, const char *arg, 242ced56a00Sopenharmony_ci const struct libfsverity_metadata_callbacks **cbs) 243ced56a00Sopenharmony_ci{ 244ced56a00Sopenharmony_ci struct metadata_callback_ctx *ctx; 245ced56a00Sopenharmony_ci struct filedes *file; 246ced56a00Sopenharmony_ci const char *opt_name; 247ced56a00Sopenharmony_ci 248ced56a00Sopenharmony_ci if (*cbs) { 249ced56a00Sopenharmony_ci ctx = (*cbs)->ctx; 250ced56a00Sopenharmony_ci } else { 251ced56a00Sopenharmony_ci ctx = xzalloc(sizeof(*ctx)); 252ced56a00Sopenharmony_ci ctx->merkle_tree_file.fd = -1; 253ced56a00Sopenharmony_ci ctx->descriptor_file.fd = -1; 254ced56a00Sopenharmony_ci ctx->callbacks.ctx = ctx; 255ced56a00Sopenharmony_ci *cbs = &ctx->callbacks; 256ced56a00Sopenharmony_ci } 257ced56a00Sopenharmony_ci 258ced56a00Sopenharmony_ci if (opt_char == OPT_OUT_MERKLE_TREE) { 259ced56a00Sopenharmony_ci file = &ctx->merkle_tree_file; 260ced56a00Sopenharmony_ci opt_name = "--out-merkle-tree"; 261ced56a00Sopenharmony_ci ctx->callbacks.merkle_tree_size = handle_merkle_tree_size; 262ced56a00Sopenharmony_ci ctx->callbacks.merkle_tree_block = handle_merkle_tree_block; 263ced56a00Sopenharmony_ci } else { 264ced56a00Sopenharmony_ci file = &ctx->descriptor_file; 265ced56a00Sopenharmony_ci opt_name = "--out-descriptor"; 266ced56a00Sopenharmony_ci ctx->callbacks.descriptor = handle_descriptor; 267ced56a00Sopenharmony_ci } 268ced56a00Sopenharmony_ci if (file->fd >= 0) { 269ced56a00Sopenharmony_ci error_msg("%s can only be specified once", opt_name); 270ced56a00Sopenharmony_ci return false; 271ced56a00Sopenharmony_ci } 272ced56a00Sopenharmony_ci return open_file(file, arg, O_WRONLY|O_CREAT|O_TRUNC, 0644); 273ced56a00Sopenharmony_ci} 274ced56a00Sopenharmony_ci 275ced56a00Sopenharmony_cibool parse_tree_param(int opt_char, const char *arg, 276ced56a00Sopenharmony_ci struct libfsverity_merkle_tree_params *params) 277ced56a00Sopenharmony_ci{ 278ced56a00Sopenharmony_ci switch (opt_char) { 279ced56a00Sopenharmony_ci case OPT_HASH_ALG: 280ced56a00Sopenharmony_ci return parse_hash_alg_option(arg, ¶ms->hash_algorithm); 281ced56a00Sopenharmony_ci case OPT_BLOCK_SIZE: 282ced56a00Sopenharmony_ci return parse_block_size_option(arg, ¶ms->block_size); 283ced56a00Sopenharmony_ci case OPT_SALT: 284ced56a00Sopenharmony_ci return parse_salt_option(arg, (u8 **)¶ms->salt, 285ced56a00Sopenharmony_ci ¶ms->salt_size); 286ced56a00Sopenharmony_ci case OPT_OUT_MERKLE_TREE: 287ced56a00Sopenharmony_ci case OPT_OUT_DESCRIPTOR: 288ced56a00Sopenharmony_ci return parse_out_metadata_option(opt_char, arg, 289ced56a00Sopenharmony_ci ¶ms->metadata_callbacks); 290ced56a00Sopenharmony_ci default: 291ced56a00Sopenharmony_ci ASSERT(0); 292ced56a00Sopenharmony_ci } 293ced56a00Sopenharmony_ci} 294ced56a00Sopenharmony_ci 295ced56a00Sopenharmony_cibool destroy_tree_params(struct libfsverity_merkle_tree_params *params) 296ced56a00Sopenharmony_ci{ 297ced56a00Sopenharmony_ci bool ok = true; 298ced56a00Sopenharmony_ci 299ced56a00Sopenharmony_ci free((u8 *)params->salt); 300ced56a00Sopenharmony_ci if (params->metadata_callbacks) { 301ced56a00Sopenharmony_ci struct metadata_callback_ctx *ctx = 302ced56a00Sopenharmony_ci params->metadata_callbacks->ctx; 303ced56a00Sopenharmony_ci 304ced56a00Sopenharmony_ci ok &= filedes_close(&ctx->merkle_tree_file); 305ced56a00Sopenharmony_ci ok &= filedes_close(&ctx->descriptor_file); 306ced56a00Sopenharmony_ci free(ctx); 307ced56a00Sopenharmony_ci } 308ced56a00Sopenharmony_ci memset(params, 0, sizeof(*params)); 309ced56a00Sopenharmony_ci return ok; 310ced56a00Sopenharmony_ci} 311ced56a00Sopenharmony_ci 312ced56a00Sopenharmony_ciint main(int argc, char *argv[]) 313ced56a00Sopenharmony_ci{ 314ced56a00Sopenharmony_ci const struct fsverity_command *cmd; 315ced56a00Sopenharmony_ci 316ced56a00Sopenharmony_ci install_libfsverity_error_handler(); 317ced56a00Sopenharmony_ci 318ced56a00Sopenharmony_ci if (argc < 2) { 319ced56a00Sopenharmony_ci error_msg("no command specified"); 320ced56a00Sopenharmony_ci usage_all(stderr); 321ced56a00Sopenharmony_ci return 2; 322ced56a00Sopenharmony_ci } 323ced56a00Sopenharmony_ci 324ced56a00Sopenharmony_ci cmd = find_command(argv[1]); 325ced56a00Sopenharmony_ci 326ced56a00Sopenharmony_ci handle_common_options(argc, argv, cmd); 327ced56a00Sopenharmony_ci 328ced56a00Sopenharmony_ci if (!cmd) { 329ced56a00Sopenharmony_ci error_msg("unrecognized command: '%s'", argv[1]); 330ced56a00Sopenharmony_ci usage_all(stderr); 331ced56a00Sopenharmony_ci return 2; 332ced56a00Sopenharmony_ci } 333ced56a00Sopenharmony_ci return cmd->func(cmd, argc - 1, argv + 1); 334ced56a00Sopenharmony_ci} 335