blob: 763480e35f0f415758e7ca21680aa33c132a7504 [file] [log] [blame]
Randall Spangler7d6898d2010-06-11 09:22:13 -07001/* Copyright (c) 2010 The Chromium OS Authors. All rights reserved.
2 * Use of this source code is governed by a BSD-style license that can be
3 * found in the LICENSE file.
4 *
5 * Verified boot kernel utility
6 */
7
Bill Richardsona08b5c92010-06-30 21:59:43 -07008#include <errno.h>
Randall Spangler7d6898d2010-06-11 09:22:13 -07009#include <getopt.h>
10#include <inttypes.h> /* For PRIu64 */
Bill Richardson249677d2010-06-23 11:16:37 -070011#include <stdarg.h>
Randall Spangler7d6898d2010-06-11 09:22:13 -070012#include <stddef.h>
13#include <stdio.h>
14#include <stdlib.h>
Bill Richardsona08b5c92010-06-30 21:59:43 -070015#include <string.h>
16#include <sys/stat.h>
17#include <sys/types.h>
Randall Spangler7d6898d2010-06-11 09:22:13 -070018#include <unistd.h>
19
20#include "cryptolib.h"
21#include "host_common.h"
22#include "kernel_blob.h"
23#include "vboot_common.h"
24
25
Bill Richardson249677d2010-06-23 11:16:37 -070026/* Global opt */
27static int opt_debug = 0;
28
Bill Richardsona08b5c92010-06-30 21:59:43 -070029static const int DEFAULT_PADDING = 65536;
Bill Richardson249677d2010-06-23 11:16:37 -070030
Randall Spangler7d6898d2010-06-11 09:22:13 -070031/* Command line options */
32enum {
33 OPT_MODE_PACK = 1000,
Bill Richardsona08b5c92010-06-30 21:59:43 -070034 OPT_MODE_REPACK,
Randall Spangler7d6898d2010-06-11 09:22:13 -070035 OPT_MODE_VERIFY,
Bill Richardsona08b5c92010-06-30 21:59:43 -070036 OPT_OLDBLOB,
Randall Spangler7d6898d2010-06-11 09:22:13 -070037 OPT_KEYBLOCK,
38 OPT_SIGNPUBKEY,
39 OPT_SIGNPRIVATE,
40 OPT_VERSION,
41 OPT_VMLINUZ,
42 OPT_BOOTLOADER,
43 OPT_CONFIG,
Bill Richardsona08b5c92010-06-30 21:59:43 -070044 OPT_VBLOCKONLY,
Randall Spangler7d6898d2010-06-11 09:22:13 -070045 OPT_PAD,
vbendebb2b0fcc2010-07-15 15:09:47 -070046 OPT_VERBOSE,
Randall Spangler7d6898d2010-06-11 09:22:13 -070047};
48
49static struct option long_opts[] = {
50 {"pack", 1, 0, OPT_MODE_PACK },
Bill Richardsona08b5c92010-06-30 21:59:43 -070051 {"repack", 1, 0, OPT_MODE_REPACK },
Randall Spangler7d6898d2010-06-11 09:22:13 -070052 {"verify", 1, 0, OPT_MODE_VERIFY },
Bill Richardsona08b5c92010-06-30 21:59:43 -070053 {"oldblob", 1, 0, OPT_OLDBLOB },
Randall Spangler7d6898d2010-06-11 09:22:13 -070054 {"keyblock", 1, 0, OPT_KEYBLOCK },
55 {"signpubkey", 1, 0, OPT_SIGNPUBKEY },
56 {"signprivate", 1, 0, OPT_SIGNPRIVATE },
57 {"version", 1, 0, OPT_VERSION },
58 {"vmlinuz", 1, 0, OPT_VMLINUZ },
59 {"bootloader", 1, 0, OPT_BOOTLOADER },
60 {"config", 1, 0, OPT_CONFIG },
Bill Richardsona08b5c92010-06-30 21:59:43 -070061 {"vblockonly", 0, 0, OPT_VBLOCKONLY },
Randall Spangler7d6898d2010-06-11 09:22:13 -070062 {"pad", 1, 0, OPT_PAD },
vbendebb2b0fcc2010-07-15 15:09:47 -070063 {"verbose", 0, 0, OPT_VERBOSE },
Bill Richardson249677d2010-06-23 11:16:37 -070064 {"debug", 0, &opt_debug, 1 },
Randall Spangler7d6898d2010-06-11 09:22:13 -070065 {NULL, 0, 0, 0}
66};
67
68
69/* Print help and return error */
Bill Richardsona08b5c92010-06-30 21:59:43 -070070static int PrintHelp(char *progname) {
71 fprintf(stderr,
72 "This program creates, signs, and verifies the kernel blob\n");
73 fprintf(stderr,
74 "\n"
75 "Usage: %s --pack <file> [PARAMETERS]\n"
76 "\n"
77 " Required parameters:\n"
78 " --keyblock <file> Key block in .keyblock format\n"
Bill Richardson4f36ef32010-08-09 17:50:14 -070079 " --signprivate <file>"
80 " Private key to sign kernel data, in .vbprivk format\n"
Bill Richardsona08b5c92010-06-30 21:59:43 -070081 " --version <number> Kernel version\n"
82 " --vmlinuz <file> Linux kernel bzImage file\n"
83 " --bootloader <file> Bootloader stub\n"
vbendebb2b0fcc2010-07-15 15:09:47 -070084 " --config <file> Command line file\n"
Bill Richardsona08b5c92010-06-30 21:59:43 -070085 "\n"
86 " Optional:\n"
87 " --pad <number> Verification padding size in bytes\n"
88 " --vblockonly Emit just the verification blob\n",
89 progname);
90 fprintf(stderr,
91 "\nOR\n\n"
92 "Usage: %s --repack <file> [PARAMETERS]\n"
93 "\n"
vbendebb2b0fcc2010-07-15 15:09:47 -070094 " Required parameters (of --keyblock and --config at least "
95 "one is required):\n"
Bill Richardsona08b5c92010-06-30 21:59:43 -070096 " --keyblock <file> Key block in .keyblock format\n"
Bill Richardson4f36ef32010-08-09 17:50:14 -070097 " --signprivate <file>"
98 " Private key to sign kernel data, in .vbprivk format\n"
Bill Richardsona08b5c92010-06-30 21:59:43 -070099 " --oldblob <file> Previously packed kernel blob\n"
vbendebb2b0fcc2010-07-15 15:09:47 -0700100 " --config <file> New command line file\n"
Bill Richardsona08b5c92010-06-30 21:59:43 -0700101 "\n"
102 " Optional:\n"
103 " --pad <number> Verification padding size in bytes\n"
104 " --vblockonly Emit just the verification blob\n",
105 progname);
106 fprintf(stderr,
107 "\nOR\n\n"
108 "Usage: %s --verify <file> [PARAMETERS]\n"
109 "\n"
vbendebb2b0fcc2010-07-15 15:09:47 -0700110 " Optional:\n"
Bill Richardson4f36ef32010-08-09 17:50:14 -0700111 " --signpubkey <file>"
112 " Public key to verify kernel keyblock, in .vbpubk format\n"
vbendebb2b0fcc2010-07-15 15:09:47 -0700113 " --verbose Print a more detailed report\n"
Bill Richardsona08b5c92010-06-30 21:59:43 -0700114 "\n",
115 progname);
Randall Spangler7d6898d2010-06-11 09:22:13 -0700116 return 1;
117}
118
Bill Richardson249677d2010-06-23 11:16:37 -0700119static void Debug(const char *format, ...) {
120 if (!opt_debug)
121 return;
122
123 va_list ap;
124 va_start(ap, format);
125 fprintf(stderr, "DEBUG: ");
126 vfprintf(stderr, format, ap);
127 va_end(ap);
128}
129
Randall Spangler7d6898d2010-06-11 09:22:13 -0700130
131/* Return the smallest integral multiple of [alignment] that is equal
132 * to or greater than [val]. Used to determine the number of
133 * pages/sectors/blocks/whatever needed to contain [val]
134 * items/bytes/etc. */
135static uint64_t roundup(uint64_t val, uint64_t alignment) {
136 uint64_t rem = val % alignment;
137 if ( rem )
138 return val + (alignment - rem);
139 return val;
140}
141
142
143/* Match regexp /\b--\b/ to delimit the start of the kernel commandline. If we
144 * don't find one, we'll use the whole thing. */
145static unsigned int find_cmdline_start(char *input, unsigned int max_len) {
146 int start = 0;
147 int i;
148 for(i = 0; i < max_len - 1 && input[i]; i++) {
149 if ('-' == input[i] && '-' == input[i + 1]) { /* found a "--" */
150 if ((i == 0 || ' ' == input[i - 1]) && /* nothing before it */
151 (i + 2 >= max_len || ' ' == input[i+2])) { /* nothing after it */
152 start = i+2; /* note: hope there's a trailing '\0' */
153 break;
154 }
155 }
156 }
157 while(' ' == input[start]) /* skip leading spaces */
158 start++;
159
160 return start;
161}
162
163
Bill Richardsona08b5c92010-06-30 21:59:43 -0700164typedef struct blob_s {
165 /* Stuff needed by VbKernelPreambleHeader */
166 uint64_t kernel_version;
167 uint64_t bootloader_address;
168 uint64_t bootloader_size;
169 /* Raw kernel blob data */
170 uint64_t blob_size;
171 uint8_t *blob;
vbendebb2b0fcc2010-07-15 15:09:47 -0700172
173 /* these fields are not always initialized */
174 VbKernelPreambleHeader* preamble;
175 VbKeyBlockHeader* key_block;
176 uint8_t *buf;
177
Bill Richardsona08b5c92010-06-30 21:59:43 -0700178} blob_t;
Randall Spangler7d6898d2010-06-11 09:22:13 -0700179
vbendebb2b0fcc2010-07-15 15:09:47 -0700180/* Given a blob return the location of the kernel command line buffer. */
181static char* BpCmdLineLocation(blob_t *bp)
182{
183 return (char*)(bp->blob + bp->bootloader_address - CROS_32BIT_ENTRY_ADDR -
184 CROS_CONFIG_SIZE - CROS_PARAMS_SIZE);
185}
Bill Richardsona08b5c92010-06-30 21:59:43 -0700186
187static void FreeBlob(blob_t *bp) {
188 if (bp) {
189 if (bp->blob)
190 Free(bp->blob);
vbendebb2b0fcc2010-07-15 15:09:47 -0700191 if (bp->buf)
192 Free(bp->buf);
Bill Richardsona08b5c92010-06-30 21:59:43 -0700193 Free(bp);
194 }
195}
196
vbendebb2b0fcc2010-07-15 15:09:47 -0700197/*
198 * Read the kernel command line from a file. Get rid of \n characters along
199 * the way and verify that the line fits into a 4K buffer.
200 *
201 * Return the buffer contaning the line on success (and set the line length
202 * using the passed in parameter), or NULL in case something goes wrong.
203 */
204static uint8_t* ReadConfigFile(const char* config_file, uint64_t* config_size)
205{
206 uint8_t* config_buf;
207 int ii;
208
209 config_buf = ReadFile(config_file, config_size);
210 Debug(" config file size=0x%" PRIx64 "\n", *config_size);
211 if (CROS_CONFIG_SIZE <= *config_size) { /* need room for trailing '\0' */
212 error("Config file %s is too large (>= %d bytes)\n",
213 config_file, CROS_CONFIG_SIZE);
214 return NULL;
215 }
216
217 /* Replace newlines with spaces */
218 for (ii = 0; ii < *config_size; ii++) {
219 if ('\n' == config_buf[ii]) {
220 config_buf[ii] = ' ';
221 }
222 }
223 return config_buf;
224}
225
Bill Richardsona08b5c92010-06-30 21:59:43 -0700226/* Create a blob from its components */
227static blob_t *NewBlob(uint64_t version,
228 const char* vmlinuz,
229 const char* bootloader_file,
230 const char* config_file) {
231 blob_t *bp;
Randall Spangler7d6898d2010-06-11 09:22:13 -0700232 struct linux_kernel_header *lh = 0;
233 struct linux_kernel_params *params = 0;
Randall Spangler7d6898d2010-06-11 09:22:13 -0700234 uint8_t* config_buf;
235 uint64_t config_size;
236 uint8_t* bootloader_buf;
237 uint64_t bootloader_size;
Randall Spangler7d6898d2010-06-11 09:22:13 -0700238 uint8_t* kernel_buf;
239 uint64_t kernel_size;
240 uint64_t kernel32_start = 0;
241 uint64_t kernel32_size = 0;
242 uint32_t cmdline_addr;
243 uint8_t* blob = NULL;
Randall Spangler7d6898d2010-06-11 09:22:13 -0700244 uint64_t now = 0;
Randall Spangler7d6898d2010-06-11 09:22:13 -0700245
Randall Spangler7d6898d2010-06-11 09:22:13 -0700246 if (!vmlinuz || !bootloader_file || !config_file) {
247 error("Must specify all input files\n");
Bill Richardsona08b5c92010-06-30 21:59:43 -0700248 return 0;
Randall Spangler7d6898d2010-06-11 09:22:13 -0700249 }
250
Bill Richardsona08b5c92010-06-30 21:59:43 -0700251 bp = (blob_t *)Malloc(sizeof(blob_t));
252 if (!bp) {
253 error("Couldn't allocate bytes for blob_t.\n");
254 return 0;
Randall Spangler7d6898d2010-06-11 09:22:13 -0700255 }
vbendebb2b0fcc2010-07-15 15:09:47 -0700256
257 Memset(bp, 0, sizeof(*bp));
Bill Richardsona08b5c92010-06-30 21:59:43 -0700258 bp->kernel_version = version;
Randall Spangler7d6898d2010-06-11 09:22:13 -0700259
260 /* Read the config file */
Bill Richardson249677d2010-06-23 11:16:37 -0700261 Debug("Reading %s\n", config_file);
vbendebb2b0fcc2010-07-15 15:09:47 -0700262 config_buf = ReadConfigFile(config_file, &config_size);
Randall Spangler7d6898d2010-06-11 09:22:13 -0700263 if (!config_buf)
Bill Richardsona08b5c92010-06-30 21:59:43 -0700264 return 0;
Randall Spangler7d6898d2010-06-11 09:22:13 -0700265
266 /* Read the bootloader */
Bill Richardson249677d2010-06-23 11:16:37 -0700267 Debug("Reading %s\n", bootloader_file);
Randall Spangler7d6898d2010-06-11 09:22:13 -0700268 bootloader_buf = ReadFile(bootloader_file, &bootloader_size);
269 if (!bootloader_buf)
Bill Richardsona08b5c92010-06-30 21:59:43 -0700270 return 0;
Bill Richardson249677d2010-06-23 11:16:37 -0700271 Debug(" bootloader file size=0x%" PRIx64 "\n", bootloader_size);
Randall Spangler7d6898d2010-06-11 09:22:13 -0700272
273 /* Read the kernel */
Bill Richardson249677d2010-06-23 11:16:37 -0700274 Debug("Reading %s\n", vmlinuz);
Randall Spangler7d6898d2010-06-11 09:22:13 -0700275 kernel_buf = ReadFile(vmlinuz, &kernel_size);
276 if (!kernel_buf)
Bill Richardsona08b5c92010-06-30 21:59:43 -0700277 return 0;
Bill Richardson249677d2010-06-23 11:16:37 -0700278 Debug(" kernel file size=0x%" PRIx64 "\n", kernel_size);
Randall Spangler7d6898d2010-06-11 09:22:13 -0700279 if (!kernel_size) {
280 error("Empty kernel file\n");
Bill Richardsona08b5c92010-06-30 21:59:43 -0700281 return 0;
Randall Spangler7d6898d2010-06-11 09:22:13 -0700282 }
283
284 /* The first part of vmlinuz is a header, followed by a real-mode
285 * boot stub. We only want the 32-bit part. */
286 lh = (struct linux_kernel_header *)kernel_buf;
287 kernel32_start = (lh->setup_sects + 1) << 9;
288 if (kernel32_start >= kernel_size) {
289 error("Malformed kernel\n");
Bill Richardsona08b5c92010-06-30 21:59:43 -0700290 return 0;
Randall Spangler7d6898d2010-06-11 09:22:13 -0700291 }
292 kernel32_size = kernel_size - kernel32_start;
Bill Richardson249677d2010-06-23 11:16:37 -0700293 Debug(" kernel32_start=0x%" PRIx64 "\n", kernel32_start);
294 Debug(" kernel32_size=0x%" PRIx64 "\n", kernel32_size);
Randall Spangler7d6898d2010-06-11 09:22:13 -0700295
296 /* Allocate and zero the blob we need. */
Bill Richardsona08b5c92010-06-30 21:59:43 -0700297 bp->blob_size = roundup(kernel32_size, CROS_ALIGN) +
Randall Spangler7d6898d2010-06-11 09:22:13 -0700298 CROS_CONFIG_SIZE +
299 CROS_PARAMS_SIZE +
300 roundup(bootloader_size, CROS_ALIGN);
Bill Richardsona08b5c92010-06-30 21:59:43 -0700301 blob = (uint8_t *)Malloc(bp->blob_size);
302 Debug("blob_size=0x%" PRIx64 "\n", bp->blob_size);
Randall Spangler7d6898d2010-06-11 09:22:13 -0700303 if (!blob) {
Bill Richardsona08b5c92010-06-30 21:59:43 -0700304 error("Couldn't allocate %ld bytes.\n", bp->blob_size);
305 return 0;
Randall Spangler7d6898d2010-06-11 09:22:13 -0700306 }
Bill Richardsona08b5c92010-06-30 21:59:43 -0700307 Memset(blob, 0, bp->blob_size);
308 bp->blob = blob;
Randall Spangler7d6898d2010-06-11 09:22:13 -0700309
310 /* Copy the 32-bit kernel. */
Bill Richardson249677d2010-06-23 11:16:37 -0700311 Debug("kernel goes at blob+=0x%" PRIx64 "\n", now);
Randall Spangler7d6898d2010-06-11 09:22:13 -0700312 if (kernel32_size)
313 Memcpy(blob + now, kernel_buf + kernel32_start, kernel32_size);
314 now += roundup(now + kernel32_size, CROS_ALIGN);
315
Bill Richardson249677d2010-06-23 11:16:37 -0700316 Debug("config goes at blob+0x%" PRIx64 "\n", now);
Randall Spangler7d6898d2010-06-11 09:22:13 -0700317 /* Find the load address of the commandline. We'll need it later. */
318 cmdline_addr = CROS_32BIT_ENTRY_ADDR + now +
319 find_cmdline_start((char *)config_buf, config_size);
Bill Richardson249677d2010-06-23 11:16:37 -0700320 Debug(" cmdline_addr=0x%" PRIx64 "\n", cmdline_addr);
Randall Spangler7d6898d2010-06-11 09:22:13 -0700321
322 /* Copy the config. */
323 if (config_size)
324 Memcpy(blob + now, config_buf, config_size);
325 now += CROS_CONFIG_SIZE;
326
327 /* The zeropage data is next. Overlay the linux_kernel_header onto it, and
328 * tweak a few fields. */
Bill Richardson249677d2010-06-23 11:16:37 -0700329 Debug("params goes at blob+=0x%" PRIx64 "\n", now);
Randall Spangler7d6898d2010-06-11 09:22:13 -0700330 params = (struct linux_kernel_params *)(blob + now);
331 Memcpy(&(params->setup_sects), &(lh->setup_sects),
332 sizeof(*lh) - offsetof(struct linux_kernel_header, setup_sects));
333 params->boot_flag = 0;
334 params->ramdisk_image = 0; /* we don't support initrd */
335 params->ramdisk_size = 0;
336 params->type_of_loader = 0xff;
337 params->cmd_line_ptr = cmdline_addr;
338 now += CROS_PARAMS_SIZE;
339
340 /* Finally, append the bootloader. Remember where it will load in
341 * memory, too. */
Bill Richardson249677d2010-06-23 11:16:37 -0700342 Debug("bootloader goes at blob+=0x%" PRIx64 "\n", now);
Bill Richardsona08b5c92010-06-30 21:59:43 -0700343 bp->bootloader_address = CROS_32BIT_ENTRY_ADDR + now;
344 bp->bootloader_size = roundup(bootloader_size, CROS_ALIGN);
345 Debug(" bootloader_address=0x%" PRIx64 "\n", bp->bootloader_address);
346 Debug(" bootloader_size=0x%" PRIx64 "\n", bp->bootloader_size);
Randall Spangler7d6898d2010-06-11 09:22:13 -0700347 if (bootloader_size)
348 Memcpy(blob + now, bootloader_buf, bootloader_size);
Bill Richardsona08b5c92010-06-30 21:59:43 -0700349 now += bp->bootloader_size;
Bill Richardson249677d2010-06-23 11:16:37 -0700350 Debug("end of blob is 0x%" PRIx64 "\n", now);
Randall Spangler7d6898d2010-06-11 09:22:13 -0700351
352 /* Free input buffers */
353 Free(kernel_buf);
354 Free(config_buf);
355 Free(bootloader_buf);
356
Bill Richardsona08b5c92010-06-30 21:59:43 -0700357 /* Success */
358 return bp;
359}
360
361
362/* Pull the blob_t stuff out of a prepacked kernel blob file */
363static blob_t *OldBlob(const char* filename) {
vbendebb2b0fcc2010-07-15 15:09:47 -0700364 FILE* fp = NULL;
365 blob_t *bp = NULL;
Bill Richardsona08b5c92010-06-30 21:59:43 -0700366 struct stat statbuf;
367 VbKeyBlockHeader* key_block;
368 VbKernelPreambleHeader* preamble;
369 uint64_t now = 0;
vbendebb2b0fcc2010-07-15 15:09:47 -0700370 uint8_t* buf = NULL;
371 int ret_error = 1;
Bill Richardsona08b5c92010-06-30 21:59:43 -0700372
373 if (!filename) {
374 error("Must specify prepacked blob to read\n");
375 return 0;
376 }
377
378 if (0 != stat(filename, &statbuf)) {
379 error("unable to stat %s: %s\n", filename, strerror(errno));
380 return 0;
381 }
382
383 Debug("%s size is 0x%" PRIx64 "\n", filename, statbuf.st_size);
384 if (statbuf.st_size < DEFAULT_PADDING) {
385 error("%s is too small to be a valid kernel blob\n");
386 return 0;
387 }
388
389 Debug("Reading %s\n", filename);
390 fp = fopen(filename, "rb");
391 if (!fp) {
392 error("Unable to open file %s: %s\n", filename, strerror(errno));
393 return 0;
394 }
395
vbendebb2b0fcc2010-07-15 15:09:47 -0700396 buf = Malloc(DEFAULT_PADDING);
397 if (!buf) {
398 error("Unable to allocate padding\n");
399 goto unwind_oldblob;
400 }
401
402 if (1 != fread(buf, DEFAULT_PADDING, 1, fp)) {
Bill Richardsona08b5c92010-06-30 21:59:43 -0700403 error("Unable to read header from %s: %s\n", filename, strerror(errno));
vbendebb2b0fcc2010-07-15 15:09:47 -0700404 goto unwind_oldblob;
Bill Richardsona08b5c92010-06-30 21:59:43 -0700405 }
406
407 /* Skip the key block */
408 key_block = (VbKeyBlockHeader*)buf;
409 Debug("Keyblock is 0x%" PRIx64 " bytes\n", key_block->key_block_size);
410 now += key_block->key_block_size;
411 if (now > statbuf.st_size) {
412 error("key_block_size advances past the end of the blob\n");
vbendebb2b0fcc2010-07-15 15:09:47 -0700413 goto unwind_oldblob;
Bill Richardsona08b5c92010-06-30 21:59:43 -0700414 }
415
416 /* Skip the preamble */
417 preamble = (VbKernelPreambleHeader*)(buf + now);
418 Debug("Preamble is 0x%" PRIx64 " bytes\n", preamble->preamble_size);
419 now += preamble->preamble_size;
420 if (now > statbuf.st_size) {
421 error("preamble_size advances past the end of the blob\n");
vbendebb2b0fcc2010-07-15 15:09:47 -0700422 goto unwind_oldblob;
Bill Richardsona08b5c92010-06-30 21:59:43 -0700423 }
424
425 /* Go find the kernel blob */
426 Debug("kernel blob is at offset 0x%" PRIx64 "\n", now);
427 if (0 != fseek(fp, now, SEEK_SET)) {
428 error("Unable to seek to 0x%" PRIx64 " in %s: %s\n", now, filename,
429 strerror(errno));
vbendebb2b0fcc2010-07-15 15:09:47 -0700430 goto unwind_oldblob;
Bill Richardsona08b5c92010-06-30 21:59:43 -0700431 }
432
433 /* Remember what we've got */
434 bp = (blob_t *)Malloc(sizeof(blob_t));
435 if (!bp) {
436 error("Couldn't allocate bytes for blob_t.\n");
vbendebb2b0fcc2010-07-15 15:09:47 -0700437 goto unwind_oldblob;
Bill Richardsona08b5c92010-06-30 21:59:43 -0700438 }
439
vbendebb2b0fcc2010-07-15 15:09:47 -0700440 bp->buf = buf;
441 bp->key_block = key_block;
442 bp->preamble = preamble;
443
Bill Richardsona08b5c92010-06-30 21:59:43 -0700444 bp->kernel_version = preamble->kernel_version;
445 bp->bootloader_address = preamble->bootloader_address;
446 bp->bootloader_size = preamble->bootloader_size;
447 bp->blob_size = preamble->body_signature.data_size;
448
449 Debug(" kernel_version = %d\n", bp->kernel_version);
450 Debug(" bootloader_address = 0x%" PRIx64 "\n", bp->bootloader_address);
451 Debug(" bootloader_size = 0x%" PRIx64 "\n", bp->bootloader_size);
452 Debug(" blob_size = 0x%" PRIx64 "\n", bp->blob_size);
453
454 bp->blob = (uint8_t *)Malloc(bp->blob_size);
455 if (!bp->blob) {
456 error("Couldn't allocate 0x%" PRIx64 " bytes for blob_t.\n", bp->blob_size);
vbendebb2b0fcc2010-07-15 15:09:47 -0700457 goto unwind_oldblob;
Bill Richardsona08b5c92010-06-30 21:59:43 -0700458 }
459
460 /* read it in */
461 if (1 != fread(bp->blob, bp->blob_size, 1, fp)) {
462 error("Unable to read kernel blob from %s: %s\n", filename, strerror(errno));
vbendebb2b0fcc2010-07-15 15:09:47 -0700463 goto unwind_oldblob;
Bill Richardsona08b5c92010-06-30 21:59:43 -0700464 }
465
vbendebb2b0fcc2010-07-15 15:09:47 -0700466 ret_error = 0;
Bill Richardsona08b5c92010-06-30 21:59:43 -0700467
vbendebb2b0fcc2010-07-15 15:09:47 -0700468 /* done */
469unwind_oldblob:
470 fclose(fp);
471 if (ret_error) {
472 if (bp) {
473 FreeBlob(bp);
474 bp = NULL;
475 } else if (buf) {
476 Free(buf);
477 }
478 }
Bill Richardsona08b5c92010-06-30 21:59:43 -0700479 return bp;
480}
481
482
483/* Pack a .kernel */
484static int Pack(const char* outfile, const char* keyblock_file,
485 const char* signprivate, blob_t *bp, uint64_t pad,
486 int vblockonly) {
487 VbPrivateKey* signing_key;
488 VbSignature* body_sig;
489 VbKernelPreambleHeader* preamble;
490 VbKeyBlockHeader* key_block;
491 uint64_t key_block_size;
492 FILE* f;
493 uint64_t i;
494
495 if (!outfile) {
496 error("Must specify output filename\n");
497 return 1;
498 }
vbendebb2b0fcc2010-07-15 15:09:47 -0700499 if ((!keyblock_file && !bp->key_block) || !signprivate) {
Bill Richardsona08b5c92010-06-30 21:59:43 -0700500 error("Must specify all keys\n");
501 return 1;
502 }
503 if (!bp) {
504 error("Refusing to pack invalid kernel blob\n");
505 return 1;
Bill Richardsona08b5c92010-06-30 21:59:43 -0700506 }
vbendebb2b0fcc2010-07-15 15:09:47 -0700507
508 /* Get the key block and read the private key. */
509 if (keyblock_file) {
510 key_block = (VbKeyBlockHeader*)ReadFile(keyblock_file, &key_block_size);
511 if (!key_block) {
512 error("Error reading key block.\n");
513 return 1;
514 }
515 } else {
516 key_block = bp->key_block;
517 key_block_size = key_block->key_block_size;
518 }
519
Bill Richardsona08b5c92010-06-30 21:59:43 -0700520 if (pad < key_block->key_block_size) {
521 error("Pad too small\n");
522 return 1;
523 }
524
Bill Richardsonabf05502010-07-01 10:22:06 -0700525 signing_key = PrivateKeyRead(signprivate);
Bill Richardsona08b5c92010-06-30 21:59:43 -0700526 if (!signing_key) {
527 error("Error reading signing key.\n");
528 return 1;
529 }
530
Randall Spangler7d6898d2010-06-11 09:22:13 -0700531 /* Sign the kernel data */
Bill Richardsona08b5c92010-06-30 21:59:43 -0700532 body_sig = CalculateSignature(bp->blob, bp->blob_size, signing_key);
Randall Spangler7d6898d2010-06-11 09:22:13 -0700533 if (!body_sig) {
534 error("Error calculating body signature\n");
535 return 1;
536 }
537
538 /* Create preamble */
Bill Richardsona08b5c92010-06-30 21:59:43 -0700539 preamble = CreateKernelPreamble(bp->kernel_version,
Randall Spangler7d6898d2010-06-11 09:22:13 -0700540 CROS_32BIT_ENTRY_ADDR,
Bill Richardsona08b5c92010-06-30 21:59:43 -0700541 bp->bootloader_address,
542 bp->bootloader_size,
Randall Spangler7d6898d2010-06-11 09:22:13 -0700543 body_sig,
544 pad - key_block_size,
545 signing_key);
546 if (!preamble) {
547 error("Error creating preamble.\n");
548 return 1;
549 }
550
551 /* Write the output file */
Bill Richardson249677d2010-06-23 11:16:37 -0700552 Debug("writing %s...\n", outfile);
Randall Spangler7d6898d2010-06-11 09:22:13 -0700553 f = fopen(outfile, "wb");
554 if (!f) {
555 error("Can't open output file %s\n", outfile);
556 return 1;
557 }
Bill Richardson249677d2010-06-23 11:16:37 -0700558 Debug("0x%" PRIx64 " bytes of key_block\n", key_block_size);
559 Debug("0x%" PRIx64 " bytes of preamble\n", preamble->preamble_size);
Randall Spangler7d6898d2010-06-11 09:22:13 -0700560 i = ((1 != fwrite(key_block, key_block_size, 1, f)) ||
Bill Richardsona08b5c92010-06-30 21:59:43 -0700561 (1 != fwrite(preamble, preamble->preamble_size, 1, f)));
Randall Spangler7d6898d2010-06-11 09:22:13 -0700562 if (i) {
563 error("Can't write output file %s\n", outfile);
Bill Richardsona08b5c92010-06-30 21:59:43 -0700564 fclose(f);
Randall Spangler7d6898d2010-06-11 09:22:13 -0700565 unlink(outfile);
566 return 1;
567 }
568
Bill Richardsona08b5c92010-06-30 21:59:43 -0700569 if (!vblockonly) {
570 Debug("0x%" PRIx64 " bytes of blob\n", bp->blob_size);
571 i = (1 != fwrite(bp->blob, bp->blob_size, 1, f));
572 if (i) {
573 error("Can't write output file %s\n", outfile);
574 fclose(f);
575 unlink(outfile);
576 return 1;
577 }
578 }
579
580 fclose(f);
581
Randall Spangler7d6898d2010-06-11 09:22:13 -0700582 /* Success */
583 return 0;
584}
585
vbendebb2b0fcc2010-07-15 15:09:47 -0700586/*
587 * Replace kernel command line in a blob representing a kernel.
588 */
589static int ReplaceConfig(blob_t* bp, const char* config_file)
590{
591 uint8_t* new_conf;
592 uint64_t config_size;
Randall Spangler7d6898d2010-06-11 09:22:13 -0700593
vbendebb2b0fcc2010-07-15 15:09:47 -0700594 if (!config_file) {
595 return 0;
596 }
597
598 new_conf = ReadConfigFile(config_file, &config_size);
599 if (!new_conf) {
600 return 1;
601 }
602
603 /* fill the config buffer with zeros */
604 Memset(BpCmdLineLocation(bp), 0, CROS_CONFIG_SIZE);
605 Memcpy(BpCmdLineLocation(bp), new_conf, config_size);
606 Free(new_conf);
607 return 0;
608}
609
610static int Verify(const char* infile, const char* signpubkey, int verbose) {
Randall Spangler7d6898d2010-06-11 09:22:13 -0700611
612 VbKeyBlockHeader* key_block;
613 VbKernelPreambleHeader* preamble;
614 VbPublicKey* data_key;
Bill Richardson4f36ef32010-08-09 17:50:14 -0700615 VbPublicKey* sign_key = NULL;
Randall Spangler7d6898d2010-06-11 09:22:13 -0700616 RSAPublicKey* rsa;
vbendebb2b0fcc2010-07-15 15:09:47 -0700617 blob_t* bp;
618 uint64_t now;
619 int rv = 1;
Randall Spangler7d6898d2010-06-11 09:22:13 -0700620
Bill Richardson4f36ef32010-08-09 17:50:14 -0700621 if (!infile) {
622 error("Must specify filename\n");
Randall Spangler7d6898d2010-06-11 09:22:13 -0700623 return 1;
624 }
625
626 /* Read public signing key */
Bill Richardson4f36ef32010-08-09 17:50:14 -0700627 if (signpubkey) {
628 sign_key = PublicKeyRead(signpubkey);
629 if (!sign_key) {
630 error("Error reading signpubkey.\n");
631 return 1;
632 }
Randall Spangler7d6898d2010-06-11 09:22:13 -0700633 }
634
635 /* Read blob */
vbendebb2b0fcc2010-07-15 15:09:47 -0700636 bp = OldBlob(infile);
637 if (!bp) {
Randall Spangler7d6898d2010-06-11 09:22:13 -0700638 error("Error reading input file\n");
639 return 1;
640 }
641
642 /* Verify key block */
vbendebb2b0fcc2010-07-15 15:09:47 -0700643 key_block = bp->key_block;
Randall Spangler138acfe2010-08-17 15:45:21 -0700644 if (0 != KeyBlockVerify(key_block, bp->blob_size, sign_key,
645 (sign_key ? 0 : 1))) {
Randall Spangler7d6898d2010-06-11 09:22:13 -0700646 error("Error verifying key block.\n");
vbendebb2b0fcc2010-07-15 15:09:47 -0700647 goto verify_exit;
Randall Spangler7d6898d2010-06-11 09:22:13 -0700648 }
vbendebb2b0fcc2010-07-15 15:09:47 -0700649 now = key_block->key_block_size;
Randall Spangler7d6898d2010-06-11 09:22:13 -0700650
651 printf("Key block:\n");
652 data_key = &key_block->data_key;
Bill Richardson4f36ef32010-08-09 17:50:14 -0700653 if (verbose)
654 printf(" Signature: %s\n", sign_key ? "valid" : "ignored");
Bill Richardsona08b5c92010-06-30 21:59:43 -0700655 printf(" Size: 0x%" PRIx64 "\n", key_block->key_block_size);
Randall Spangler7d6898d2010-06-11 09:22:13 -0700656 printf(" Data key algorithm: %" PRIu64 " %s\n", data_key->algorithm,
657 (data_key->algorithm < kNumAlgorithms ?
658 algo_strings[data_key->algorithm] : "(invalid)"));
659 printf(" Data key version: %" PRIu64 "\n", data_key->key_version);
660 printf(" Flags: %" PRIu64 "\n", key_block->key_block_flags);
661
662 rsa = PublicKeyToRSA(&key_block->data_key);
663 if (!rsa) {
664 error("Error parsing data key.\n");
vbendebb2b0fcc2010-07-15 15:09:47 -0700665 goto verify_exit;
Randall Spangler7d6898d2010-06-11 09:22:13 -0700666 }
667
668 /* Verify preamble */
vbendebb2b0fcc2010-07-15 15:09:47 -0700669 preamble = bp->preamble;
Randall Spangler87c13d82010-07-19 10:35:40 -0700670 if (0 != VerifyKernelPreamble(
Bill Richardson4f36ef32010-08-09 17:50:14 -0700671 preamble, bp->blob_size - key_block->key_block_size, rsa)) {
Randall Spangler7d6898d2010-06-11 09:22:13 -0700672 error("Error verifying preamble.\n");
vbendebb2b0fcc2010-07-15 15:09:47 -0700673 goto verify_exit;
Randall Spangler7d6898d2010-06-11 09:22:13 -0700674 }
675 now += preamble->preamble_size;
676
677 printf("Preamble:\n");
Bill Richardsona08b5c92010-06-30 21:59:43 -0700678 printf(" Size: 0x%" PRIx64 "\n", preamble->preamble_size);
Randall Spangler7d6898d2010-06-11 09:22:13 -0700679 printf(" Header version: %" PRIu32 ".%" PRIu32"\n",
680 preamble->header_version_major, preamble->header_version_minor);
681 printf(" Kernel version: %" PRIu64 "\n", preamble->kernel_version);
Bill Richardson249677d2010-06-23 11:16:37 -0700682 printf(" Body load address: 0x%" PRIx64 "\n", preamble->body_load_address);
683 printf(" Body size: 0x%" PRIx64 "\n",
Randall Spangler7d6898d2010-06-11 09:22:13 -0700684 preamble->body_signature.data_size);
Randall Spangler87c13d82010-07-19 10:35:40 -0700685 printf(" Bootloader address: 0x%" PRIx64 "\n",
686 preamble->bootloader_address);
Bill Richardson249677d2010-06-23 11:16:37 -0700687 printf(" Bootloader size: 0x%" PRIx64 "\n", preamble->bootloader_size);
Randall Spangler7d6898d2010-06-11 09:22:13 -0700688
689 /* Verify body */
Randall Spangler87c13d82010-07-19 10:35:40 -0700690 if (0 != VerifyData(bp->blob, bp->blob_size, &preamble->body_signature,
691 rsa)) {
Randall Spangler7d6898d2010-06-11 09:22:13 -0700692 error("Error verifying kernel body.\n");
vbendebb2b0fcc2010-07-15 15:09:47 -0700693 goto verify_exit;
Randall Spangler7d6898d2010-06-11 09:22:13 -0700694 }
695 printf("Body verification succeeded.\n");
vbendebb2b0fcc2010-07-15 15:09:47 -0700696
697 rv = 0;
698
699 if (!verbose) {
700 goto verify_exit;
701 }
702
703 printf("Config:\n%s\n", BpCmdLineLocation(bp));
704
705verify_exit:
706 FreeBlob(bp);
707 return rv;
Randall Spangler7d6898d2010-06-11 09:22:13 -0700708}
709
710
711int main(int argc, char* argv[]) {
Randall Spangler7d6898d2010-06-11 09:22:13 -0700712 char* filename = NULL;
Bill Richardsona08b5c92010-06-30 21:59:43 -0700713 char* oldfile = NULL;
Randall Spangler7d6898d2010-06-11 09:22:13 -0700714 char* key_block_file = NULL;
715 char* signpubkey = NULL;
716 char* signprivate = NULL;
717 uint64_t version = 0;
718 char* vmlinuz = NULL;
719 char* bootloader = NULL;
720 char* config_file = NULL;
Bill Richardsona08b5c92010-06-30 21:59:43 -0700721 int vblockonly = 0;
vbendebb2b0fcc2010-07-15 15:09:47 -0700722 int verbose = 0;
Bill Richardsona08b5c92010-06-30 21:59:43 -0700723 uint64_t pad = DEFAULT_PADDING;
Randall Spangler7d6898d2010-06-11 09:22:13 -0700724 int mode = 0;
725 int parse_error = 0;
726 char* e;
Bill Richardsona08b5c92010-06-30 21:59:43 -0700727 int i,r;
728 blob_t *bp;
Randall Spangler7d6898d2010-06-11 09:22:13 -0700729
Bill Richardsona08b5c92010-06-30 21:59:43 -0700730
731 char *progname = strrchr(argv[0], '/');
732 if (progname)
733 progname++;
734 else
735 progname = argv[0];
736
vbendebb2b0fcc2010-07-15 15:09:47 -0700737 while (((i = getopt_long(argc, argv, ":", long_opts, NULL)) != -1) &&
738 !parse_error) {
Randall Spangler7d6898d2010-06-11 09:22:13 -0700739 switch (i) {
vbendebb2b0fcc2010-07-15 15:09:47 -0700740 default:
Randall Spangler7d6898d2010-06-11 09:22:13 -0700741 case '?':
742 /* Unhandled option */
Randall Spangler7d6898d2010-06-11 09:22:13 -0700743 parse_error = 1;
744 break;
745
Bill Richardson4f36ef32010-08-09 17:50:14 -0700746 case 0:
747 /* silently handled option */
748 break;
749
Randall Spangler7d6898d2010-06-11 09:22:13 -0700750 case OPT_MODE_PACK:
Bill Richardsona08b5c92010-06-30 21:59:43 -0700751 case OPT_MODE_REPACK:
Randall Spangler7d6898d2010-06-11 09:22:13 -0700752 case OPT_MODE_VERIFY:
vbendebb2b0fcc2010-07-15 15:09:47 -0700753 if (mode && (mode != i)) {
754 fprintf(stderr, "Only single mode can be specified\n");
755 parse_error = 1;
756 break;
757 }
Randall Spangler7d6898d2010-06-11 09:22:13 -0700758 mode = i;
759 filename = optarg;
760 break;
761
Bill Richardsona08b5c92010-06-30 21:59:43 -0700762 case OPT_OLDBLOB:
763 oldfile = optarg;
764 break;
765
Randall Spangler7d6898d2010-06-11 09:22:13 -0700766 case OPT_KEYBLOCK:
767 key_block_file = optarg;
768 break;
769
770 case OPT_SIGNPUBKEY:
771 signpubkey = optarg;
772 break;
773
774 case OPT_SIGNPRIVATE:
775 signprivate = optarg;
776 break;
777
778 case OPT_VMLINUZ:
779 vmlinuz = optarg;
780 break;
781
782 case OPT_BOOTLOADER:
783 bootloader = optarg;
784 break;
785
786 case OPT_CONFIG:
787 config_file = optarg;
788 break;
789
Bill Richardsona08b5c92010-06-30 21:59:43 -0700790 case OPT_VBLOCKONLY:
791 vblockonly = 1;
792 break;
793
Randall Spangler7d6898d2010-06-11 09:22:13 -0700794 case OPT_VERSION:
795 version = strtoul(optarg, &e, 0);
796 if (!*optarg || (e && *e)) {
Bill Richardsona08b5c92010-06-30 21:59:43 -0700797 fprintf(stderr, "Invalid --version\n");
Randall Spangler7d6898d2010-06-11 09:22:13 -0700798 parse_error = 1;
799 }
800 break;
801
802 case OPT_PAD:
803 pad = strtoul(optarg, &e, 0);
804 if (!*optarg || (e && *e)) {
Bill Richardsona08b5c92010-06-30 21:59:43 -0700805 fprintf(stderr, "Invalid --pad\n");
Randall Spangler7d6898d2010-06-11 09:22:13 -0700806 parse_error = 1;
807 }
808 break;
vbendebb2b0fcc2010-07-15 15:09:47 -0700809
810 case OPT_VERBOSE:
811 verbose = 1;
812 break;
Randall Spangler7d6898d2010-06-11 09:22:13 -0700813 }
814 }
815
816 if (parse_error)
Bill Richardsona08b5c92010-06-30 21:59:43 -0700817 return PrintHelp(progname);
Randall Spangler7d6898d2010-06-11 09:22:13 -0700818
819 switch(mode) {
820 case OPT_MODE_PACK:
Bill Richardsona08b5c92010-06-30 21:59:43 -0700821 bp = NewBlob(version, vmlinuz, bootloader, config_file);
822 if (!bp)
823 return 1;
824 r = Pack(filename, key_block_file, signprivate, bp, pad, vblockonly);
825 FreeBlob(bp);
826 return r;
827
828 case OPT_MODE_REPACK:
vbendebb2b0fcc2010-07-15 15:09:47 -0700829 if (!config_file && !key_block_file) {
830 fprintf(stderr,
831 "You must supply at least one of --config and --keyblock\n");
832 return 1;
833 }
834
Bill Richardsona08b5c92010-06-30 21:59:43 -0700835 bp = OldBlob(oldfile);
836 if (!bp)
837 return 1;
vbendebb2b0fcc2010-07-15 15:09:47 -0700838 r = ReplaceConfig(bp, config_file);
839 if (!r) {
840 r = Pack(filename, key_block_file, signprivate, bp, pad, vblockonly);
841 }
Bill Richardsona08b5c92010-06-30 21:59:43 -0700842 FreeBlob(bp);
843 return r;
844
Randall Spangler7d6898d2010-06-11 09:22:13 -0700845 case OPT_MODE_VERIFY:
vbendebb2b0fcc2010-07-15 15:09:47 -0700846 return Verify(filename, signpubkey, verbose);
Bill Richardsona08b5c92010-06-30 21:59:43 -0700847
Randall Spangler7d6898d2010-06-11 09:22:13 -0700848 default:
Bill Richardsona08b5c92010-06-30 21:59:43 -0700849 fprintf(stderr,
850 "You must specify a mode: --pack, --repack or --verify\n");
851 return PrintHelp(progname);
Randall Spangler7d6898d2010-06-11 09:22:13 -0700852 }
853}