• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * The 'fsverity setup' command
4  *
5  * Copyright (C) 2018 Google LLC
6  *
7  * Written by Eric Biggers.
8  */
9 
10 #include <fcntl.h>
11 #include <getopt.h>
12 #include <stdlib.h>
13 #include <string.h>
14 #include <unistd.h>
15 
16 #include "commands.h"
17 #include "fsverity_uapi.h"
18 #include "fsveritysetup.h"
19 #include "hash_algs.h"
20 
21 enum {
22 	OPT_HASH,
23 	OPT_SALT,
24 	OPT_BLOCKSIZE,
25 	OPT_SIGNING_KEY,
26 	OPT_SIGNING_CERT,
27 	OPT_SIGNATURE,
28 	OPT_ELIDE,
29 	OPT_PATCH,
30 };
31 
32 static const struct option longopts[] = {
33 	{"hash",		required_argument, NULL, OPT_HASH},
34 	{"salt",		required_argument, NULL, OPT_SALT},
35 	{"blocksize",		required_argument, NULL, OPT_BLOCKSIZE},
36 	{"signing-key",		required_argument, NULL, OPT_SIGNING_KEY},
37 	{"signing-cert",	required_argument, NULL, OPT_SIGNING_CERT},
38 	{"signature",		required_argument, NULL, OPT_SIGNATURE},
39 	{"elide",		required_argument, NULL, OPT_ELIDE},
40 	{"patch",		required_argument, NULL, OPT_PATCH},
41 	{NULL, 0, NULL, 0}
42 };
43 
44 /* Parse the --blocksize=BLOCKSIZE option */
parse_blocksize_option(const char * opt,int * blocksize_ret)45 static bool parse_blocksize_option(const char *opt, int *blocksize_ret)
46 {
47 	char *end;
48 	unsigned long n = strtoul(opt, &end, 10);
49 
50 	if (n <= 0 || n >= INT32_MAX || *end || !is_power_of_2(n)) {
51 		error_msg("Invalid block size: %s.  Must be power of 2", opt);
52 		return false;
53 	}
54 	*blocksize_ret = n;
55 	return true;
56 }
57 
58 #define FS_VERITY_MAX_LEVELS	64
59 
60 /*
61  * Calculate the depth of the Merkle tree, then create a map from level to the
62  * block offset at which that level's hash blocks start.  Level 'depth - 1' is
63  * the root and is stored first in the file, in the first block following the
64  * original data.  Level 0 is the "leaf" level: it's directly "above" the data
65  * blocks and is stored last in the file.
66  */
compute_tree_layout(u64 data_size,u64 tree_offset,int blockbits,unsigned int hashes_per_block,u64 hash_lvl_region_idx[FS_VERITY_MAX_LEVELS],int * depth_ret,u64 * tree_end_ret)67 static void compute_tree_layout(u64 data_size, u64 tree_offset, int blockbits,
68 				unsigned int hashes_per_block,
69 				u64 hash_lvl_region_idx[FS_VERITY_MAX_LEVELS],
70 				int *depth_ret, u64 *tree_end_ret)
71 {
72 	u64 blocks = data_size >> blockbits;
73 	u64 offset = tree_offset >> blockbits;
74 	int depth = 0;
75 	int i;
76 
77 	ASSERT(data_size > 0);
78 	ASSERT(data_size % (1 << blockbits) == 0);
79 	ASSERT(tree_offset % (1 << blockbits) == 0);
80 	ASSERT(hashes_per_block >= 2);
81 
82 	while (blocks > 1) {
83 		ASSERT(depth < FS_VERITY_MAX_LEVELS);
84 		blocks = DIV_ROUND_UP(blocks, hashes_per_block);
85 		hash_lvl_region_idx[depth++] = blocks;
86 	}
87 	for (i = depth - 1; i >= 0; i--) {
88 		u64 next_count = hash_lvl_region_idx[i];
89 
90 		hash_lvl_region_idx[i] = offset;
91 		offset += next_count;
92 	}
93 	*depth_ret = depth;
94 	*tree_end_ret = offset << blockbits;
95 }
96 
97 /*
98  * Build a Merkle tree (hash tree) over the data of a file.
99  *
100  * @params: Block size, hashes per block, and salt
101  * @hash: Handle for the hash algorithm
102  * @data_file: input data file
103  * @data_size: size of data file in bytes; must be aligned to ->blocksize
104  * @tree_file: output tree file
105  * @tree_offset: byte offset in tree file at which to write the tree;
106  *		 must be aligned to ->blocksize
107  * @tree_end_ret: On success, the byte offset in the tree file of the end of the
108  *		  tree is written here
109  * @root_hash_ret: On success, the Merkle tree root hash is written here
110  *
111  * Return: exit status code (0 on success, nonzero on failure)
112  */
build_merkle_tree(const struct fsveritysetup_params * params,struct hash_ctx * hash,struct filedes * data_file,u64 data_size,struct filedes * tree_file,u64 tree_offset,u64 * tree_end_ret,u8 * root_hash_ret)113 static int build_merkle_tree(const struct fsveritysetup_params *params,
114 			     struct hash_ctx *hash,
115 			     struct filedes *data_file, u64 data_size,
116 			     struct filedes *tree_file, u64 tree_offset,
117 			     u64 *tree_end_ret, u8 *root_hash_ret)
118 {
119 	const unsigned int digest_size = hash->alg->digest_size;
120 	int depth;
121 	u64 hash_lvl_region_idx[FS_VERITY_MAX_LEVELS];
122 	u8 *data_to_hash = NULL;
123 	u8 *pending_hashes = NULL;
124 	unsigned int pending_hash_bytes;
125 	u64 nr_hashes_at_this_lvl;
126 	int lvl;
127 	int status;
128 
129 	compute_tree_layout(data_size, tree_offset, params->blockbits,
130 			    params->hashes_per_block, hash_lvl_region_idx,
131 			    &depth, tree_end_ret);
132 
133 	/* Allocate block buffers */
134 	data_to_hash = xmalloc(params->blocksize);
135 	pending_hashes = xmalloc(params->blocksize);
136 	pending_hash_bytes = 0;
137 	nr_hashes_at_this_lvl = data_size >> params->blockbits;
138 
139 	/*
140 	 * Generate each level of the Merkle tree, starting at the leaf level
141 	 * ('lvl == 0') and ascending to the root node ('lvl == depth - 1').
142 	 * Then at the end ('lvl == depth'), calculate the root node's hash.
143 	 */
144 	for (lvl = 0; lvl <= depth; lvl++) {
145 		u64 i;
146 
147 		for (i = 0; i < nr_hashes_at_this_lvl; i++) {
148 			struct filedes *file;
149 			u64 blk_idx;
150 
151 			hash_init(hash);
152 			hash_update(hash, params->salt, params->saltlen);
153 
154 			if (lvl == 0) {
155 				/* Leaf: hashing a data block */
156 				file = data_file;
157 				blk_idx = i;
158 			} else {
159 				/* Non-leaf: hashing a hash block */
160 				file = tree_file;
161 				blk_idx = hash_lvl_region_idx[lvl - 1] + i;
162 			}
163 			if (!full_pread(file, data_to_hash, params->blocksize,
164 					blk_idx << params->blockbits))
165 				goto out_err;
166 			hash_update(hash, data_to_hash, params->blocksize);
167 
168 			hash_final(hash, &pending_hashes[pending_hash_bytes]);
169 			pending_hash_bytes += digest_size;
170 
171 			if (lvl == depth) {
172 				/* Root hash */
173 				ASSERT(nr_hashes_at_this_lvl == 1);
174 				ASSERT(pending_hash_bytes == digest_size);
175 				memcpy(root_hash_ret, pending_hashes,
176 				       digest_size);
177 				status = 0;
178 				goto out;
179 			}
180 
181 			if (pending_hash_bytes + digest_size > params->blocksize
182 			    || i + 1 == nr_hashes_at_this_lvl) {
183 				/* Flush the pending hash block */
184 				memset(&pending_hashes[pending_hash_bytes], 0,
185 				       params->blocksize - pending_hash_bytes);
186 				blk_idx = hash_lvl_region_idx[lvl] +
187 					  (i / params->hashes_per_block);
188 				if (!full_pwrite(tree_file,
189 						 pending_hashes,
190 						 params->blocksize,
191 						 blk_idx << params->blockbits))
192 					goto out_err;
193 				pending_hash_bytes = 0;
194 			}
195 		}
196 
197 		nr_hashes_at_this_lvl = DIV_ROUND_UP(nr_hashes_at_this_lvl,
198 						     params->hashes_per_block);
199 	}
200 	ASSERT(0); /* unreachable; should exit via "Root hash" case above */
201 out_err:
202 	status = 1;
203 out:
204 	free(data_to_hash);
205 	free(pending_hashes);
206 	return status;
207 }
208 
209 /*
210  * Append to the buffer @*buf_p an extension (variable-length metadata) item of
211  * type @type, containing the data @ext of length @extlen bytes.
212  */
fsverity_append_extension(void ** buf_p,int type,const void * ext,size_t extlen)213 void fsverity_append_extension(void **buf_p, int type,
214 			       const void *ext, size_t extlen)
215 {
216 	void *buf = *buf_p;
217 	struct fsverity_extension *hdr = buf;
218 
219 	hdr->type = cpu_to_le16(type);
220 	hdr->length = cpu_to_le32(sizeof(*hdr) + extlen);
221 	hdr->reserved = 0;
222 	buf += sizeof(*hdr);
223 	memcpy(buf, ext, extlen);
224 	buf += extlen;
225 	memset(buf, 0, -extlen & 7);
226 	buf += -extlen & 7;
227 	ASSERT(buf - *buf_p == FSVERITY_EXTLEN(extlen));
228 	*buf_p = buf;
229 }
230 
231 /*
232  * Append the authenticated portion of the fs-verity descriptor to 'out', in the
233  * process updating 'hash' with the data written.
234  */
append_fsverity_descriptor(const struct fsveritysetup_params * params,u64 filesize,const u8 * root_hash,struct filedes * out,struct hash_ctx * hash)235 static int append_fsverity_descriptor(const struct fsveritysetup_params *params,
236 				      u64 filesize, const u8 *root_hash,
237 				      struct filedes *out,
238 				      struct hash_ctx *hash)
239 {
240 	size_t desc_auth_len;
241 	void *buf;
242 	struct fsverity_descriptor *desc;
243 	u16 auth_ext_count;
244 	int status;
245 
246 	desc_auth_len = sizeof(*desc);
247 	desc_auth_len += FSVERITY_EXTLEN(params->hash_alg->digest_size);
248 	if (params->saltlen)
249 		desc_auth_len += FSVERITY_EXTLEN(params->saltlen);
250 	desc_auth_len += total_elide_patch_ext_length(params);
251 	desc = buf = xzalloc(desc_auth_len);
252 
253 	memcpy(desc->magic, FS_VERITY_MAGIC, sizeof(desc->magic));
254 	desc->major_version = 1;
255 	desc->minor_version = 0;
256 	desc->log_data_blocksize = params->blockbits;
257 	desc->log_tree_blocksize = params->blockbits;
258 	desc->data_algorithm = cpu_to_le16(params->hash_alg -
259 					   fsverity_hash_algs);
260 	desc->tree_algorithm = desc->data_algorithm;
261 	desc->orig_file_size = cpu_to_le64(filesize);
262 
263 	auth_ext_count = 1; /* root hash */
264 	if (params->saltlen)
265 		auth_ext_count++;
266 	auth_ext_count += params->num_elisions_and_patches;
267 	desc->auth_ext_count = cpu_to_le16(auth_ext_count);
268 
269 	buf += sizeof(*desc);
270 	fsverity_append_extension(&buf, FS_VERITY_EXT_ROOT_HASH,
271 				  root_hash, params->hash_alg->digest_size);
272 	if (params->saltlen)
273 		fsverity_append_extension(&buf, FS_VERITY_EXT_SALT,
274 					  params->salt, params->saltlen);
275 	append_elide_patch_exts(&buf, params);
276 	ASSERT(buf - (void *)desc == desc_auth_len);
277 
278 	hash_update(hash, desc, desc_auth_len);
279 	if (!full_write(out, desc, desc_auth_len))
280 		goto out_err;
281 	status = 0;
282 out:
283 	free(desc);
284 	return status;
285 
286 out_err:
287 	status = 1;
288 	goto out;
289 }
290 
291 /*
292  * Append any needed unauthenticated extension items: currently, just possibly a
293  * PKCS7_SIGNATURE item containing the signed file measurement.
294  */
295 static int
append_unauthenticated_extensions(struct filedes * out,const struct fsveritysetup_params * params,const u8 * measurement)296 append_unauthenticated_extensions(struct filedes *out,
297 				  const struct fsveritysetup_params *params,
298 				  const u8 *measurement)
299 {
300 	u16 unauth_ext_count = 0;
301 	struct {
302 		__le16 unauth_ext_count;
303 		__le16 pad[3];
304 	} hdr;
305 	bool have_sig = params->signing_key_file || params->signature_file;
306 
307 	if (have_sig)
308 		unauth_ext_count++;
309 
310 	ASSERT(sizeof(hdr) % 8 == 0);
311 	memset(&hdr, 0, sizeof(hdr));
312 	hdr.unauth_ext_count = cpu_to_le16(unauth_ext_count);
313 
314 	if (!full_write(out, &hdr, sizeof(hdr)))
315 		return 1;
316 
317 	if (have_sig)
318 		return append_signed_measurement(out, params, measurement);
319 
320 	return 0;
321 }
322 
append_footer(struct filedes * out,u64 desc_offset)323 static int append_footer(struct filedes *out, u64 desc_offset)
324 {
325 	struct fsverity_footer ftr;
326 	u32 offset = (out->pos + sizeof(ftr)) - desc_offset;
327 
328 	ftr.desc_reverse_offset = cpu_to_le32(offset);
329 	memcpy(ftr.magic, FS_VERITY_MAGIC, sizeof(ftr.magic));
330 
331 	if (!full_write(out, &ftr, sizeof(ftr)))
332 		return 1;
333 	return 0;
334 }
335 
fsveritysetup(const char * infile,const char * outfile,const struct fsveritysetup_params * params)336 static int fsveritysetup(const char *infile, const char *outfile,
337 			 const struct fsveritysetup_params *params)
338 {
339 	struct filedes _in = { .fd = -1 };
340 	struct filedes _out = { .fd = -1 };
341 	struct filedes _tmp = { .fd = -1 };
342 	struct hash_ctx *hash = NULL;
343 	struct filedes *in = &_in, *out = &_out, *src;
344 	u64 filesize;
345 	u64 aligned_filesize;
346 	u64 src_filesize;
347 	u64 tree_end_offset;
348 	u8 root_hash[FS_VERITY_MAX_DIGEST_SIZE];
349 	u8 measurement[FS_VERITY_MAX_DIGEST_SIZE];
350 	char hash_hex[FS_VERITY_MAX_DIGEST_SIZE * 2 + 1];
351 	int status;
352 
353 	if (!open_file(in, infile, (infile == outfile ? O_RDWR : O_RDONLY), 0))
354 		goto out_err;
355 
356 	if (!get_file_size(in, &filesize))
357 		goto out_err;
358 
359 	if (filesize <= 0) {
360 		error_msg("input file is empty: '%s'", infile);
361 		goto out_err;
362 	}
363 
364 	if (infile == outfile) {
365 		/*
366 		 * Invoked with one file argument: we're appending verity
367 		 * metadata to an existing file.
368 		 */
369 		out = in;
370 		if (!filedes_seek(out, filesize, SEEK_SET))
371 			goto out_err;
372 	} else {
373 		/*
374 		 * Invoked with two file arguments: we're copying the first file
375 		 * to the second file, then appending verity metadata to it.
376 		 */
377 		if (!open_file(out, outfile, O_RDWR|O_CREAT|O_TRUNC, 0644))
378 			goto out_err;
379 		if (!copy_file_data(in, out, filesize))
380 			goto out_err;
381 	}
382 
383 	/* Zero-pad the output file to the next block boundary */
384 	aligned_filesize = ALIGN(filesize, params->blocksize);
385 	if (!write_zeroes(out, aligned_filesize - filesize))
386 		goto out_err;
387 
388 	if (params->num_elisions_and_patches) {
389 		/* Merkle tree is built over temporary elided/patched file */
390 		src = &_tmp;
391 		if (!apply_elisions_and_patches(params, in, filesize,
392 						src, &src_filesize))
393 			goto out_err;
394 	} else {
395 		/* Merkle tree is built over original file */
396 		src = out;
397 		src_filesize = aligned_filesize;
398 	}
399 
400 	hash = hash_create(params->hash_alg);
401 
402 	/* Build the file's Merkle tree and calculate its root hash */
403 	status = build_merkle_tree(params, hash, src, src_filesize,
404 				   out, aligned_filesize,
405 				   &tree_end_offset, root_hash);
406 	if (status)
407 		goto out;
408 	if (!filedes_seek(out, tree_end_offset, SEEK_SET))
409 		goto out_err;
410 
411 	/* Append the additional needed metadata */
412 
413 	hash_init(hash);
414 	status = append_fsverity_descriptor(params, filesize, root_hash,
415 					    out, hash);
416 	if (status)
417 		goto out;
418 	hash_final(hash, measurement);
419 
420 	status = append_unauthenticated_extensions(out, params, measurement);
421 	if (status)
422 		goto out;
423 
424 	status = append_footer(out, tree_end_offset);
425 	if (status)
426 		goto out;
427 
428 	bin2hex(measurement, params->hash_alg->digest_size, hash_hex);
429 	printf("File measurement: %s:%s\n", params->hash_alg->name, hash_hex);
430 	status = 0;
431 out:
432 	hash_free(hash);
433 	if (status != 0 && out->fd >= 0) {
434 		/* Error occurred; undo what we wrote */
435 		if (in == out)
436 			(void)ftruncate(out->fd, filesize);
437 		else
438 			out->autodelete = true;
439 	}
440 	filedes_close(&_in);
441 	filedes_close(&_tmp);
442 	if (!filedes_close(&_out) && status == 0)
443 		status = 1;
444 	return status;
445 
446 out_err:
447 	status = 1;
448 	goto out;
449 }
450 
fsverity_cmd_setup(const struct fsverity_command * cmd,int argc,char * argv[])451 int fsverity_cmd_setup(const struct fsverity_command *cmd,
452 		       int argc, char *argv[])
453 {
454 	struct fsveritysetup_params params = {
455 		.hash_alg = DEFAULT_HASH_ALG,
456 	};
457 	STRING_LIST(elide_opts);
458 	STRING_LIST(patch_opts);
459 	int c;
460 	int status;
461 
462 	while ((c = getopt_long(argc, argv, "", longopts, NULL)) != -1) {
463 		switch (c) {
464 		case OPT_HASH:
465 			params.hash_alg = find_hash_alg_by_name(optarg);
466 			if (!params.hash_alg)
467 				goto out_usage;
468 			break;
469 		case OPT_SALT:
470 			if (params.salt) {
471 				error_msg("--salt can only be specified once");
472 				goto out_usage;
473 			}
474 			params.saltlen = strlen(optarg) / 2;
475 			params.salt = xmalloc(params.saltlen);
476 			if (!hex2bin(optarg, params.salt, params.saltlen)) {
477 				error_msg("salt is not a valid hex string");
478 				goto out_usage;
479 			}
480 			break;
481 		case OPT_BLOCKSIZE:
482 			if (!parse_blocksize_option(optarg, &params.blocksize))
483 				goto out_usage;
484 			break;
485 		case OPT_SIGNING_KEY:
486 			params.signing_key_file = optarg;
487 			break;
488 		case OPT_SIGNING_CERT:
489 			params.signing_cert_file = optarg;
490 			break;
491 		case OPT_SIGNATURE:
492 			params.signature_file = optarg;
493 			break;
494 		case OPT_ELIDE:
495 			string_list_append(&elide_opts, optarg);
496 			break;
497 		case OPT_PATCH:
498 			string_list_append(&patch_opts, optarg);
499 			break;
500 		default:
501 			goto out_usage;
502 		}
503 	}
504 
505 	argv += optind;
506 	argc -= optind;
507 
508 	if (argc != 1 && argc != 2)
509 		goto out_usage;
510 
511 	ASSERT(params.hash_alg->digest_size <= FS_VERITY_MAX_DIGEST_SIZE);
512 
513 	if (params.blocksize == 0) {
514 		params.blocksize = sysconf(_SC_PAGESIZE);
515 		if (params.blocksize <= 0 || !is_power_of_2(params.blocksize)) {
516 			fprintf(stderr,
517 				"Warning: invalid _SC_PAGESIZE (%d).  Assuming 4K blocks.\n",
518 				params.blocksize);
519 			params.blocksize = 4096;
520 		}
521 	}
522 	params.blockbits = ilog2(params.blocksize);
523 
524 	params.hashes_per_block = params.blocksize /
525 				  params.hash_alg->digest_size;
526 	if (params.hashes_per_block < 2) {
527 		error_msg("block size of %d bytes is too small for %s hash",
528 			  params.blocksize, params.hash_alg->name);
529 		goto out_err;
530 	}
531 
532 	if (params.signing_cert_file && !params.signing_key_file) {
533 		error_msg("--signing-cert was given, but --signing-key was not.\n"
534 "       You must provide the certificate's private key file using --signing-key.");
535 		goto out_err;
536 	}
537 
538 	if ((params.signing_key_file || params.signature_file) &&
539 	    !params.hash_alg->cryptographic) {
540 		error_msg("Signing a file using '%s' checksums does not make sense\n"
541 			  "       because '%s' is not a cryptographically secure hash algorithm.",
542 			  params.hash_alg->name, params.hash_alg->name);
543 		goto out_err;
544 	}
545 
546 	if (!load_elisions_and_patches(&elide_opts, &patch_opts, &params))
547 		goto out_err;
548 
549 	status = fsveritysetup(argv[0], argv[argc - 1], &params);
550 out:
551 	free(params.salt);
552 	free_elisions_and_patches(&params);
553 	string_list_destroy(&elide_opts);
554 	string_list_destroy(&patch_opts);
555 	return status;
556 
557 out_err:
558 	status = 1;
559 	goto out;
560 
561 out_usage:
562 	usage(cmd, stderr);
563 	status = 2;
564 	goto out;
565 }
566