Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1682)

Unified Diff: utility/vbutil_kernel.c

Issue 3004001: Introduce ability to change the kernel command line. (Closed) Base URL: ssh://git@chromiumos-git/vboot_reference.git
Patch Set: Address review comments. Created 10 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « Makefile ('k') | no next file » | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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,
« no previous file with comments | « Makefile ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698