xref: /aosp_15_r20/external/fsverity-utils/programs/fsverity.c (revision b13c0e4024008a1f948ee8189745cb3371f4ac04)
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, &params->hash_algorithm);
281*b13c0e40SEric Biggers 	case OPT_BLOCK_SIZE:
282*b13c0e40SEric Biggers 		return parse_block_size_option(arg, &params->block_size);
283*b13c0e40SEric Biggers 	case OPT_SALT:
284*b13c0e40SEric Biggers 		return parse_salt_option(arg, (u8 **)&params->salt,
285*b13c0e40SEric Biggers 					 &params->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 						 &params->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