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
Fatal(const char * format,...)30 static 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 */
41 static int opt_verbose;
42 static int opt_vblockonly;
43 static uint64_t opt_pad = 65536;
44
45 /* Command line options */
46 enum {
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
69 static 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
96 static 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 */
print_help(const char * progname)152 static void print_help(const char *progname)
153 {
154 printf(usage, progname, progname, progname, progname);
155 }
156
157
158 /* Return an explanation when fread() fails. */
error_fread(FILE * fp)159 static 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 */
ReadOldKPartFromFileOrDie(const char * filename,uint64_t * size_ptr)172 static 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", filename);
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
do_vbutil_kernel(int argc,char * argv[])217 static 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
650 DECLARE_FUTIL_COMMAND(vbutil_kernel, do_vbutil_kernel,
651 VBOOT_VERSION_1_0,
652 "Creates, signs, and verifies the kernel partition",
653 print_help);
654