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