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