xref: /aosp_15_r20/external/vboot_reference/futility/cmd_vbutil_kernel.c (revision 8617a60d3594060b7ecbd21bc622a7c14f3cf2bc)
1 /* Copyright 2014 The ChromiumOS Authors
2  * Use of this source code is governed by a BSD-style license that can be
3  * found in the LICENSE file.
4  *
5  * Verified boot kernel utility
6  */
7 
8 #include <errno.h>
9 #include <fcntl.h>
10 #include <getopt.h>
11 #include <inttypes.h>		/* For PRIu64 */
12 #if !defined(HAVE_MACOS) && !defined(__FreeBSD__) && !defined(__OpenBSD__)
13 #include <linux/fs.h>		/* For BLKGETSIZE64 */
14 #endif
15 #include <stdarg.h>
16 #include <stdio.h>
17 #include <string.h>
18 #include <sys/ioctl.h>
19 #include <sys/stat.h>
20 #include <unistd.h>
21 
22 #include "2common.h"
23 #include "2sysincludes.h"
24 #include "file_type.h"
25 #include "futility.h"
26 #include "host_common.h"
27 #include "kernel_blob.h"
28 #include "vb1_helper.h"
29 
30 /* Global opts */
31 static int opt_verbose;
32 static int opt_vblockonly;
33 static uint64_t opt_pad = 65536;
34 
35 /* Command line options */
36 enum {
37 	OPT_MODE_PACK = 1000,
38 	OPT_MODE_REPACK,
39 	OPT_MODE_VERIFY,
40 	OPT_MODE_GET_VMLINUZ,
41 	OPT_ARCH,
42 	OPT_OLDBLOB,
43 	OPT_KLOADADDR,
44 	OPT_KEYBLOCK,
45 	OPT_SIGNPUBKEY,
46 	OPT_SIGNPRIVATE,
47 	OPT_VERSION,
48 	OPT_VMLINUZ,
49 	OPT_BOOTLOADER,
50 	OPT_CONFIG,
51 	OPT_VBLOCKONLY,
52 	OPT_PAD,
53 	OPT_VERBOSE,
54 	OPT_MINVERSION,
55 	OPT_VMLINUZ_OUT,
56 	OPT_FLAGS,
57 	OPT_HELP,
58 };
59 
60 static const struct option long_opts[] = {
61 	{"pack", 1, 0, OPT_MODE_PACK},
62 	{"repack", 1, 0, OPT_MODE_REPACK},
63 	{"verify", 1, 0, OPT_MODE_VERIFY},
64 	{"get-vmlinuz", 1, 0, OPT_MODE_GET_VMLINUZ},
65 	{"arch", 1, 0, OPT_ARCH},
66 	{"oldblob", 1, 0, OPT_OLDBLOB},
67 	{"kloadaddr", 1, 0, OPT_KLOADADDR},
68 	{"keyblock", 1, 0, OPT_KEYBLOCK},
69 	{"signpubkey", 1, 0, OPT_SIGNPUBKEY},
70 	{"signprivate", 1, 0, OPT_SIGNPRIVATE},
71 	{"version", 1, 0, OPT_VERSION},
72 	{"minversion", 1, 0, OPT_MINVERSION},
73 	{"vmlinuz", 1, 0, OPT_VMLINUZ},
74 	{"bootloader", 1, 0, OPT_BOOTLOADER},
75 	{"config", 1, 0, OPT_CONFIG},
76 	{"vblockonly", 0, 0, OPT_VBLOCKONLY},
77 	{"pad", 1, 0, OPT_PAD},
78 	{"verbose", 0, &opt_verbose, 1},
79 	{"vmlinuz-out", 1, 0, OPT_VMLINUZ_OUT},
80 	{"flags", 1, 0, OPT_FLAGS},
81 	{"help", 0, 0, OPT_HELP},
82 	{NULL, 0, 0, 0}
83 };
84 
85 
86 
87 static const char usage[] =
88 	"\n"
89 	"Usage:  " MYNAME " %s --pack <file> [PARAMETERS]\n"
90 	"\n"
91 	"  Required parameters:\n"
92 	"    --keyblock <file>         Keyblock in .keyblock format\n"
93 	"    --signprivate <file>      Private key to sign kernel data,\n"
94 	"                                in .vbprivk format\n"
95 	"    --version <number>        Kernel version\n"
96 	"    --vmlinuz <file>          Linux kernel bzImage file\n"
97 	"    --config <file>           Command line file\n"
98 	"    --arch <arch>             Cpu architecture (default x86)\n"
99 	"\n"
100 	"  Optional:\n"
101 	"    --kloadaddr <address>     Assign kernel body load address\n"
102 	"    --bootloader <file>       Bootloader stub\n"
103 	"    --pad <number>            Verification padding size in bytes\n"
104 	"    --vblockonly              Emit just the verification blob\n"
105 	"    --flags NUM               Flags to be passed in the header\n"
106 	"\nOR\n\n"
107 	"Usage:  " MYNAME " %s --repack <file> [PARAMETERS]\n"
108 	"\n"
109 	"  Required parameters:\n"
110 	"    --signprivate <file>      Private key to sign kernel data,\n"
111 	"                                in .vbprivk format\n"
112 	"    --oldblob <file>          Previously packed kernel blob\n"
113 	"                                (including verfication blob)\n"
114 	"\n"
115 	"  Optional:\n"
116 	"    --keyblock <file>         Keyblock in .keyblock format\n"
117 	"    --config <file>           New command line file\n"
118 	"    --version <number>        Kernel version\n"
119 	"    --kloadaddr <address>     Assign kernel body load address\n"
120 	"    --pad <number>            Verification blob size in bytes\n"
121 	"    --vblockonly              Emit just the verification blob\n"
122 	"\nOR\n\n"
123 	"Usage:  " MYNAME " %s --verify <file> [PARAMETERS]\n"
124 	"\n"
125 	"  Optional:\n"
126 	"    --signpubkey <file>"
127 	"       Public key to verify kernel keyblock,\n"
128 	"                                in .vbpubk format\n"
129 	"    --verbose                 Print a more detailed report\n"
130 	"    --keyblock <file>         Outputs the verified keyblock,\n"
131 	"                                in .keyblock format\n"
132 	"    --pad <number>            Verification padding size in bytes\n"
133 	"    --minversion <number>     Minimum combined kernel key version\n"
134 	"\nOR\n\n"
135 	"Usage:  " MYNAME " %s --get-vmlinuz <file> [PARAMETERS]\n"
136 	"\n"
137 	"  Required parameters:\n"
138 	"    --vmlinuz-out <file>      vmlinuz image output file\n"
139 	"\n";
140 
141 
142 /* Print help and return error */
print_help(int argc,char * argv[])143 static void print_help(int argc, char *argv[])
144 {
145 	printf(usage, argv[0], argv[0], argv[0], argv[0]);
146 }
147 
148 
149 /* Return an explanation when fread() fails. */
error_fread(FILE * fp)150 static const char *error_fread(FILE *fp)
151 {
152 	const char *retval = "beats me why";
153 	if (feof(fp))
154 		retval = "EOF";
155 	else if (ferror(fp))
156 		retval = strerror(errno);
157 	clearerr(fp);
158 	return retval;
159 }
160 
161 
162 /* This reads a complete kernel partition into a buffer */
ReadOldKPartFromFileOrDie(const char * filename,uint32_t * size_ptr)163 static uint8_t *ReadOldKPartFromFileOrDie(const char *filename,
164 					 uint32_t *size_ptr)
165 {
166 	FILE *fp = NULL;
167 	struct stat statbuf;
168 	uint8_t *buf;
169 	uint32_t file_size = 0;
170 
171 	if (stat(filename, &statbuf))
172 		FATAL("Unable to stat %s: %s\n", filename, strerror(errno));
173 
174 	if (S_ISBLK(statbuf.st_mode)) {
175 #if !defined(HAVE_MACOS) && !defined(__FreeBSD__) && !defined(__OpenBSD__)
176 		int fd = open(filename, O_RDONLY);
177 		if (fd >= 0) {
178 			ioctl(fd, BLKGETSIZE64, &file_size);
179 			close(fd);
180 		}
181 #endif
182 	} else {
183 		file_size = statbuf.st_size;
184 	}
185 	VB2_DEBUG("%s size is %#x\n", filename, file_size);
186 	if (file_size < opt_pad)
187 		FATAL("%s is too small to be a valid kernel blob\n", filename);
188 
189 	VB2_DEBUG("Reading %s\n", filename);
190 	fp = fopen(filename, "rb");
191 	if (!fp)
192 		FATAL("Unable to open file %s: %s\n", filename,
193 		      strerror(errno));
194 
195 	buf = malloc(file_size);
196 	if (1 != fread(buf, file_size, 1, fp))
197 		FATAL("Unable to read entirety of %s: %s\n", filename,
198 		      error_fread(fp));
199 
200 	if (size_ptr)
201 		*size_ptr = file_size;
202 
203 	fclose(fp);
204 	return buf;
205 }
206 
207 /****************************************************************************/
208 
do_vbutil_kernel(int argc,char * argv[])209 static int do_vbutil_kernel(int argc, char *argv[])
210 {
211 	char *filename = NULL;
212 	char *oldfile = NULL;
213 	char *keyblock_file = NULL;
214 	char *signpubkey_file = NULL;
215 	char *signprivkey_info = NULL;
216 	char *version_str = NULL;
217 	int version = -1;
218 	char *vmlinuz_file = NULL;
219 	char *bootloader_file = NULL;
220 	char *config_file = NULL;
221 	char *vmlinuz_out_file = NULL;
222 	enum arch_t arch = ARCH_X86;
223 	uint64_t kernel_body_load_address = CROS_32BIT_ENTRY_ADDR;
224 	int mode = 0;
225 	int parse_error = 0;
226 	uint32_t min_version = 0;
227 	char *e;
228 	int i = 0;
229 	int errcount = 0;
230 	int rv;
231 	struct vb2_keyblock *keyblock = NULL;
232 	struct vb2_keyblock *t_keyblock = NULL;
233 	struct vb2_private_key *signpriv_key = NULL;
234 	struct vb2_packed_key *signpub_key = NULL;
235 	uint8_t *kpart_data = NULL;
236 	uint32_t kpart_size = 0;
237 	uint8_t *vmlinuz_buf = NULL;
238 	uint32_t vmlinuz_size = 0;
239 	uint8_t *t_config_data;
240 	uint32_t t_config_size;
241 	uint8_t *t_bootloader_data;
242 	uint32_t t_bootloader_size;
243 	uint32_t vmlinuz_header_size = 0;
244 	uint64_t vmlinuz_header_address = 0;
245 	uint32_t vmlinuz_header_offset = 0;
246 	struct vb2_kernel_preamble *preamble = NULL;
247 	uint8_t *kblob_data = NULL;
248 	uint32_t kblob_size = 0;
249 	uint8_t *vblock_data = NULL;
250 	uint32_t vblock_size = 0;
251 	uint32_t flags = 0;
252 	FILE *f;
253 
254 	while (((i = getopt_long(argc, argv, ":", long_opts, NULL)) != -1) &&
255 	       !parse_error) {
256 		switch (i) {
257 		default:
258 		case '?':
259 			/* Unhandled option */
260 			parse_error = 1;
261 			break;
262 
263 		case 0:
264 			/* silently handled option */
265 			break;
266 		case OPT_HELP:
267 			print_help(argc, argv);
268 			return !!parse_error;
269 
270 		case OPT_MODE_PACK:
271 		case OPT_MODE_REPACK:
272 		case OPT_MODE_VERIFY:
273 		case OPT_MODE_GET_VMLINUZ:
274 			if (mode && (mode != i)) {
275 				ERROR("Only one mode can be specified\n");
276 				parse_error = 1;
277 				break;
278 			}
279 			mode = i;
280 			filename = optarg;
281 			break;
282 
283 		case OPT_ARCH:
284 			/* check the first 3 characters to also detect x86_64 */
285 			if ((!strncasecmp(optarg, "x86", 3)) ||
286 			    (!strcasecmp(optarg, "amd64")))
287 				arch = ARCH_X86;
288 			/* check the first 3 characters to also detect arm64 */
289 			else if ((!strncasecmp(optarg, "arm", 3)) ||
290 				 (!strcasecmp(optarg, "aarch64")))
291 				arch = ARCH_ARM;
292 			else if (!strcasecmp(optarg, "mips"))
293 				arch = ARCH_MIPS;
294 			else {
295 				ERROR("Unknown architecture string: %s\n", optarg);
296 				parse_error = 1;
297 			}
298 			break;
299 
300 		case OPT_OLDBLOB:
301 			oldfile = optarg;
302 			break;
303 
304 		case OPT_KLOADADDR:
305 			kernel_body_load_address = strtoul(optarg, &e, 0);
306 			if (!*optarg || (e && *e)) {
307 				ERROR("Invalid --kloadaddr\n");
308 				parse_error = 1;
309 			}
310 			break;
311 
312 		case OPT_KEYBLOCK:
313 			keyblock_file = optarg;
314 			break;
315 
316 		case OPT_SIGNPUBKEY:
317 			signpubkey_file = optarg;
318 			break;
319 
320 		case OPT_SIGNPRIVATE:
321 			signprivkey_info = optarg;
322 			break;
323 
324 		case OPT_VMLINUZ:
325 			vmlinuz_file = optarg;
326 			break;
327 
328 		case OPT_FLAGS:
329 			flags = (uint32_t)strtoul(optarg, &e, 0);
330 			if (!*optarg || (e && *e)) {
331 				ERROR("Invalid --flags\n");
332 				parse_error = 1;
333 			}
334 			break;
335 
336 		case OPT_BOOTLOADER:
337 			bootloader_file = optarg;
338 			break;
339 
340 		case OPT_CONFIG:
341 			config_file = optarg;
342 			break;
343 
344 		case OPT_VBLOCKONLY:
345 			opt_vblockonly = 1;
346 			break;
347 
348 		case OPT_VERSION:
349 			version_str = optarg;
350 			version = strtoul(optarg, &e, 0);
351 			if (!*optarg || (e && *e)) {
352 				ERROR("Invalid --version\n");
353 				parse_error = 1;
354 			}
355 			break;
356 
357 		case OPT_MINVERSION:
358 			min_version = strtoul(optarg, &e, 0);
359 			if (!*optarg || (e && *e)) {
360 				ERROR("Invalid --minversion\n");
361 				parse_error = 1;
362 			}
363 			break;
364 
365 		case OPT_PAD:
366 			opt_pad = strtoul(optarg, &e, 0);
367 			if (!*optarg || (e && *e)) {
368 				ERROR("Invalid --pad\n");
369 				parse_error = 1;
370 			}
371 			break;
372 		case OPT_VMLINUZ_OUT:
373 			vmlinuz_out_file = optarg;
374 		}
375 	}
376 
377 	if (parse_error) {
378 		print_help(argc, argv);
379 		return 1;
380 	}
381 
382 	switch (mode) {
383 	case OPT_MODE_PACK:
384 
385 		if (!keyblock_file)
386 			FATAL("Missing required keyblock file.\n");
387 
388 		t_keyblock = (struct vb2_keyblock *)ReadFile(keyblock_file, 0);
389 		if (!t_keyblock)
390 			FATAL("Error reading keyblock.\n");
391 
392 		if (!signprivkey_info)
393 			FATAL("Missing required signprivate info.\n");
394 
395 		signpriv_key = vb2_read_private_key(signprivkey_info);
396 		if (!signpriv_key)
397 			FATAL("Error reading signing key.\n");
398 
399 		if (!config_file)
400 			FATAL("Missing required config file.\n");
401 
402 		VB2_DEBUG("Reading %s\n", config_file);
403 		t_config_data =
404 			ReadConfigFile(config_file, &t_config_size);
405 		if (!t_config_data)
406 			FATAL("Error reading config file.\n");
407 
408 		if (bootloader_file) {
409 			VB2_DEBUG("Reading %s\n", bootloader_file);
410 			if (VB2_SUCCESS != vb2_read_file(bootloader_file,
411 							 &t_bootloader_data,
412 							 &t_bootloader_size))
413 				FATAL("Error reading bootloader file.\n");
414 			VB2_DEBUG(" bootloader file size=%#x\n", t_bootloader_size);
415 		} else {
416 			t_bootloader_data = NULL;
417 			t_bootloader_size = 0;
418 			VB2_DEBUG("No external bootloader file passed in.\n");
419 		}
420 
421 		if (!vmlinuz_file)
422 			FATAL("Missing required vmlinuz file.\n");
423 
424 		VB2_DEBUG("Reading %s\n", vmlinuz_file);
425 		if (VB2_SUCCESS !=
426 		    vb2_read_file(vmlinuz_file, &vmlinuz_buf, &vmlinuz_size))
427 			FATAL("Error reading vmlinuz file.\n");
428 
429 		VB2_DEBUG(" vmlinuz file size=%#x\n", vmlinuz_size);
430 		if (!vmlinuz_size)
431 			FATAL("Empty vmlinuz file\n");
432 
433 		kblob_data = CreateKernelBlob(
434 			vmlinuz_buf, vmlinuz_size,
435 			arch, kernel_body_load_address,
436 			t_config_data, t_config_size,
437 			t_bootloader_data, t_bootloader_size,
438 			&kblob_size);
439 		if (!kblob_data)
440 			FATAL("Unable to create kernel blob\n");
441 
442 		VB2_DEBUG("kblob_size = %#x\n", kblob_size);
443 
444 		vblock_data = SignKernelBlob(kblob_data, kblob_size, opt_pad,
445 					     version, kernel_body_load_address,
446 					     t_keyblock, signpriv_key, flags,
447 					     &vblock_size);
448 		if (!vblock_data)
449 			FATAL("Unable to sign kernel blob\n");
450 
451 		VB2_DEBUG("vblock_size = %#x\n", vblock_size);
452 
453 		if (opt_vblockonly)
454 			rv = WriteSomeParts(filename,
455 					    vblock_data, vblock_size,
456 					    NULL, 0);
457 		else
458 			rv = WriteSomeParts(filename,
459 					    vblock_data, vblock_size,
460 					    kblob_data, kblob_size);
461 
462 		free(vmlinuz_buf);
463 		free(t_config_data);
464 		free(t_bootloader_data);
465 		free(vblock_data);
466 		vb2_free_private_key(signpriv_key);
467 		return rv;
468 
469 	case OPT_MODE_REPACK:
470 
471 		/* Required */
472 
473 		if (!signprivkey_info)
474 			FATAL("Missing required signprivate info.\n");
475 
476 		if (bootloader_file)
477 			FATAL("--repack doesn't support --bootloader.\n");
478 
479 		signpriv_key = vb2_read_private_key(signprivkey_info);
480 		if (!signpriv_key)
481 			FATAL("Error reading signing key.\n");
482 
483 		if (!oldfile)
484 			FATAL("Missing previously packed blob.\n");
485 
486 		/* Load the kernel partition */
487 		kpart_data = ReadOldKPartFromFileOrDie(oldfile, &kpart_size);
488 
489 		/* Make sure we have a kernel partition */
490 		if (FILE_TYPE_KERN_PREAMBLE !=
491 		    futil_file_type_buf(kpart_data, kpart_size))
492 			FATAL("%s is not a kernel blob\n", oldfile);
493 
494 		kblob_data = unpack_kernel_partition(kpart_data, kpart_size,
495 						     &keyblock, &preamble,
496 						     &kblob_size);
497 
498 		if (!kblob_data)
499 			FATAL("Unable to unpack kernel partition\n");
500 
501 		kernel_body_load_address = preamble->body_load_address;
502 
503 		/* Update the config if asked */
504 		if (config_file) {
505 			VB2_DEBUG("Reading %s\n", config_file);
506 			t_config_data =
507 				ReadConfigFile(config_file, &t_config_size);
508 			if (!t_config_data)
509 				FATAL("Error reading config file.\n");
510 			if (UpdateKernelBlobConfig(
511 				    kblob_data, kblob_size,
512 				    t_config_data, t_config_size))
513 				FATAL("Unable to update config\n");
514 		}
515 
516 		if (!version_str)
517 			version = preamble->kernel_version;
518 
519 		if (vb2_kernel_get_flags(preamble))
520 			flags = vb2_kernel_get_flags(preamble);
521 
522 		if (keyblock_file) {
523 			t_keyblock = (struct vb2_keyblock *)
524 				ReadFile(keyblock_file, 0);
525 			if (!t_keyblock)
526 				FATAL("Error reading keyblock.\n");
527 		}
528 
529 		/* Reuse previous body size */
530 		vblock_data = SignKernelBlob(kblob_data, kblob_size, opt_pad,
531 					     version, kernel_body_load_address,
532 					     t_keyblock ? t_keyblock : keyblock,
533 					     signpriv_key, flags, &vblock_size);
534 		if (!vblock_data)
535 			FATAL("Unable to sign kernel blob\n");
536 
537 		if (opt_vblockonly)
538 			rv = WriteSomeParts(filename,
539 					    vblock_data, vblock_size,
540 					    NULL, 0);
541 		else
542 			rv = WriteSomeParts(filename,
543 					    vblock_data, vblock_size,
544 					    kblob_data, kblob_size);
545 		return rv;
546 
547 	case OPT_MODE_VERIFY:
548 
549 		/* Optional */
550 
551 		if (signpubkey_file) {
552 			signpub_key = vb2_read_packed_key(signpubkey_file);
553 			if (!signpub_key)
554 				FATAL("Error reading public key.\n");
555 		}
556 
557 		/* Do it */
558 
559 		/* Load the kernel partition */
560 		kpart_data = ReadOldKPartFromFileOrDie(filename, &kpart_size);
561 
562 		kblob_data = unpack_kernel_partition(kpart_data, kpart_size,
563 						     0, 0, &kblob_size);
564 		if (!kblob_data)
565 			FATAL("Unable to unpack kernel partition\n");
566 
567 		rv = VerifyKernelBlob(kblob_data, kblob_size,
568 				      signpub_key, keyblock_file, min_version);
569 
570 		return rv;
571 
572 	case OPT_MODE_GET_VMLINUZ:
573 
574 		if (!vmlinuz_out_file) {
575 			ERROR("USE: vbutil_kernel --get-vmlinuz <file> "
576 				"--vmlinuz-out <file>\n");
577 			print_help(argc, argv);
578 			return 1;
579 		}
580 
581 		kpart_data = ReadOldKPartFromFileOrDie(filename, &kpart_size);
582 
583 		kblob_data = unpack_kernel_partition(kpart_data, kpart_size,
584 						     &keyblock, &preamble,
585 						     &kblob_size);
586 
587 		if (!kblob_data)
588 			FATAL("Unable to unpack kernel partition\n");
589 
590 		f = fopen(vmlinuz_out_file, "wb");
591 		if (!f) {
592 			FATAL("Can't open output file %s\n", vmlinuz_out_file);
593 			return 1;
594 		}
595 
596 		/* Now stick 16-bit header followed by kernel block into
597 		   output */
598 		vb2_kernel_get_vmlinuz_header(preamble,
599 					      &vmlinuz_header_address,
600 					      &vmlinuz_header_size);
601 		if (vmlinuz_header_size) {
602 			// verify that the 16-bit header is included in the
603 			// kblob (to make sure that it's included in the
604 			// signature)
605 			if (vb2_verify_member_inside(
606 					(void *)preamble->body_load_address,
607 					kblob_size,
608 					(void *)vmlinuz_header_address,
609 					vmlinuz_header_size, 0, 0)) {
610 				fclose(f);
611 				unlink(vmlinuz_out_file);
612 				FATAL("Vmlinuz header not signed!\n");
613 				return 1;
614 			}
615 			// calculate the vmlinuz_header offset from
616 			// the beginning of the kpart_data.  The kblob doesn't
617 			// include the body_load_offset, but does include
618 			// the keyblock and preamble sections.
619 			vmlinuz_header_offset = vmlinuz_header_address -
620 				preamble->body_load_address +
621 				keyblock->keyblock_size +
622 				preamble->preamble_size;
623 			errcount |=
624 				(1 != fwrite(kpart_data + vmlinuz_header_offset,
625 					     vmlinuz_header_size,
626 					     1,
627 					     f));
628 		}
629 		errcount |= (1 != fwrite(kblob_data,
630 					 kblob_size,
631 					 1,
632 					 f));
633 		if (errcount) {
634 			fclose(f);
635 			unlink(vmlinuz_out_file);
636 			FATAL("Can't write output file %s\n", vmlinuz_out_file);
637 			return 1;
638 		}
639 
640 		fclose(f);
641 		return 0;
642 	}
643 
644 	ERROR("You must specify a mode: "
645 		"--pack, --repack, --verify, or --get-vmlinuz\n");
646 	print_help(argc, argv);
647 	return 1;
648 }
649 
650 DECLARE_FUTIL_COMMAND(vbutil_kernel, do_vbutil_kernel, VBOOT_VERSION_1_0,
651 		      "Creates, signs, and verifies the kernel partition");
652