| Index: utility/vbutil_kernel.c
|
| diff --git a/utility/vbutil_kernel.c b/utility/vbutil_kernel.c
|
| index 349cc8e012bba6fd3909768eb2775e0414741660..d688e61f20a2abcea299347691fe02a7400811ab 100644
|
| --- a/utility/vbutil_kernel.c
|
| +++ b/utility/vbutil_kernel.c
|
| @@ -43,6 +43,7 @@ enum {
|
| OPT_CONFIG,
|
| OPT_VBLOCKONLY,
|
| OPT_PAD,
|
| + OPT_VERBOSE,
|
| };
|
|
|
| static struct option long_opts[] = {
|
| @@ -59,6 +60,7 @@ static struct option long_opts[] = {
|
| {"config", 1, 0, OPT_CONFIG },
|
| {"vblockonly", 0, 0, OPT_VBLOCKONLY },
|
| {"pad", 1, 0, OPT_PAD },
|
| + {"verbose", 0, 0, OPT_VERBOSE },
|
| {"debug", 0, &opt_debug, 1 },
|
| {NULL, 0, 0, 0}
|
| };
|
| @@ -78,7 +80,7 @@ static int PrintHelp(char *progname) {
|
| " --version <number> Kernel version\n"
|
| " --vmlinuz <file> Linux kernel bzImage file\n"
|
| " --bootloader <file> Bootloader stub\n"
|
| - " --config <file> Config file\n"
|
| + " --config <file> Command line file\n"
|
| "\n"
|
| " Optional:\n"
|
| " --pad <number> Verification padding size in bytes\n"
|
| @@ -88,10 +90,12 @@ static int PrintHelp(char *progname) {
|
| "\nOR\n\n"
|
| "Usage: %s --repack <file> [PARAMETERS]\n"
|
| "\n"
|
| - " Required parameters:\n"
|
| + " Required parameters (of --keyblock and --config at least "
|
| + "one is required):\n"
|
| " --keyblock <file> Key block in .keyblock format\n"
|
| " --signprivate <file> Signing private key in .pem format\n"
|
| " --oldblob <file> Previously packed kernel blob\n"
|
| + " --config <file> New command line file\n"
|
| "\n"
|
| " Optional:\n"
|
| " --pad <number> Verification padding size in bytes\n"
|
| @@ -103,6 +107,9 @@ static int PrintHelp(char *progname) {
|
| "\n"
|
| " Required parameters:\n"
|
| " --signpubkey <file> Signing public key in .vbpubk format\n"
|
| + "\n"
|
| + " Optional:\n"
|
| + " --verbose Print a more detailed report\n"
|
| "\n",
|
| progname);
|
| return 1;
|
| @@ -161,17 +168,60 @@ typedef struct blob_s {
|
| /* Raw kernel blob data */
|
| uint64_t blob_size;
|
| uint8_t *blob;
|
| +
|
| + /* these fields are not always initialized */
|
| + VbKernelPreambleHeader* preamble;
|
| + VbKeyBlockHeader* key_block;
|
| + uint8_t *buf;
|
| +
|
| } blob_t;
|
|
|
| +/* Given a blob return the location of the kernel command line buffer. */
|
| +static char* BpCmdLineLocation(blob_t *bp)
|
| +{
|
| + return (char*)(bp->blob + bp->bootloader_address - CROS_32BIT_ENTRY_ADDR -
|
| + CROS_CONFIG_SIZE - CROS_PARAMS_SIZE);
|
| +}
|
|
|
| static void FreeBlob(blob_t *bp) {
|
| if (bp) {
|
| if (bp->blob)
|
| Free(bp->blob);
|
| + if (bp->buf)
|
| + Free(bp->buf);
|
| Free(bp);
|
| }
|
| }
|
|
|
| +/*
|
| + * 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' */
|
| + error("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;
|
| +}
|
| +
|
| /* Create a blob from its components */
|
| static blob_t *NewBlob(uint64_t version,
|
| const char* vmlinuz,
|
| @@ -191,7 +241,6 @@ static blob_t *NewBlob(uint64_t version,
|
| uint32_t cmdline_addr;
|
| uint8_t* blob = NULL;
|
| uint64_t now = 0;
|
| - uint64_t i;
|
|
|
| if (!vmlinuz || !bootloader_file || !config_file) {
|
| error("Must specify all input files\n");
|
| @@ -203,23 +252,15 @@ static blob_t *NewBlob(uint64_t version,
|
| error("Couldn't allocate bytes for blob_t.\n");
|
| return 0;
|
| }
|
| +
|
| + Memset(bp, 0, sizeof(*bp));
|
| bp->kernel_version = version;
|
|
|
| /* Read the config file */
|
| Debug("Reading %s\n", config_file);
|
| - config_buf = ReadFile(config_file, &config_size);
|
| + config_buf = ReadConfigFile(config_file, &config_size);
|
| if (!config_buf)
|
| return 0;
|
| - Debug(" config file size=0x%" PRIx64 "\n", config_size);
|
| - if (CROS_CONFIG_SIZE <= config_size) { /* need room for trailing '\0' */
|
| - error("Config file %s is too large (>= %d bytes)\n",
|
| - config_file, CROS_CONFIG_SIZE);
|
| - return 0;
|
| - }
|
| - /* Replace newlines with spaces */
|
| - for (i = 0; i < config_size; i++)
|
| - if ('\n' == config_buf[i])
|
| - config_buf[i] = ' ';
|
|
|
| /* Read the bootloader */
|
| Debug("Reading %s\n", bootloader_file);
|
| @@ -319,13 +360,14 @@ static blob_t *NewBlob(uint64_t version,
|
|
|
| /* Pull the blob_t stuff out of a prepacked kernel blob file */
|
| static blob_t *OldBlob(const char* filename) {
|
| - FILE* fp;
|
| - blob_t *bp;
|
| + FILE* fp = NULL;
|
| + blob_t *bp = NULL;
|
| struct stat statbuf;
|
| VbKeyBlockHeader* key_block;
|
| VbKernelPreambleHeader* preamble;
|
| uint64_t now = 0;
|
| - uint8_t buf[DEFAULT_PADDING];
|
| + uint8_t* buf = NULL;
|
| + int ret_error = 1;
|
|
|
| if (!filename) {
|
| error("Must specify prepacked blob to read\n");
|
| @@ -350,10 +392,15 @@ static blob_t *OldBlob(const char* filename) {
|
| return 0;
|
| }
|
|
|
| - if (1 != fread(buf, sizeof(buf), 1, fp)) {
|
| + buf = Malloc(DEFAULT_PADDING);
|
| + if (!buf) {
|
| + error("Unable to allocate padding\n");
|
| + goto unwind_oldblob;
|
| + }
|
| +
|
| + if (1 != fread(buf, DEFAULT_PADDING, 1, fp)) {
|
| error("Unable to read header from %s: %s\n", filename, strerror(errno));
|
| - fclose(fp);
|
| - return 0;
|
| + goto unwind_oldblob;
|
| }
|
|
|
| /* Skip the key block */
|
| @@ -362,7 +409,7 @@ static blob_t *OldBlob(const char* filename) {
|
| now += key_block->key_block_size;
|
| if (now > statbuf.st_size) {
|
| error("key_block_size advances past the end of the blob\n");
|
| - return 0;
|
| + goto unwind_oldblob;
|
| }
|
|
|
| /* Skip the preamble */
|
| @@ -371,7 +418,7 @@ static blob_t *OldBlob(const char* filename) {
|
| now += preamble->preamble_size;
|
| if (now > statbuf.st_size) {
|
| error("preamble_size advances past the end of the blob\n");
|
| - return 0;
|
| + goto unwind_oldblob;
|
| }
|
|
|
| /* Go find the kernel blob */
|
| @@ -379,18 +426,20 @@ static blob_t *OldBlob(const char* filename) {
|
| if (0 != fseek(fp, now, SEEK_SET)) {
|
| error("Unable to seek to 0x%" PRIx64 " in %s: %s\n", now, filename,
|
| strerror(errno));
|
| - fclose(fp);
|
| - return 0;
|
| + goto unwind_oldblob;
|
| }
|
|
|
| /* Remember what we've got */
|
| bp = (blob_t *)Malloc(sizeof(blob_t));
|
| if (!bp) {
|
| error("Couldn't allocate bytes for blob_t.\n");
|
| - fclose(fp);
|
| - return 0;
|
| + goto unwind_oldblob;
|
| }
|
|
|
| + bp->buf = buf;
|
| + bp->key_block = key_block;
|
| + bp->preamble = preamble;
|
| +
|
| bp->kernel_version = preamble->kernel_version;
|
| bp->bootloader_address = preamble->bootloader_address;
|
| bp->bootloader_size = preamble->bootloader_size;
|
| @@ -404,22 +453,28 @@ static blob_t *OldBlob(const char* filename) {
|
| bp->blob = (uint8_t *)Malloc(bp->blob_size);
|
| if (!bp->blob) {
|
| error("Couldn't allocate 0x%" PRIx64 " bytes for blob_t.\n", bp->blob_size);
|
| - fclose(fp);
|
| - Free(bp);
|
| - return 0;
|
| + goto unwind_oldblob;
|
| }
|
|
|
| /* read it in */
|
| if (1 != fread(bp->blob, bp->blob_size, 1, fp)) {
|
| error("Unable to read kernel blob from %s: %s\n", filename, strerror(errno));
|
| - fclose(fp);
|
| - Free(bp);
|
| - return 0;
|
| + goto unwind_oldblob;
|
| }
|
|
|
| + ret_error = 0;
|
| +
|
| /* done */
|
| +unwind_oldblob:
|
| fclose(fp);
|
| -
|
| + if (ret_error) {
|
| + if (bp) {
|
| + FreeBlob(bp);
|
| + bp = NULL;
|
| + } else if (buf) {
|
| + Free(buf);
|
| + }
|
| + }
|
| return bp;
|
| }
|
|
|
| @@ -440,21 +495,27 @@ static int Pack(const char* outfile, const char* keyblock_file,
|
| error("Must specify output filename\n");
|
| return 1;
|
| }
|
| - if (!keyblock_file || !signprivate) {
|
| + if ((!keyblock_file && !bp->key_block) || !signprivate) {
|
| error("Must specify all keys\n");
|
| return 1;
|
| }
|
| if (!bp) {
|
| error("Refusing to pack invalid kernel blob\n");
|
| return 1;
|
| - }
|
| + }
|
|
|
| - /* Read the key block and private key */
|
| - key_block = (VbKeyBlockHeader*)ReadFile(keyblock_file, &key_block_size);
|
| - if (!key_block) {
|
| - error("Error reading key block.\n");
|
| - return 1;
|
| + /* Get the key block and read the private key. */
|
| + if (keyblock_file) {
|
| + key_block = (VbKeyBlockHeader*)ReadFile(keyblock_file, &key_block_size);
|
| + if (!key_block) {
|
| + error("Error reading key block.\n");
|
| + return 1;
|
| + }
|
| + } else {
|
| + key_block = bp->key_block;
|
| + key_block_size = key_block->key_block_size;
|
| }
|
| +
|
| if (pad < key_block->key_block_size) {
|
| error("Pad too small\n");
|
| return 1;
|
| @@ -521,17 +582,40 @@ static int Pack(const char* outfile, const char* keyblock_file,
|
| return 0;
|
| }
|
|
|
| +/*
|
| + * Replace kernel command line in a blob representing a kernel.
|
| + */
|
| +static int ReplaceConfig(blob_t* bp, const char* config_file)
|
| +{
|
| + uint8_t* new_conf;
|
| + uint64_t config_size;
|
| +
|
| + if (!config_file) {
|
| + return 0;
|
| + }
|
|
|
| -static int Verify(const char* infile, const char* signpubkey) {
|
| + new_conf = ReadConfigFile(config_file, &config_size);
|
| + if (!new_conf) {
|
| + return 1;
|
| + }
|
| +
|
| + /* fill the config buffer with zeros */
|
| + Memset(BpCmdLineLocation(bp), 0, CROS_CONFIG_SIZE);
|
| + Memcpy(BpCmdLineLocation(bp), new_conf, config_size);
|
| + Free(new_conf);
|
| + return 0;
|
| +}
|
| +
|
| +static int Verify(const char* infile, const char* signpubkey, int verbose) {
|
|
|
| VbKeyBlockHeader* key_block;
|
| VbKernelPreambleHeader* preamble;
|
| VbPublicKey* data_key;
|
| VbPublicKey* sign_key;
|
| RSAPublicKey* rsa;
|
| - uint8_t* blob;
|
| - uint64_t blob_size;
|
| - uint64_t now = 0;
|
| + blob_t* bp;
|
| + uint64_t now;
|
| + int rv = 1;
|
|
|
| if (!infile || !signpubkey) {
|
| error("Must specify filename and signpubkey\n");
|
| @@ -546,20 +630,19 @@ static int Verify(const char* infile, const char* signpubkey) {
|
| }
|
|
|
| /* Read blob */
|
| - blob = ReadFile(infile, &blob_size);
|
| - if (!blob) {
|
| + bp = OldBlob(infile);
|
| + if (!bp) {
|
| error("Error reading input file\n");
|
| return 1;
|
| }
|
|
|
| /* Verify key block */
|
| - key_block = (VbKeyBlockHeader*)blob;
|
| - if (0 != KeyBlockVerify(key_block, blob_size, sign_key)) {
|
| + key_block = bp->key_block;
|
| + if (0 != KeyBlockVerify(key_block, bp->blob_size, sign_key)) {
|
| error("Error verifying key block.\n");
|
| - return 1;
|
| + goto verify_exit;
|
| }
|
| - Free(sign_key);
|
| - now += key_block->key_block_size;
|
| + now = key_block->key_block_size;
|
|
|
| printf("Key block:\n");
|
| data_key = &key_block->data_key;
|
| @@ -573,14 +656,15 @@ static int Verify(const char* infile, const char* signpubkey) {
|
| rsa = PublicKeyToRSA(&key_block->data_key);
|
| if (!rsa) {
|
| error("Error parsing data key.\n");
|
| - return 1;
|
| + goto verify_exit;
|
| }
|
|
|
| /* Verify preamble */
|
| - preamble = (VbKernelPreambleHeader*)(blob + now);
|
| - if (0 != VerifyKernelPreamble2(preamble, blob_size - now, rsa)) {
|
| + preamble = bp->preamble;
|
| + if (0 != VerifyKernelPreamble2(
|
| + preamble, bp->blob_size - key_block->key_block_size, rsa)) {
|
| error("Error verifying preamble.\n");
|
| - return 1;
|
| + goto verify_exit;
|
| }
|
| now += preamble->preamble_size;
|
|
|
| @@ -596,12 +680,23 @@ static int Verify(const char* infile, const char* signpubkey) {
|
| printf(" Bootloader size: 0x%" PRIx64 "\n", preamble->bootloader_size);
|
|
|
| /* Verify body */
|
| - if (0 != VerifyData(blob + now, &preamble->body_signature, rsa)) {
|
| + if (0 != VerifyData(bp->blob, &preamble->body_signature, rsa)) {
|
| error("Error verifying kernel body.\n");
|
| - return 1;
|
| + goto verify_exit;
|
| }
|
| printf("Body verification succeeded.\n");
|
| - return 0;
|
| +
|
| + rv = 0;
|
| +
|
| + if (!verbose) {
|
| + goto verify_exit;
|
| + }
|
| +
|
| + printf("Config:\n%s\n", BpCmdLineLocation(bp));
|
| +
|
| +verify_exit:
|
| + FreeBlob(bp);
|
| + return rv;
|
| }
|
|
|
|
|
| @@ -616,6 +711,7 @@ int main(int argc, char* argv[]) {
|
| char* bootloader = NULL;
|
| char* config_file = NULL;
|
| int vblockonly = 0;
|
| + int verbose = 0;
|
| uint64_t pad = DEFAULT_PADDING;
|
| int mode = 0;
|
| int parse_error = 0;
|
| @@ -630,8 +726,10 @@ int main(int argc, char* argv[]) {
|
| else
|
| progname = argv[0];
|
|
|
| - while ((i = getopt_long(argc, argv, ":", long_opts, NULL)) != -1) {
|
| + while (((i = getopt_long(argc, argv, ":", long_opts, NULL)) != -1) &&
|
| + !parse_error) {
|
| switch (i) {
|
| + default:
|
| case '?':
|
| /* Unhandled option */
|
| parse_error = 1;
|
| @@ -640,6 +738,11 @@ int main(int argc, char* argv[]) {
|
| case OPT_MODE_PACK:
|
| case OPT_MODE_REPACK:
|
| case OPT_MODE_VERIFY:
|
| + if (mode && (mode != i)) {
|
| + fprintf(stderr, "Only single mode can be specified\n");
|
| + parse_error = 1;
|
| + break;
|
| + }
|
| mode = i;
|
| filename = optarg;
|
| break;
|
| @@ -691,6 +794,10 @@ int main(int argc, char* argv[]) {
|
| parse_error = 1;
|
| }
|
| break;
|
| +
|
| + case OPT_VERBOSE:
|
| + verbose = 1;
|
| + break;
|
| }
|
| }
|
|
|
| @@ -707,15 +814,24 @@ int main(int argc, char* argv[]) {
|
| return r;
|
|
|
| case OPT_MODE_REPACK:
|
| + if (!config_file && !key_block_file) {
|
| + fprintf(stderr,
|
| + "You must supply at least one of --config and --keyblock\n");
|
| + return 1;
|
| + }
|
| +
|
| bp = OldBlob(oldfile);
|
| if (!bp)
|
| return 1;
|
| - r = Pack(filename, key_block_file, signprivate, bp, pad, vblockonly);
|
| + r = ReplaceConfig(bp, config_file);
|
| + if (!r) {
|
| + r = Pack(filename, key_block_file, signprivate, bp, pad, vblockonly);
|
| + }
|
| FreeBlob(bp);
|
| return r;
|
|
|
| case OPT_MODE_VERIFY:
|
| - return Verify(filename, signpubkey);
|
| + return Verify(filename, signpubkey, verbose);
|
|
|
| default:
|
| fprintf(stderr,
|
|
|