/* Copyright (c) 2012 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 #include #include #include /* For PRIu64 */ #include #include /* For BLKGETSIZE64 */ #include #include #include #include #include #include #include #include #include "cryptolib.h" #include "host_common.h" #include "kernel_blob.h" #include "vboot_common.h" /* Global opts */ static int opt_debug = 0; static int opt_verbose = 0; static int opt_vblockonly = 0; static uint64_t opt_pad = 65536; /* Command line options */ enum { OPT_MODE_PACK = 1000, OPT_MODE_REPACK, OPT_MODE_VERIFY, 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, }; typedef enum { ARCH_ARM, ARCH_X86, /* default */ ARCH_MIPS } arch_t; static struct option long_opts[] = { {"pack", 1, 0, OPT_MODE_PACK }, {"repack", 1, 0, OPT_MODE_REPACK }, {"verify", 1, 0, OPT_MODE_VERIFY }, {"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 }, {"debug", 0, &opt_debug, 1 }, {NULL, 0, 0, 0} }; /* Print help and return error */ static int PrintHelp(char *progname) { fprintf(stderr, "This program creates, signs, and verifies the kernel blob\n"); fprintf(stderr, "\n" "Usage: %s --pack [PARAMETERS]\n" "\n" " Required parameters:\n" " --keyblock Key block in .keyblock format\n" " --signprivate Private key to sign kernel data,\n" " in .vbprivk format\n" " --version Kernel version\n" " --vmlinuz Linux kernel bzImage file\n" " --bootloader Bootloader stub\n" " --config Command line file\n" " --arch Cpu architecture (default x86)\n" "\n" " Optional:\n" " --kloadaddr
Assign kernel body load address\n" " --pad Verification padding size in bytes\n" " --vblockonly Emit just the verification blob\n", progname); fprintf(stderr, "\nOR\n\n" "Usage: %s --repack [PARAMETERS]\n" "\n" " Required parameters:\n" " --signprivate Private key to sign kernel data,\n" " in .vbprivk format\n" " --oldblob Previously packed kernel blob\n" " (including verfication blob)\n" "\n" " Optional:\n" " --keyblock Key block in .keyblock format\n" " --config New command line file\n" " --version Kernel version\n" " --kloadaddr
Assign kernel body load address\n" " --pad Verification blob size in bytes\n" " --vblockonly Emit just the verification blob\n", progname); fprintf(stderr, "\nOR\n\n" "Usage: %s --verify [PARAMETERS]\n" "\n" " Optional:\n" " --signpubkey " " Public key to verify kernel keyblock,\n" " in .vbpubk format\n" " --verbose Print a more detailed report\n" " --keyblock Outputs the verified key block,\n" " in .keyblock format\n" " --pad Verification padding size in bytes\n" " --minversion Minimum combined kernel key version\n" " and kernel version\n" "\n", progname); return 1; } static void Debug(const char *format, ...) { if (!opt_debug) return; va_list ap; va_start(ap, format); fprintf(stderr, "DEBUG: "); vfprintf(stderr, format, ap); va_end(ap); } 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); } /* 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; } /* Return the smallest integral multiple of [alignment] that is equal * to or greater than [val]. Used to determine the number of * pages/sectors/blocks/whatever needed to contain [val] * items/bytes/etc. */ static uint64_t roundup(uint64_t val, uint64_t alignment) { uint64_t rem = val % alignment; if ( rem ) return val + (alignment - rem); return val; } /* Match regexp /\b--\b/ to delimit the start of the kernel commandline. If we * don't find one, we'll use the whole thing. */ static unsigned int find_cmdline_start(char *input, unsigned int max_len) { int start = 0; int i; for(i = 0; i < max_len - 1 && input[i]; i++) { if ('-' == input[i] && '-' == input[i + 1]) { /* found a "--" */ if ((i == 0 || ' ' == input[i - 1]) && /* nothing before it */ (i + 2 >= max_len || ' ' == input[i+2])) { /* nothing after it */ start = i+2; /* note: hope there's a trailing '\0' */ break; } } } while(' ' == input[start]) /* skip leading spaces */ start++; return start; } /****************************************************************************/ /* Here are globals containing all the bits & pieces I'm working on. */ /* The individual parts that go into the kernel blob */ uint8_t *g_kernel_data; uint64_t g_kernel_size; uint8_t *g_param_data; uint64_t g_param_size; uint8_t *g_config_data; uint64_t g_config_size; uint8_t *g_bootloader_data; uint64_t g_bootloader_size; uint64_t g_bootloader_address; /* The individual parts of the verification blob (including the data that * immediately follows the headers) */ VbKeyBlockHeader* g_keyblock; VbKernelPreambleHeader* g_preamble; /****************************************************************************/ /* * Read the kernel command line from a file. Get rid of \n characters along * the way and verify that the line fits into a 4K buffer. * * Return the buffer contaning the line on success (and set the line length * using the passed in parameter), or NULL in case something goes wrong. */ static uint8_t* ReadConfigFile(const char* config_file, uint64_t* config_size) { uint8_t* config_buf; int ii; config_buf = ReadFile(config_file, config_size); Debug(" config file size=0x%" PRIx64 "\n", *config_size); if (CROS_CONFIG_SIZE <= *config_size) { /* need room for trailing '\0' */ VbExError("Config file %s is too large (>= %d bytes)\n", config_file, CROS_CONFIG_SIZE); return NULL; } /* Replace newlines with spaces */ for (ii = 0; ii < *config_size; ii++) { if ('\n' == config_buf[ii]) { config_buf[ii] = ' '; } } return config_buf; } /* Offset of kernel command line string from start of packed kernel blob */ static uint64_t CmdLineOffset(VbKernelPreambleHeader *preamble) { return preamble->bootloader_address - preamble->body_load_address - CROS_CONFIG_SIZE - CROS_PARAMS_SIZE; } /* This initializes g_vmlinuz and g_param from a standard vmlinuz file. * It returns 0 on error. */ static int ImportVmlinuzFile(const char *vmlinuz_file, arch_t arch, uint64_t kernel_body_load_address) { uint8_t *kernel_buf; uint64_t kernel_size; uint64_t kernel32_start = 0; uint64_t kernel32_size = 0; struct linux_kernel_params *params = NULL, *lh = NULL; /* Read the kernel */ Debug("Reading %s\n", vmlinuz_file); kernel_buf = ReadFile(vmlinuz_file, &kernel_size); if (!kernel_buf) return 0; Debug(" kernel file size=0x%" PRIx64 "\n", kernel_size); if (!kernel_size) Fatal("Empty kernel file\n"); /* Go ahead and allocate the param region anyway. I don't think we need it * for non-x86, but let's keep it for now. */ g_param_size = CROS_PARAMS_SIZE; g_param_data= VbExMalloc(g_param_size); Memset(g_param_data, 0, g_param_size); /* Unless we're handling x86, the kernel is the kernel, so we're done. */ if (arch != ARCH_X86) { g_kernel_data = kernel_buf; g_kernel_size = kernel_size; return 1; } /* The first part of the x86 vmlinuz is a header, followed by a real-mode * boot stub. We only want the 32-bit part. */ lh = (struct linux_kernel_params *)kernel_buf; kernel32_start = (lh->setup_sects + 1) << 9; if (kernel32_start >= kernel_size) Fatal("Malformed kernel\n"); kernel32_size = kernel_size - kernel32_start; Debug(" kernel32_start=0x%" PRIx64 "\n", kernel32_start); Debug(" kernel32_size=0x%" PRIx64 "\n", kernel32_size); /* Keep just the 32-bit kernel. */ if (kernel32_size) { g_kernel_size = kernel32_size; g_kernel_data = VbExMalloc(g_kernel_size); Memcpy(g_kernel_data, kernel_buf + kernel32_start, kernel32_size); } /* Copy the original zeropage data from kernel_buf into g_param_data, then * tweak a few fields for our purposes */ params = (struct linux_kernel_params *)(g_param_data); Memcpy(&(params->setup_sects), &(lh->setup_sects), offsetof(struct linux_kernel_params, e820_entries) - offsetof(struct linux_kernel_params, setup_sects)); params->boot_flag = 0; params->ramdisk_image = 0; /* we don't support initrd */ params->ramdisk_size = 0; params->type_of_loader = 0xff; /* We need to point to the kernel commandline arg. On disk, it will come * right after the 32-bit part of the kernel. */ params->cmd_line_ptr = kernel_body_load_address + roundup(kernel32_size, CROS_ALIGN) + find_cmdline_start((char *)g_config_data, g_config_size); Debug(" cmdline_addr=0x%x\n", params->cmd_line_ptr); Debug(" version=0x%x\n", params->version); Debug(" kernel_alignment=0x%x\n", params->kernel_alignment); Debug(" relocatable_kernel=0x%x\n", params->relocatable_kernel); /* A fake e820 memory map with 2 entries */ params->n_e820_entry = 2; params->e820_entries[0].start_addr = 0x00000000; params->e820_entries[0].segment_size = 0x00001000; params->e820_entries[0].segment_type = E820_TYPE_RAM; params->e820_entries[1].start_addr = 0xfffff000; params->e820_entries[1].segment_size = 0x00001000; params->e820_entries[1].segment_type = E820_TYPE_RESERVED; /* done */ free(kernel_buf); return 1; } /* This returns just the kernel blob, with the verification blob separated * and copied to new memory in g_keyblock and g_preamble. */ static uint8_t* ReadOldBlobFromFileOrDie(const char *filename, uint64_t* size_ptr) { FILE* fp = NULL; struct stat statbuf; VbKeyBlockHeader* key_block; VbKernelPreambleHeader* preamble; uint64_t now = 0; uint8_t* buf; uint8_t* kernel_blob_data; uint64_t kernel_blob_size; uint64_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)) { int fd; if ((fd = open(filename, O_RDONLY)) >= 0) { ioctl(fd, BLKGETSIZE64, &file_size); close(fd); } } else { file_size = statbuf.st_size; } Debug("%s size is 0x%" PRIx64 "\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 = VbExMalloc(opt_pad); if (1 != fread(buf, opt_pad, 1, fp)) Fatal("Unable to read header from %s: %s\n", filename, error_fread(fp)); /* Sanity-check the key_block */ key_block = (VbKeyBlockHeader*)buf; Debug("Keyblock is 0x%" PRIx64 " bytes\n", key_block->key_block_size); now += key_block->key_block_size; if (now > file_size) Fatal("key_block_size advances past the end of the blob\n"); if (now > opt_pad) Fatal("key_block_size advances past %" PRIu64 " byte padding\n", opt_pad); /* LGTM */ g_keyblock = (VbKeyBlockHeader*)VbExMalloc(key_block->key_block_size); Memcpy(g_keyblock, key_block, key_block->key_block_size); /* And the preamble */ preamble = (VbKernelPreambleHeader*)(buf + now); Debug("Preamble is 0x%" PRIx64 " bytes\n", preamble->preamble_size); now += preamble->preamble_size; if (now > file_size) Fatal("preamble_size advances past the end of the blob\n"); if (now > opt_pad) Fatal("preamble_size advances past %" PRIu64 " byte padding\n", opt_pad); /* LGTM */ Debug(" kernel_version = %d\n", preamble->kernel_version); Debug(" bootloader_address = 0x%" PRIx64 "\n", preamble->bootloader_address); Debug(" bootloader_size = 0x%" PRIx64 "\n", preamble->bootloader_size); Debug(" kern_blob_size = 0x%" PRIx64 "\n", preamble->body_signature.data_size); g_preamble = (VbKernelPreambleHeader*)VbExMalloc(preamble->preamble_size); Memcpy(g_preamble, preamble, preamble->preamble_size); /* Now for the kernel blob */ Debug("kernel blob is at offset 0x%" PRIx64 "\n", now); if (0 != fseek(fp, now, SEEK_SET)) Fatal("Unable to seek to 0x%" PRIx64 " in %s: %s\n", now, filename, strerror(errno)); /* Sanity check */ kernel_blob_size = file_size - now; if (!kernel_blob_size) Fatal("No kernel blob found\n"); if (kernel_blob_size < preamble->body_signature.data_size) fprintf(stderr, "Warning: kernel file only has 0x%" PRIx64 " bytes\n", kernel_blob_size); kernel_blob_data = VbExMalloc(kernel_blob_size); /* Read it in */ if (1 != fread(kernel_blob_data, kernel_blob_size, 1, fp)) Fatal("Unable to read kernel blob from %s: %s\n", filename, error_fread(fp)); /* Done */ VbExFree(buf); if (size_ptr) *size_ptr = kernel_blob_size; return kernel_blob_data; } /* Split a kernel blob into separate g_kernel, g_param, g_config, and * g_bootloader parts. */ static void UnpackKernelBlob(uint8_t *kernel_blob_data, uint64_t kernel_blob_size) { uint64_t k_blob_size = g_preamble->body_signature.data_size; uint64_t k_blob_ofs = 0; uint64_t b_size = g_preamble->bootloader_size; uint64_t b_ofs = k_blob_ofs + g_preamble->bootloader_address - g_preamble->body_load_address; uint64_t p_ofs = b_ofs - CROS_CONFIG_SIZE; uint64_t c_ofs = p_ofs - CROS_PARAMS_SIZE; Debug("k_blob_size = 0x%" PRIx64 "\n", k_blob_size ); Debug("k_blob_ofs = 0x%" PRIx64 "\n", k_blob_ofs ); Debug("b_size = 0x%" PRIx64 "\n", b_size ); Debug("b_ofs = 0x%" PRIx64 "\n", b_ofs ); Debug("p_ofs = 0x%" PRIx64 "\n", p_ofs ); Debug("c_ofs = 0x%" PRIx64 "\n", c_ofs ); g_kernel_size = c_ofs; g_kernel_data = VbExMalloc(g_kernel_size); Memcpy(g_kernel_data, kernel_blob_data, g_kernel_size); g_param_size = CROS_PARAMS_SIZE; g_param_data = VbExMalloc(g_param_size); Memcpy(g_param_data, kernel_blob_data + p_ofs, g_param_size); g_config_size = CROS_CONFIG_SIZE; g_config_data = VbExMalloc(g_config_size); Memcpy(g_config_data, kernel_blob_data + c_ofs, g_config_size); g_bootloader_size = b_size; g_bootloader_data = VbExMalloc(g_bootloader_size); Memcpy(g_bootloader_data, kernel_blob_data + b_ofs, g_bootloader_size); } /****************************************************************************/ static uint8_t* CreateKernelBlob(uint64_t kernel_body_load_address, arch_t arch, uint64_t *size_ptr) { uint8_t *kern_blob; uint64_t kern_blob_size; uint64_t now; uint64_t bootloader_size = roundup(g_bootloader_size, CROS_ALIGN); /* Put the kernel blob together */ kern_blob_size = roundup(g_kernel_size, CROS_ALIGN) + CROS_CONFIG_SIZE + CROS_PARAMS_SIZE + bootloader_size; Debug("kern_blob_size=0x%" PRIx64 "\n", kern_blob_size); kern_blob = VbExMalloc(kern_blob_size); Memset(kern_blob, 0, kern_blob_size); now = 0; Debug("kernel goes at kern_blob+0x%" PRIx64 "\n", now); Memcpy(kern_blob+now, g_kernel_data, g_kernel_size); now += roundup(g_kernel_size, CROS_ALIGN); Debug("config goes at kern_blob+0x%" PRIx64 "\n", now); if (g_config_size) Memcpy(kern_blob + now, g_config_data, g_config_size); now += CROS_CONFIG_SIZE; Debug("params goes at kern_blob+0x%" PRIx64 "\n", now); if (g_param_size) { Memcpy(kern_blob + now, g_param_data, g_param_size); } now += CROS_PARAMS_SIZE; Debug("bootloader goes at kern_blob+0x%" PRIx64 "\n", now); g_bootloader_address = kernel_body_load_address + now; Debug(" bootloader_address=0x%" PRIx64 "\n", g_bootloader_address); Debug(" bootloader_size=0x%" PRIx64 "\n", bootloader_size); if (bootloader_size) Memcpy(kern_blob + now, g_bootloader_data, g_bootloader_size); now += bootloader_size; Debug("end of kern_blob at kern_blob+0x%" PRIx64 "\n", now); /* Done */ if (size_ptr) *size_ptr = kern_blob_size; return kern_blob; } static int Pack(const char* outfile, uint8_t *kernel_blob, uint64_t kernel_size, int version, uint64_t kernel_body_load_address, VbPrivateKey* signpriv_key) { VbSignature* body_sig; FILE* f; uint64_t i; uint64_t written = 0; /* Sign the kernel data */ body_sig = CalculateSignature(kernel_blob, kernel_size, signpriv_key); if (!body_sig) Fatal("Error calculating body signature\n"); /* Create preamble */ g_preamble = CreateKernelPreamble(version, kernel_body_load_address, g_bootloader_address, roundup(g_bootloader_size, CROS_ALIGN), body_sig, opt_pad - g_keyblock->key_block_size, signpriv_key); if (!g_preamble) { VbExError("Error creating preamble.\n"); return 1; } /* Write the output file */ Debug("writing %s...\n", outfile); f = fopen(outfile, "wb"); if (!f) { VbExError("Can't open output file %s\n", outfile); return 1; } Debug("0x%" PRIx64 " bytes of key_block\n", g_keyblock->key_block_size); Debug("0x%" PRIx64 " bytes of preamble\n", g_preamble->preamble_size); i = ((1 != fwrite(g_keyblock, g_keyblock->key_block_size, 1, f)) || (1 != fwrite(g_preamble, g_preamble->preamble_size, 1, f))); if (i) { VbExError("Can't write output file %s\n", outfile); fclose(f); unlink(outfile); return 1; } written += g_keyblock->key_block_size; written += g_preamble->preamble_size; if (!opt_vblockonly) { Debug("0x%" PRIx64 " bytes of kern_blob\n", kernel_size); i = (1 != fwrite(kernel_blob, kernel_size, 1, f)); if (i) { fclose(f); unlink(outfile); Fatal("Can't write output file %s\n", outfile); } written += kernel_size; } Debug("0x%" PRIx64 " bytes total\n", written); fclose(f); /* Success */ return 0; } static int Verify(uint8_t* kernel_blob, uint64_t kernel_size, VbPublicKey* signpub_key, const char* keyblock_outfile, uint64_t min_version) { VbPublicKey* data_key; RSAPublicKey* rsa; if (0 != KeyBlockVerify(g_keyblock, g_keyblock->key_block_size, signpub_key, (0 == signpub_key))) Fatal("Error verifying key block.\n"); printf("Key block:\n"); data_key = &g_keyblock->data_key; if (opt_verbose) printf(" Signature: %s\n", signpub_key ? "valid" : "ignored"); printf(" Size: 0x%" PRIx64 "\n", g_keyblock->key_block_size); printf(" Flags: %" PRIu64 " ", g_keyblock->key_block_flags); if (g_keyblock->key_block_flags & KEY_BLOCK_FLAG_DEVELOPER_0) printf(" !DEV"); if (g_keyblock->key_block_flags & KEY_BLOCK_FLAG_DEVELOPER_1) printf(" DEV"); if (g_keyblock->key_block_flags & KEY_BLOCK_FLAG_RECOVERY_0) printf(" !REC"); if (g_keyblock->key_block_flags & KEY_BLOCK_FLAG_RECOVERY_1) printf(" REC"); printf("\n"); printf(" Data key algorithm: %" PRIu64 " %s\n", data_key->algorithm, (data_key->algorithm < kNumAlgorithms ? algo_strings[data_key->algorithm] : "(invalid)")); printf(" Data key version: %" PRIu64 "\n", data_key->key_version); printf(" Data key sha1sum: "); PrintPubKeySha1Sum(data_key); printf("\n"); if (keyblock_outfile) { FILE* f = NULL; f = fopen(keyblock_outfile, "wb"); if (!f) Fatal("Can't open key block file %s\n", keyblock_outfile); if (1 != fwrite(g_keyblock, g_keyblock->key_block_size, 1, f)) Fatal("Can't write key block file %s\n", keyblock_outfile); fclose(f); } if (data_key->key_version < (min_version >> 16)) Fatal("Data key version %" PRIu64 " is lower than minimum %" PRIu64".\n", data_key->key_version, (min_version >> 16)); rsa = PublicKeyToRSA(data_key); if (!rsa) Fatal("Error parsing data key.\n"); /* Verify preamble */ if (0 != VerifyKernelPreamble( g_preamble, g_preamble->preamble_size, rsa)) Fatal("Error verifying preamble.\n"); printf("Preamble:\n"); printf(" Size: 0x%" PRIx64 "\n", g_preamble->preamble_size); printf(" Header version: %" PRIu32 ".%" PRIu32"\n", g_preamble->header_version_major, g_preamble->header_version_minor); printf(" Kernel version: %" PRIu64 "\n", g_preamble->kernel_version); printf(" Body load address: 0x%" PRIx64 "\n", g_preamble->body_load_address); printf(" Body size: 0x%" PRIx64 "\n", g_preamble->body_signature.data_size); printf(" Bootloader address: 0x%" PRIx64 "\n", g_preamble->bootloader_address); printf(" Bootloader size: 0x%" PRIx64 "\n", g_preamble->bootloader_size); if (g_preamble->kernel_version < (min_version & 0xFFFF)) Fatal("Kernel version %" PRIu64 " is lower than minimum %" PRIu64 ".\n", g_preamble->kernel_version, (min_version & 0xFFFF)); /* Verify body */ if (0 != VerifyData(kernel_blob, kernel_size, &g_preamble->body_signature, rsa)) Fatal("Error verifying kernel body.\n"); printf("Body verification succeeded.\n"); if (opt_verbose) printf("Config:\n%s\n", kernel_blob + CmdLineOffset(g_preamble)); return 0; } /****************************************************************************/ int main(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; arch_t arch = ARCH_X86; char *address_str = NULL; uint64_t kernel_body_load_address = CROS_32BIT_ENTRY_ADDR; int mode = 0; int parse_error = 0; uint64_t min_version = 0; char* e; int i; VbPrivateKey* signpriv_key = NULL; VbPublicKey* signpub_key = NULL; uint8_t* kernel_blob = NULL; uint64_t kernel_size = 0; char *progname = strrchr(argv[0], '/'); if (progname) progname++; else progname = argv[0]; 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_MODE_PACK: case OPT_MODE_REPACK: case OPT_MODE_VERIFY: if (mode && (mode != i)) { fprintf(stderr, "Only a single 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")) 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: address_str = optarg; 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_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; } } if (parse_error) return PrintHelp(progname); switch(mode) { case OPT_MODE_PACK: /* Required */ if (!keyblock_file) Fatal("Missing required keyblock file.\n"); g_keyblock = (VbKeyBlockHeader*)ReadFile(keyblock_file, 0); if (!g_keyblock) Fatal("Error reading key block.\n"); if (!signprivkey_file) Fatal("Missing required signprivate file.\n"); signpriv_key = PrivateKeyRead(signprivkey_file); if (!signpriv_key) Fatal("Error reading signing key.\n"); /* Optional */ if (config_file) { Debug("Reading %s\n", config_file); g_config_data = ReadConfigFile(config_file, &g_config_size); if (!g_config_data) Fatal("Error reading config file.\n"); } if (vmlinuz_file) if (!ImportVmlinuzFile(vmlinuz_file, arch, kernel_body_load_address)) Fatal("Error reading kernel file.\n"); if (bootloader_file) { Debug("Reading %s\n", bootloader_file); g_bootloader_data = ReadFile(bootloader_file, &g_bootloader_size); if (!g_bootloader_data) Fatal("Error reading bootloader file.\n"); Debug(" bootloader file size=0x%" PRIx64 "\n", g_bootloader_size); } /* Do it */ kernel_blob = CreateKernelBlob(kernel_body_load_address, arch, &kernel_size); return Pack(filename, kernel_blob, kernel_size, version, kernel_body_load_address, signpriv_key); case OPT_MODE_REPACK: /* Required */ if (!signprivkey_file) Fatal("Missing required signprivate file.\n"); signpriv_key = PrivateKeyRead(signprivkey_file); if (!signpriv_key) Fatal("Error reading signing key.\n"); if (!oldfile) Fatal("Missing previously packed blob.\n"); /* Load the old blob */ kernel_blob = ReadOldBlobFromFileOrDie(oldfile, &kernel_size); if (0 != Verify(kernel_blob, kernel_size, 0, 0, 0)) Fatal("The oldblob doesn't verify\n"); /* Take it apart */ UnpackKernelBlob(kernel_blob, kernel_size); free(kernel_blob); /* Load optional params */ if (!version_str) version = g_preamble->kernel_version; if (!address_str) kernel_body_load_address = g_preamble->body_load_address; if (config_file) { if (g_config_data) free(g_config_data); Debug("Reading %s\n", config_file); g_config_data = ReadConfigFile(config_file, &g_config_size); if (!g_config_data) Fatal("Error reading config file.\n"); } if (keyblock_file) { if (g_keyblock) free(g_keyblock); g_keyblock = (VbKeyBlockHeader*)ReadFile(keyblock_file, 0); if (!g_keyblock) Fatal("Error reading key block.\n"); } /* Put it back together */ kernel_blob = CreateKernelBlob(kernel_body_load_address, arch, &kernel_size); return Pack(filename, kernel_blob, kernel_size, version, kernel_body_load_address, signpriv_key); case OPT_MODE_VERIFY: /* Optional */ if (signpubkey_file) { signpub_key = PublicKeyRead(signpubkey_file); if (!signpub_key) Fatal("Error reading public key.\n"); } /* Do it */ kernel_blob = ReadOldBlobFromFileOrDie(filename, &kernel_size); return Verify(kernel_blob, kernel_size, signpub_key, keyblock_file, min_version); } fprintf(stderr, "You must specify a mode: --pack, --repack or --verify\n"); return PrintHelp(progname); }