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