123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666 |
- /*
- * Copyright 2014 The Chromium OS Authors. All rights reserved.
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- *
- * Verified boot kernel utility
- */
- #include <errno.h>
- #include <fcntl.h>
- #include <getopt.h>
- #include <inttypes.h> /* For PRIu64 */
- #ifndef HAVE_MACOS
- #include <linux/fs.h> /* For BLKGETSIZE64 */
- #endif
- #include <stdarg.h>
- #include <stdio.h>
- #include <string.h>
- #include <sys/ioctl.h>
- #include <sys/stat.h>
- #include <unistd.h>
- #include "2sysincludes.h"
- #include "2common.h"
- #include "file_type.h"
- #include "futility.h"
- #include "host_common.h"
- #include "kernel_blob.h"
- #include "vb1_helper.h"
- #include "vb2_common.h"
- #include "vb2_struct.h"
- static void Fatal(const char *format, ...)
- {
- va_list ap;
- va_start(ap, format);
- fprintf(stderr, "ERROR: ");
- vfprintf(stderr, format, ap);
- va_end(ap);
- exit(1);
- }
- /* Global opts */
- static int opt_verbose;
- static int opt_vblockonly;
- static uint64_t opt_pad = 65536;
- /* Command line options */
- enum {
- OPT_MODE_PACK = 1000,
- OPT_MODE_REPACK,
- OPT_MODE_VERIFY,
- OPT_MODE_GET_VMLINUZ,
- OPT_ARCH,
- OPT_OLDBLOB,
- OPT_KLOADADDR,
- OPT_KEYBLOCK,
- OPT_SIGNPUBKEY,
- OPT_SIGNPRIVATE,
- OPT_VERSION,
- OPT_VMLINUZ,
- OPT_BOOTLOADER,
- OPT_CONFIG,
- OPT_VBLOCKONLY,
- OPT_PAD,
- OPT_VERBOSE,
- OPT_MINVERSION,
- OPT_VMLINUZ_OUT,
- OPT_FLAGS,
- OPT_HELP,
- };
- static const struct option long_opts[] = {
- {"pack", 1, 0, OPT_MODE_PACK},
- {"repack", 1, 0, OPT_MODE_REPACK},
- {"verify", 1, 0, OPT_MODE_VERIFY},
- {"get-vmlinuz", 1, 0, OPT_MODE_GET_VMLINUZ},
- {"arch", 1, 0, OPT_ARCH},
- {"oldblob", 1, 0, OPT_OLDBLOB},
- {"kloadaddr", 1, 0, OPT_KLOADADDR},
- {"keyblock", 1, 0, OPT_KEYBLOCK},
- {"signpubkey", 1, 0, OPT_SIGNPUBKEY},
- {"signprivate", 1, 0, OPT_SIGNPRIVATE},
- {"version", 1, 0, OPT_VERSION},
- {"minversion", 1, 0, OPT_MINVERSION},
- {"vmlinuz", 1, 0, OPT_VMLINUZ},
- {"bootloader", 1, 0, OPT_BOOTLOADER},
- {"config", 1, 0, OPT_CONFIG},
- {"vblockonly", 0, 0, OPT_VBLOCKONLY},
- {"pad", 1, 0, OPT_PAD},
- {"verbose", 0, &opt_verbose, 1},
- {"vmlinuz-out", 1, 0, OPT_VMLINUZ_OUT},
- {"flags", 1, 0, OPT_FLAGS},
- {"help", 0, 0, OPT_HELP},
- {NULL, 0, 0, 0}
- };
- static const char usage[] =
- "\n"
- "Usage: " MYNAME " %s --pack <file> [PARAMETERS]\n"
- "\n"
- " Required parameters:\n"
- " --keyblock <file> Key block in .keyblock format\n"
- " --signprivate <file> Private key to sign kernel data,\n"
- " in .vbprivk format\n"
- " --version <number> Kernel version\n"
- " --vmlinuz <file> Linux kernel bzImage file\n"
- " --bootloader <file> Bootloader stub\n"
- " --config <file> Command line file\n"
- " --arch <arch> Cpu architecture (default x86)\n"
- "\n"
- " Optional:\n"
- " --kloadaddr <address> Assign kernel body load address\n"
- " --pad <number> Verification padding size in bytes\n"
- " --vblockonly Emit just the verification blob\n"
- " --flags NUM Flags to be passed in the header\n"
- "\nOR\n\n"
- "Usage: " MYNAME " %s --repack <file> [PARAMETERS]\n"
- "\n"
- " Required parameters:\n"
- " --signprivate <file> Private key to sign kernel data,\n"
- " in .vbprivk format\n"
- " --oldblob <file> Previously packed kernel blob\n"
- " (including verfication blob)\n"
- "\n"
- " Optional:\n"
- " --keyblock <file> Key block in .keyblock format\n"
- " --config <file> New command line file\n"
- " --version <number> Kernel version\n"
- " --kloadaddr <address> Assign kernel body load address\n"
- " --pad <number> Verification blob size in bytes\n"
- " --vblockonly Emit just the verification blob\n"
- "\nOR\n\n"
- "Usage: " MYNAME " %s --verify <file> [PARAMETERS]\n"
- "\n"
- " Optional:\n"
- " --signpubkey <file>"
- " Public key to verify kernel keyblock,\n"
- " in .vbpubk format\n"
- " --verbose Print a more detailed report\n"
- " --keyblock <file> Outputs the verified key block,\n"
- " in .keyblock format\n"
- " --pad <number> Verification padding size in bytes\n"
- " --minversion <number> Minimum combined kernel key version\n"
- "\nOR\n\n"
- "Usage: " MYNAME " %s --get-vmlinuz <file> [PARAMETERS]\n"
- "\n"
- " Required parameters:\n"
- " --vmlinuz-out <file> vmlinuz image output file\n"
- "\n";
- /* Print help and return error */
- static void print_help(int argc, char *argv[])
- {
- printf(usage, argv[0], argv[0], argv[0], argv[0]);
- }
- /* Return an explanation when fread() fails. */
- static const char *error_fread(FILE *fp)
- {
- const char *retval = "beats me why";
- if (feof(fp))
- retval = "EOF";
- else if (ferror(fp))
- retval = strerror(errno);
- clearerr(fp);
- return retval;
- }
- /* This reads a complete kernel partition into a buffer */
- static uint8_t *ReadOldKPartFromFileOrDie(const char *filename,
- uint32_t *size_ptr)
- {
- FILE *fp = NULL;
- struct stat statbuf;
- uint8_t *buf;
- uint32_t file_size = 0;
- if (0 != stat(filename, &statbuf))
- Fatal("Unable to stat %s: %s\n", filename, strerror(errno));
- if (S_ISBLK(statbuf.st_mode)) {
- #ifndef HAVE_MACOS
- int fd = open(filename, O_RDONLY);
- if (fd >= 0) {
- ioctl(fd, BLKGETSIZE64, &file_size);
- close(fd);
- }
- #endif
- } else {
- file_size = statbuf.st_size;
- }
- Debug("%s size is 0x%x\n", filename, file_size);
- if (file_size < opt_pad)
- Fatal("%s is too small to be a valid kernel blob\n");
- Debug("Reading %s\n", filename);
- fp = fopen(filename, "rb");
- if (!fp)
- Fatal("Unable to open file %s: %s\n", filename,
- strerror(errno));
- buf = malloc(file_size);
- if (1 != fread(buf, file_size, 1, fp))
- Fatal("Unable to read entirety of %s: %s\n", filename,
- error_fread(fp));
- if (size_ptr)
- *size_ptr = file_size;
- fclose(fp);
- return buf;
- }
- /****************************************************************************/
- static int do_vbutil_kernel(int argc, char *argv[])
- {
- char *filename = NULL;
- char *oldfile = NULL;
- char *keyblock_file = NULL;
- char *signpubkey_file = NULL;
- char *signprivkey_file = NULL;
- char *version_str = NULL;
- int version = -1;
- char *vmlinuz_file = NULL;
- char *bootloader_file = NULL;
- char *config_file = NULL;
- char *vmlinuz_out_file = NULL;
- enum arch_t arch = ARCH_X86;
- uint64_t kernel_body_load_address = CROS_32BIT_ENTRY_ADDR;
- int mode = 0;
- int parse_error = 0;
- uint32_t min_version = 0;
- char *e;
- int i = 0;
- int errcount = 0;
- int rv;
- struct vb2_keyblock *keyblock = NULL;
- struct vb2_keyblock *t_keyblock = NULL;
- struct vb2_private_key *signpriv_key = NULL;
- struct vb2_packed_key *signpub_key = NULL;
- uint8_t *kpart_data = NULL;
- uint32_t kpart_size = 0;
- uint8_t *vmlinuz_buf = NULL;
- uint32_t vmlinuz_size = 0;
- uint8_t *t_config_data;
- uint32_t t_config_size;
- uint8_t *t_bootloader_data;
- uint32_t t_bootloader_size;
- uint32_t vmlinuz_header_size = 0;
- uint64_t vmlinuz_header_address = 0;
- uint32_t vmlinuz_header_offset = 0;
- struct vb2_kernel_preamble *preamble = NULL;
- uint8_t *kblob_data = NULL;
- uint32_t kblob_size = 0;
- uint8_t *vblock_data = NULL;
- uint32_t vblock_size = 0;
- uint32_t flags = 0;
- FILE *f;
- while (((i = getopt_long(argc, argv, ":", long_opts, NULL)) != -1) &&
- !parse_error) {
- switch (i) {
- default:
- case '?':
- /* Unhandled option */
- parse_error = 1;
- break;
- case 0:
- /* silently handled option */
- break;
- case OPT_HELP:
- print_help(argc, argv);
- return !!parse_error;
- case OPT_MODE_PACK:
- case OPT_MODE_REPACK:
- case OPT_MODE_VERIFY:
- case OPT_MODE_GET_VMLINUZ:
- if (mode && (mode != i)) {
- fprintf(stderr,
- "Only one mode can be specified\n");
- parse_error = 1;
- break;
- }
- mode = i;
- filename = optarg;
- break;
- case OPT_ARCH:
- /* check the first 3 characters to also detect x86_64 */
- if ((!strncasecmp(optarg, "x86", 3)) ||
- (!strcasecmp(optarg, "amd64")))
- arch = ARCH_X86;
- else if ((!strcasecmp(optarg, "arm")) ||
- (!strcasecmp(optarg, "aarch64")))
- arch = ARCH_ARM;
- else if (!strcasecmp(optarg, "mips"))
- arch = ARCH_MIPS;
- else {
- fprintf(stderr,
- "Unknown architecture string: %s\n",
- optarg);
- parse_error = 1;
- }
- break;
- case OPT_OLDBLOB:
- oldfile = optarg;
- break;
- case OPT_KLOADADDR:
- kernel_body_load_address = strtoul(optarg, &e, 0);
- if (!*optarg || (e && *e)) {
- fprintf(stderr, "Invalid --kloadaddr\n");
- parse_error = 1;
- }
- break;
- case OPT_KEYBLOCK:
- keyblock_file = optarg;
- break;
- case OPT_SIGNPUBKEY:
- signpubkey_file = optarg;
- break;
- case OPT_SIGNPRIVATE:
- signprivkey_file = optarg;
- break;
- case OPT_VMLINUZ:
- vmlinuz_file = optarg;
- break;
- case OPT_FLAGS:
- flags = (uint32_t)strtoul(optarg, &e, 0);
- if (!*optarg || (e && *e)) {
- fprintf(stderr, "Invalid --flags\n");
- parse_error = 1;
- }
- break;
- case OPT_BOOTLOADER:
- bootloader_file = optarg;
- break;
- case OPT_CONFIG:
- config_file = optarg;
- break;
- case OPT_VBLOCKONLY:
- opt_vblockonly = 1;
- break;
- case OPT_VERSION:
- version_str = optarg;
- version = strtoul(optarg, &e, 0);
- if (!*optarg || (e && *e)) {
- fprintf(stderr, "Invalid --version\n");
- parse_error = 1;
- }
- break;
- case OPT_MINVERSION:
- min_version = strtoul(optarg, &e, 0);
- if (!*optarg || (e && *e)) {
- fprintf(stderr, "Invalid --minversion\n");
- parse_error = 1;
- }
- break;
- case OPT_PAD:
- opt_pad = strtoul(optarg, &e, 0);
- if (!*optarg || (e && *e)) {
- fprintf(stderr, "Invalid --pad\n");
- parse_error = 1;
- }
- break;
- case OPT_VMLINUZ_OUT:
- vmlinuz_out_file = optarg;
- }
- }
- if (parse_error) {
- print_help(argc, argv);
- return 1;
- }
- switch (mode) {
- case OPT_MODE_PACK:
- if (!keyblock_file)
- Fatal("Missing required keyblock file.\n");
- t_keyblock = (struct vb2_keyblock *)ReadFile(keyblock_file, 0);
- if (!t_keyblock)
- Fatal("Error reading key block.\n");
- if (!signprivkey_file)
- Fatal("Missing required signprivate file.\n");
- signpriv_key = vb2_read_private_key(signprivkey_file);
- if (!signpriv_key)
- Fatal("Error reading signing key.\n");
- if (!config_file)
- Fatal("Missing required config file.\n");
- Debug("Reading %s\n", config_file);
- t_config_data =
- ReadConfigFile(config_file, &t_config_size);
- if (!t_config_data)
- Fatal("Error reading config file.\n");
- if (!bootloader_file)
- Fatal("Missing required bootloader file.\n");
- Debug("Reading %s\n", bootloader_file);
- if (VB2_SUCCESS != vb2_read_file(bootloader_file,
- &t_bootloader_data,
- &t_bootloader_size))
- Fatal("Error reading bootloader file.\n");
- Debug(" bootloader file size=0x%x\n", t_bootloader_size);
- if (!vmlinuz_file)
- Fatal("Missing required vmlinuz file.\n");
- Debug("Reading %s\n", vmlinuz_file);
- if (VB2_SUCCESS !=
- vb2_read_file(vmlinuz_file, &vmlinuz_buf, &vmlinuz_size))
- Fatal("Error reading vmlinuz file.\n");
- Debug(" vmlinuz file size=0x%x\n", vmlinuz_size);
- if (!vmlinuz_size)
- Fatal("Empty vmlinuz file\n");
- kblob_data = CreateKernelBlob(
- vmlinuz_buf, vmlinuz_size,
- arch, kernel_body_load_address,
- t_config_data, t_config_size,
- t_bootloader_data, t_bootloader_size,
- &kblob_size);
- if (!kblob_data)
- Fatal("Unable to create kernel blob\n");
- Debug("kblob_size = 0x%x\n", kblob_size);
- vblock_data = SignKernelBlob(kblob_data, kblob_size, opt_pad,
- version, kernel_body_load_address,
- t_keyblock, signpriv_key, flags,
- &vblock_size);
- if (!vblock_data)
- Fatal("Unable to sign kernel blob\n");
- Debug("vblock_size = 0x%x\n", vblock_size);
- if (opt_vblockonly)
- rv = WriteSomeParts(filename,
- vblock_data, vblock_size,
- NULL, 0);
- else
- rv = WriteSomeParts(filename,
- vblock_data, vblock_size,
- kblob_data, kblob_size);
- free(vmlinuz_buf);
- free(t_config_data);
- free(t_bootloader_data);
- free(vblock_data);
- vb2_free_private_key(signpriv_key);
- return rv;
- case OPT_MODE_REPACK:
- /* Required */
- if (!signprivkey_file)
- Fatal("Missing required signprivate file.\n");
- signpriv_key = vb2_read_private_key(signprivkey_file);
- if (!signpriv_key)
- Fatal("Error reading signing key.\n");
- if (!oldfile)
- Fatal("Missing previously packed blob.\n");
- /* Load the kernel partition */
- kpart_data = ReadOldKPartFromFileOrDie(oldfile, &kpart_size);
- /* Make sure we have a kernel partition */
- if (FILE_TYPE_KERN_PREAMBLE !=
- futil_file_type_buf(kpart_data, kpart_size))
- Fatal("%s is not a kernel blob\n", oldfile);
- kblob_data = unpack_kernel_partition(kpart_data, kpart_size,
- opt_pad, &keyblock,
- &preamble, &kblob_size);
- if (!kblob_data)
- Fatal("Unable to unpack kernel partition\n");
- kernel_body_load_address = preamble->body_load_address;
- /* Update the config if asked */
- if (config_file) {
- Debug("Reading %s\n", config_file);
- t_config_data =
- ReadConfigFile(config_file, &t_config_size);
- if (!t_config_data)
- Fatal("Error reading config file.\n");
- if (0 != UpdateKernelBlobConfig(
- kblob_data, kblob_size,
- t_config_data, t_config_size))
- Fatal("Unable to update config\n");
- }
- if (!version_str)
- version = preamble->kernel_version;
- if (vb2_kernel_get_flags(preamble))
- flags = vb2_kernel_get_flags(preamble);
- if (keyblock_file) {
- t_keyblock = (struct vb2_keyblock *)
- ReadFile(keyblock_file, 0);
- if (!t_keyblock)
- Fatal("Error reading key block.\n");
- }
- /* Reuse previous body size */
- vblock_data = SignKernelBlob(kblob_data, kblob_size, opt_pad,
- version, kernel_body_load_address,
- t_keyblock ? t_keyblock : keyblock,
- signpriv_key, flags, &vblock_size);
- if (!vblock_data)
- Fatal("Unable to sign kernel blob\n");
- if (opt_vblockonly)
- rv = WriteSomeParts(filename,
- vblock_data, vblock_size,
- NULL, 0);
- else
- rv = WriteSomeParts(filename,
- vblock_data, vblock_size,
- kblob_data, kblob_size);
- return rv;
- case OPT_MODE_VERIFY:
- /* Optional */
- if (signpubkey_file) {
- signpub_key = vb2_read_packed_key(signpubkey_file);
- if (!signpub_key)
- Fatal("Error reading public key.\n");
- }
- /* Do it */
- /* Load the kernel partition */
- kpart_data = ReadOldKPartFromFileOrDie(filename, &kpart_size);
- kblob_data = unpack_kernel_partition(kpart_data, kpart_size,
- opt_pad, 0, 0,
- &kblob_size);
- if (!kblob_data)
- Fatal("Unable to unpack kernel partition\n");
- rv = VerifyKernelBlob(kblob_data, kblob_size,
- signpub_key, keyblock_file, min_version);
- return rv;
- case OPT_MODE_GET_VMLINUZ:
- if (!vmlinuz_out_file) {
- fprintf(stderr,
- "USE: vbutil_kernel --get-vmlinuz <file> "
- "--vmlinuz-out <file>\n");
- print_help(argc, argv);
- return 1;
- }
- kpart_data = ReadOldKPartFromFileOrDie(filename, &kpart_size);
- kblob_data = unpack_kernel_partition(kpart_data, kpart_size,
- opt_pad, &keyblock,
- &preamble, &kblob_size);
- if (!kblob_data)
- Fatal("Unable to unpack kernel partition\n");
- f = fopen(vmlinuz_out_file, "wb");
- if (!f) {
- VbExError("Can't open output file %s\n",
- vmlinuz_out_file);
- return 1;
- }
- /* Now stick 16-bit header followed by kernel block into
- output */
- vb2_kernel_get_vmlinuz_header(preamble,
- &vmlinuz_header_address,
- &vmlinuz_header_size);
- if (vmlinuz_header_size) {
- // verify that the 16-bit header is included in the
- // kblob (to make sure that it's included in the
- // signature)
- if (VerifyVmlinuzInsideKBlob(preamble->body_load_address,
- kblob_size,
- vmlinuz_header_address,
- vmlinuz_header_size)) {
- VbExError("Vmlinuz header not signed!\n");
- fclose(f);
- unlink(vmlinuz_out_file);
- return 1;
- }
- // calculate the vmlinuz_header offset from
- // the beginning of the kpart_data. The kblob doesn't
- // include the body_load_offset, but does include
- // the keyblock and preamble sections.
- vmlinuz_header_offset = vmlinuz_header_address -
- preamble->body_load_address +
- keyblock->keyblock_size +
- preamble->preamble_size;
- errcount |=
- (1 != fwrite(kpart_data + vmlinuz_header_offset,
- vmlinuz_header_size,
- 1,
- f));
- }
- errcount |= (1 != fwrite(kblob_data,
- kblob_size,
- 1,
- f));
- if (errcount) {
- VbExError("Can't write output file %s\n",
- vmlinuz_out_file);
- fclose(f);
- unlink(vmlinuz_out_file);
- return 1;
- }
- fclose(f);
- return 0;
- }
- fprintf(stderr,
- "You must specify a mode: "
- "--pack, --repack, --verify, or --get-vmlinuz\n");
- print_help(argc, argv);
- return 1;
- }
- DECLARE_FUTIL_COMMAND(vbutil_kernel, do_vbutil_kernel, VBOOT_VERSION_1_0,
- "Creates, signs, and verifies the kernel partition");
|