blob: c2b32d877bcd5c2938384c03c27087aca34111d9 [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"
vbendeb858fffb2010-10-06 09:51:44 -070094 " Required parameters (of --keyblock, --config, and --version \n"
95 " at least 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"
vbendeb858fffb2010-10-06 09:51:44 -0700101 " --version <number> Kernel version\n"
Bill Richardsona08b5c92010-06-30 21:59:43 -0700102 "\n"
103 " Optional:\n"
104 " --pad <number> Verification padding size in bytes\n"
105 " --vblockonly Emit just the verification blob\n",
106 progname);
107 fprintf(stderr,
108 "\nOR\n\n"
109 "Usage: %s --verify <file> [PARAMETERS]\n"
110 "\n"
vbendebb2b0fcc2010-07-15 15:09:47 -0700111 " Optional:\n"
Bill Richardson4f36ef32010-08-09 17:50:14 -0700112 " --signpubkey <file>"
113 " Public key to verify kernel keyblock, in .vbpubk format\n"
vbendebb2b0fcc2010-07-15 15:09:47 -0700114 " --verbose Print a more detailed report\n"
Will Drewry9342f882010-10-26 10:22:05 -0500115 " --keyblock <file>"
116 " Outputs the verified key block, in .keyblock format\n"
Bill Richardsona08b5c92010-06-30 21:59:43 -0700117 "\n",
118 progname);
Randall Spangler7d6898d2010-06-11 09:22:13 -0700119 return 1;
120}
121
Bill Richardson249677d2010-06-23 11:16:37 -0700122static void Debug(const char *format, ...) {
123 if (!opt_debug)
124 return;
125
126 va_list ap;
127 va_start(ap, format);
128 fprintf(stderr, "DEBUG: ");
129 vfprintf(stderr, format, ap);
130 va_end(ap);
131}
132
Bill Richardson2f6a71f2010-10-14 09:25:39 -0700133/* Return an explanation when fread() fails. */
134static const char *error_fread(FILE *fp) {
135 const char *retval = "beats me why";
136 if (feof(fp))
137 retval = "EOF";
138 else if (ferror(fp))
139 retval = strerror(errno);
140 clearerr(fp);
141 return retval;
142}
Randall Spangler7d6898d2010-06-11 09:22:13 -0700143
144/* Return the smallest integral multiple of [alignment] that is equal
145 * to or greater than [val]. Used to determine the number of
146 * pages/sectors/blocks/whatever needed to contain [val]
147 * items/bytes/etc. */
148static uint64_t roundup(uint64_t val, uint64_t alignment) {
149 uint64_t rem = val % alignment;
150 if ( rem )
151 return val + (alignment - rem);
152 return val;
153}
154
155
156/* Match regexp /\b--\b/ to delimit the start of the kernel commandline. If we
157 * don't find one, we'll use the whole thing. */
158static unsigned int find_cmdline_start(char *input, unsigned int max_len) {
159 int start = 0;
160 int i;
161 for(i = 0; i < max_len - 1 && input[i]; i++) {
162 if ('-' == input[i] && '-' == input[i + 1]) { /* found a "--" */
163 if ((i == 0 || ' ' == input[i - 1]) && /* nothing before it */
164 (i + 2 >= max_len || ' ' == input[i+2])) { /* nothing after it */
165 start = i+2; /* note: hope there's a trailing '\0' */
166 break;
167 }
168 }
169 }
170 while(' ' == input[start]) /* skip leading spaces */
171 start++;
172
173 return start;
174}
175
176
Bill Richardsona08b5c92010-06-30 21:59:43 -0700177typedef struct blob_s {
178 /* Stuff needed by VbKernelPreambleHeader */
179 uint64_t kernel_version;
180 uint64_t bootloader_address;
181 uint64_t bootloader_size;
182 /* Raw kernel blob data */
183 uint64_t blob_size;
184 uint8_t *blob;
vbendebb2b0fcc2010-07-15 15:09:47 -0700185
186 /* these fields are not always initialized */
187 VbKernelPreambleHeader* preamble;
188 VbKeyBlockHeader* key_block;
189 uint8_t *buf;
190
Bill Richardsona08b5c92010-06-30 21:59:43 -0700191} blob_t;
Randall Spangler7d6898d2010-06-11 09:22:13 -0700192
vbendebb2b0fcc2010-07-15 15:09:47 -0700193/* Given a blob return the location of the kernel command line buffer. */
194static char* BpCmdLineLocation(blob_t *bp)
195{
196 return (char*)(bp->blob + bp->bootloader_address - CROS_32BIT_ENTRY_ADDR -
197 CROS_CONFIG_SIZE - CROS_PARAMS_SIZE);
198}
Bill Richardsona08b5c92010-06-30 21:59:43 -0700199
200static void FreeBlob(blob_t *bp) {
201 if (bp) {
202 if (bp->blob)
203 Free(bp->blob);
vbendebb2b0fcc2010-07-15 15:09:47 -0700204 if (bp->buf)
205 Free(bp->buf);
Bill Richardsona08b5c92010-06-30 21:59:43 -0700206 Free(bp);
207 }
208}
209
vbendebb2b0fcc2010-07-15 15:09:47 -0700210/*
211 * Read the kernel command line from a file. Get rid of \n characters along
212 * the way and verify that the line fits into a 4K buffer.
213 *
214 * Return the buffer contaning the line on success (and set the line length
215 * using the passed in parameter), or NULL in case something goes wrong.
216 */
217static uint8_t* ReadConfigFile(const char* config_file, uint64_t* config_size)
218{
219 uint8_t* config_buf;
220 int ii;
221
222 config_buf = ReadFile(config_file, config_size);
223 Debug(" config file size=0x%" PRIx64 "\n", *config_size);
224 if (CROS_CONFIG_SIZE <= *config_size) { /* need room for trailing '\0' */
225 error("Config file %s is too large (>= %d bytes)\n",
226 config_file, CROS_CONFIG_SIZE);
227 return NULL;
228 }
229
230 /* Replace newlines with spaces */
231 for (ii = 0; ii < *config_size; ii++) {
232 if ('\n' == config_buf[ii]) {
233 config_buf[ii] = ' ';
234 }
235 }
236 return config_buf;
237}
238
Bill Richardsona08b5c92010-06-30 21:59:43 -0700239/* Create a blob from its components */
240static blob_t *NewBlob(uint64_t version,
241 const char* vmlinuz,
242 const char* bootloader_file,
243 const char* config_file) {
244 blob_t *bp;
Randall Spangler7d6898d2010-06-11 09:22:13 -0700245 struct linux_kernel_header *lh = 0;
246 struct linux_kernel_params *params = 0;
Randall Spangler7d6898d2010-06-11 09:22:13 -0700247 uint8_t* config_buf;
248 uint64_t config_size;
249 uint8_t* bootloader_buf;
250 uint64_t bootloader_size;
Randall Spangler7d6898d2010-06-11 09:22:13 -0700251 uint8_t* kernel_buf;
252 uint64_t kernel_size;
253 uint64_t kernel32_start = 0;
254 uint64_t kernel32_size = 0;
255 uint32_t cmdline_addr;
256 uint8_t* blob = NULL;
Randall Spangler7d6898d2010-06-11 09:22:13 -0700257 uint64_t now = 0;
Randall Spangler7d6898d2010-06-11 09:22:13 -0700258
Randall Spangler7d6898d2010-06-11 09:22:13 -0700259 if (!vmlinuz || !bootloader_file || !config_file) {
260 error("Must specify all input files\n");
Bill Richardsona08b5c92010-06-30 21:59:43 -0700261 return 0;
Randall Spangler7d6898d2010-06-11 09:22:13 -0700262 }
263
Bill Richardsona08b5c92010-06-30 21:59:43 -0700264 bp = (blob_t *)Malloc(sizeof(blob_t));
265 if (!bp) {
266 error("Couldn't allocate bytes for blob_t.\n");
267 return 0;
Randall Spangler7d6898d2010-06-11 09:22:13 -0700268 }
vbendebb2b0fcc2010-07-15 15:09:47 -0700269
270 Memset(bp, 0, sizeof(*bp));
Bill Richardsona08b5c92010-06-30 21:59:43 -0700271 bp->kernel_version = version;
Randall Spangler7d6898d2010-06-11 09:22:13 -0700272
273 /* Read the config file */
Bill Richardson249677d2010-06-23 11:16:37 -0700274 Debug("Reading %s\n", config_file);
vbendebb2b0fcc2010-07-15 15:09:47 -0700275 config_buf = ReadConfigFile(config_file, &config_size);
Randall Spangler7d6898d2010-06-11 09:22:13 -0700276 if (!config_buf)
Bill Richardsona08b5c92010-06-30 21:59:43 -0700277 return 0;
Randall Spangler7d6898d2010-06-11 09:22:13 -0700278
279 /* Read the bootloader */
Bill Richardson249677d2010-06-23 11:16:37 -0700280 Debug("Reading %s\n", bootloader_file);
Randall Spangler7d6898d2010-06-11 09:22:13 -0700281 bootloader_buf = ReadFile(bootloader_file, &bootloader_size);
282 if (!bootloader_buf)
Bill Richardsona08b5c92010-06-30 21:59:43 -0700283 return 0;
Bill Richardson249677d2010-06-23 11:16:37 -0700284 Debug(" bootloader file size=0x%" PRIx64 "\n", bootloader_size);
Randall Spangler7d6898d2010-06-11 09:22:13 -0700285
286 /* Read the kernel */
Bill Richardson249677d2010-06-23 11:16:37 -0700287 Debug("Reading %s\n", vmlinuz);
Randall Spangler7d6898d2010-06-11 09:22:13 -0700288 kernel_buf = ReadFile(vmlinuz, &kernel_size);
289 if (!kernel_buf)
Bill Richardsona08b5c92010-06-30 21:59:43 -0700290 return 0;
Bill Richardson249677d2010-06-23 11:16:37 -0700291 Debug(" kernel file size=0x%" PRIx64 "\n", kernel_size);
Randall Spangler7d6898d2010-06-11 09:22:13 -0700292 if (!kernel_size) {
293 error("Empty kernel file\n");
Bill Richardsona08b5c92010-06-30 21:59:43 -0700294 return 0;
Randall Spangler7d6898d2010-06-11 09:22:13 -0700295 }
296
297 /* The first part of vmlinuz is a header, followed by a real-mode
298 * boot stub. We only want the 32-bit part. */
299 lh = (struct linux_kernel_header *)kernel_buf;
300 kernel32_start = (lh->setup_sects + 1) << 9;
301 if (kernel32_start >= kernel_size) {
302 error("Malformed kernel\n");
Bill Richardsona08b5c92010-06-30 21:59:43 -0700303 return 0;
Randall Spangler7d6898d2010-06-11 09:22:13 -0700304 }
305 kernel32_size = kernel_size - kernel32_start;
Bill Richardson249677d2010-06-23 11:16:37 -0700306 Debug(" kernel32_start=0x%" PRIx64 "\n", kernel32_start);
307 Debug(" kernel32_size=0x%" PRIx64 "\n", kernel32_size);
Randall Spangler7d6898d2010-06-11 09:22:13 -0700308
309 /* Allocate and zero the blob we need. */
Bill Richardsona08b5c92010-06-30 21:59:43 -0700310 bp->blob_size = roundup(kernel32_size, CROS_ALIGN) +
Randall Spangler7d6898d2010-06-11 09:22:13 -0700311 CROS_CONFIG_SIZE +
312 CROS_PARAMS_SIZE +
313 roundup(bootloader_size, CROS_ALIGN);
Bill Richardsona08b5c92010-06-30 21:59:43 -0700314 blob = (uint8_t *)Malloc(bp->blob_size);
315 Debug("blob_size=0x%" PRIx64 "\n", bp->blob_size);
Randall Spangler7d6898d2010-06-11 09:22:13 -0700316 if (!blob) {
Bill Richardsona08b5c92010-06-30 21:59:43 -0700317 error("Couldn't allocate %ld bytes.\n", bp->blob_size);
318 return 0;
Randall Spangler7d6898d2010-06-11 09:22:13 -0700319 }
Bill Richardsona08b5c92010-06-30 21:59:43 -0700320 Memset(blob, 0, bp->blob_size);
321 bp->blob = blob;
Randall Spangler7d6898d2010-06-11 09:22:13 -0700322
323 /* Copy the 32-bit kernel. */
Bill Richardson249677d2010-06-23 11:16:37 -0700324 Debug("kernel goes at blob+=0x%" PRIx64 "\n", now);
Randall Spangler7d6898d2010-06-11 09:22:13 -0700325 if (kernel32_size)
326 Memcpy(blob + now, kernel_buf + kernel32_start, kernel32_size);
327 now += roundup(now + kernel32_size, CROS_ALIGN);
328
Bill Richardson249677d2010-06-23 11:16:37 -0700329 Debug("config goes at blob+0x%" PRIx64 "\n", now);
Randall Spangler7d6898d2010-06-11 09:22:13 -0700330 /* Find the load address of the commandline. We'll need it later. */
331 cmdline_addr = CROS_32BIT_ENTRY_ADDR + now +
332 find_cmdline_start((char *)config_buf, config_size);
Bill Richardson249677d2010-06-23 11:16:37 -0700333 Debug(" cmdline_addr=0x%" PRIx64 "\n", cmdline_addr);
Randall Spangler7d6898d2010-06-11 09:22:13 -0700334
335 /* Copy the config. */
336 if (config_size)
337 Memcpy(blob + now, config_buf, config_size);
338 now += CROS_CONFIG_SIZE;
339
340 /* The zeropage data is next. Overlay the linux_kernel_header onto it, and
341 * tweak a few fields. */
Bill Richardson249677d2010-06-23 11:16:37 -0700342 Debug("params goes at blob+=0x%" PRIx64 "\n", now);
Randall Spangler7d6898d2010-06-11 09:22:13 -0700343 params = (struct linux_kernel_params *)(blob + now);
344 Memcpy(&(params->setup_sects), &(lh->setup_sects),
345 sizeof(*lh) - offsetof(struct linux_kernel_header, setup_sects));
346 params->boot_flag = 0;
347 params->ramdisk_image = 0; /* we don't support initrd */
348 params->ramdisk_size = 0;
349 params->type_of_loader = 0xff;
350 params->cmd_line_ptr = cmdline_addr;
Che-Liang Chiou475bf442010-08-23 11:20:44 +0800351 /* A fake e820 memory map with 2 entries */
352 params->n_e820_entry = 2;
353 params->e820_entries[0].start_addr = 0x00000000;
354 params->e820_entries[0].segment_size = 0x00001000;
355 params->e820_entries[0].segment_type = E820_TYPE_RAM;
356 params->e820_entries[1].start_addr = 0xfffff000;
357 params->e820_entries[1].segment_size = 0x00001000;
358 params->e820_entries[1].segment_type = E820_TYPE_RESERVED;
Randall Spangler7d6898d2010-06-11 09:22:13 -0700359 now += CROS_PARAMS_SIZE;
360
361 /* Finally, append the bootloader. Remember where it will load in
362 * memory, too. */
Bill Richardson249677d2010-06-23 11:16:37 -0700363 Debug("bootloader goes at blob+=0x%" PRIx64 "\n", now);
Bill Richardsona08b5c92010-06-30 21:59:43 -0700364 bp->bootloader_address = CROS_32BIT_ENTRY_ADDR + now;
365 bp->bootloader_size = roundup(bootloader_size, CROS_ALIGN);
366 Debug(" bootloader_address=0x%" PRIx64 "\n", bp->bootloader_address);
367 Debug(" bootloader_size=0x%" PRIx64 "\n", bp->bootloader_size);
Randall Spangler7d6898d2010-06-11 09:22:13 -0700368 if (bootloader_size)
369 Memcpy(blob + now, bootloader_buf, bootloader_size);
Bill Richardsona08b5c92010-06-30 21:59:43 -0700370 now += bp->bootloader_size;
Bill Richardson249677d2010-06-23 11:16:37 -0700371 Debug("end of blob is 0x%" PRIx64 "\n", now);
Randall Spangler7d6898d2010-06-11 09:22:13 -0700372
373 /* Free input buffers */
374 Free(kernel_buf);
375 Free(config_buf);
376 Free(bootloader_buf);
377
Bill Richardsona08b5c92010-06-30 21:59:43 -0700378 /* Success */
379 return bp;
380}
381
382
383/* Pull the blob_t stuff out of a prepacked kernel blob file */
384static blob_t *OldBlob(const char* filename) {
vbendebb2b0fcc2010-07-15 15:09:47 -0700385 FILE* fp = NULL;
386 blob_t *bp = NULL;
Bill Richardsona08b5c92010-06-30 21:59:43 -0700387 struct stat statbuf;
388 VbKeyBlockHeader* key_block;
389 VbKernelPreambleHeader* preamble;
390 uint64_t now = 0;
vbendebb2b0fcc2010-07-15 15:09:47 -0700391 uint8_t* buf = NULL;
392 int ret_error = 1;
Bill Richardsona08b5c92010-06-30 21:59:43 -0700393
394 if (!filename) {
395 error("Must specify prepacked blob to read\n");
396 return 0;
397 }
398
399 if (0 != stat(filename, &statbuf)) {
Bill Richardson2f6a71f2010-10-14 09:25:39 -0700400 error("Unable to stat %s: %s\n", filename, strerror(errno));
Bill Richardsona08b5c92010-06-30 21:59:43 -0700401 return 0;
402 }
403
404 Debug("%s size is 0x%" PRIx64 "\n", filename, statbuf.st_size);
405 if (statbuf.st_size < DEFAULT_PADDING) {
406 error("%s is too small to be a valid kernel blob\n");
407 return 0;
408 }
409
410 Debug("Reading %s\n", filename);
411 fp = fopen(filename, "rb");
412 if (!fp) {
413 error("Unable to open file %s: %s\n", filename, strerror(errno));
414 return 0;
415 }
416
vbendebb2b0fcc2010-07-15 15:09:47 -0700417 buf = Malloc(DEFAULT_PADDING);
418 if (!buf) {
419 error("Unable to allocate padding\n");
420 goto unwind_oldblob;
421 }
422
423 if (1 != fread(buf, DEFAULT_PADDING, 1, fp)) {
Bill Richardson2f6a71f2010-10-14 09:25:39 -0700424 error("Unable to read header from %s: %s\n", filename, error_fread(fp));
vbendebb2b0fcc2010-07-15 15:09:47 -0700425 goto unwind_oldblob;
Bill Richardsona08b5c92010-06-30 21:59:43 -0700426 }
427
428 /* Skip the key block */
429 key_block = (VbKeyBlockHeader*)buf;
430 Debug("Keyblock is 0x%" PRIx64 " bytes\n", key_block->key_block_size);
431 now += key_block->key_block_size;
432 if (now > statbuf.st_size) {
433 error("key_block_size advances past the end of the blob\n");
vbendebb2b0fcc2010-07-15 15:09:47 -0700434 goto unwind_oldblob;
Bill Richardsona08b5c92010-06-30 21:59:43 -0700435 }
436
437 /* Skip the preamble */
438 preamble = (VbKernelPreambleHeader*)(buf + now);
439 Debug("Preamble is 0x%" PRIx64 " bytes\n", preamble->preamble_size);
440 now += preamble->preamble_size;
441 if (now > statbuf.st_size) {
442 error("preamble_size advances past the end of the blob\n");
vbendebb2b0fcc2010-07-15 15:09:47 -0700443 goto unwind_oldblob;
Bill Richardsona08b5c92010-06-30 21:59:43 -0700444 }
445
446 /* Go find the kernel blob */
447 Debug("kernel blob is at offset 0x%" PRIx64 "\n", now);
448 if (0 != fseek(fp, now, SEEK_SET)) {
449 error("Unable to seek to 0x%" PRIx64 " in %s: %s\n", now, filename,
450 strerror(errno));
vbendebb2b0fcc2010-07-15 15:09:47 -0700451 goto unwind_oldblob;
Bill Richardsona08b5c92010-06-30 21:59:43 -0700452 }
453
454 /* Remember what we've got */
455 bp = (blob_t *)Malloc(sizeof(blob_t));
456 if (!bp) {
457 error("Couldn't allocate bytes for blob_t.\n");
vbendebb2b0fcc2010-07-15 15:09:47 -0700458 goto unwind_oldblob;
Bill Richardsona08b5c92010-06-30 21:59:43 -0700459 }
460
vbendebb2b0fcc2010-07-15 15:09:47 -0700461 bp->buf = buf;
462 bp->key_block = key_block;
463 bp->preamble = preamble;
464
Bill Richardsona08b5c92010-06-30 21:59:43 -0700465 bp->kernel_version = preamble->kernel_version;
466 bp->bootloader_address = preamble->bootloader_address;
467 bp->bootloader_size = preamble->bootloader_size;
468 bp->blob_size = preamble->body_signature.data_size;
469
470 Debug(" kernel_version = %d\n", bp->kernel_version);
471 Debug(" bootloader_address = 0x%" PRIx64 "\n", bp->bootloader_address);
472 Debug(" bootloader_size = 0x%" PRIx64 "\n", bp->bootloader_size);
473 Debug(" blob_size = 0x%" PRIx64 "\n", bp->blob_size);
474
Bill Richardson2f6a71f2010-10-14 09:25:39 -0700475 if (!bp->blob_size) {
476 error("No kernel blob found\n");
477 goto unwind_oldblob;
478 }
479
Bill Richardsona08b5c92010-06-30 21:59:43 -0700480 bp->blob = (uint8_t *)Malloc(bp->blob_size);
481 if (!bp->blob) {
482 error("Couldn't allocate 0x%" PRIx64 " bytes for blob_t.\n", bp->blob_size);
vbendebb2b0fcc2010-07-15 15:09:47 -0700483 goto unwind_oldblob;
Bill Richardsona08b5c92010-06-30 21:59:43 -0700484 }
485
486 /* read it in */
487 if (1 != fread(bp->blob, bp->blob_size, 1, fp)) {
Bill Richardson2f6a71f2010-10-14 09:25:39 -0700488 error("Unable to read kernel blob from %s: %s\n", filename, error_fread(fp));
vbendebb2b0fcc2010-07-15 15:09:47 -0700489 goto unwind_oldblob;
Bill Richardsona08b5c92010-06-30 21:59:43 -0700490 }
491
vbendebb2b0fcc2010-07-15 15:09:47 -0700492 ret_error = 0;
Bill Richardsona08b5c92010-06-30 21:59:43 -0700493
vbendebb2b0fcc2010-07-15 15:09:47 -0700494 /* done */
495unwind_oldblob:
496 fclose(fp);
497 if (ret_error) {
498 if (bp) {
499 FreeBlob(bp);
500 bp = NULL;
501 } else if (buf) {
502 Free(buf);
503 }
504 }
Bill Richardsona08b5c92010-06-30 21:59:43 -0700505 return bp;
506}
507
508
509/* Pack a .kernel */
510static int Pack(const char* outfile, const char* keyblock_file,
511 const char* signprivate, blob_t *bp, uint64_t pad,
512 int vblockonly) {
513 VbPrivateKey* signing_key;
514 VbSignature* body_sig;
515 VbKernelPreambleHeader* preamble;
516 VbKeyBlockHeader* key_block;
517 uint64_t key_block_size;
518 FILE* f;
519 uint64_t i;
520
521 if (!outfile) {
522 error("Must specify output filename\n");
523 return 1;
524 }
vbendebb2b0fcc2010-07-15 15:09:47 -0700525 if ((!keyblock_file && !bp->key_block) || !signprivate) {
Bill Richardsona08b5c92010-06-30 21:59:43 -0700526 error("Must specify all keys\n");
527 return 1;
528 }
529 if (!bp) {
530 error("Refusing to pack invalid kernel blob\n");
531 return 1;
Bill Richardsona08b5c92010-06-30 21:59:43 -0700532 }
vbendebb2b0fcc2010-07-15 15:09:47 -0700533
534 /* Get the key block and read the private key. */
535 if (keyblock_file) {
536 key_block = (VbKeyBlockHeader*)ReadFile(keyblock_file, &key_block_size);
537 if (!key_block) {
538 error("Error reading key block.\n");
539 return 1;
540 }
541 } else {
542 key_block = bp->key_block;
543 key_block_size = key_block->key_block_size;
544 }
545
Bill Richardsona08b5c92010-06-30 21:59:43 -0700546 if (pad < key_block->key_block_size) {
547 error("Pad too small\n");
548 return 1;
549 }
550
Bill Richardsonabf05502010-07-01 10:22:06 -0700551 signing_key = PrivateKeyRead(signprivate);
Bill Richardsona08b5c92010-06-30 21:59:43 -0700552 if (!signing_key) {
553 error("Error reading signing key.\n");
554 return 1;
555 }
556
Randall Spangler7d6898d2010-06-11 09:22:13 -0700557 /* Sign the kernel data */
Bill Richardsona08b5c92010-06-30 21:59:43 -0700558 body_sig = CalculateSignature(bp->blob, bp->blob_size, signing_key);
Randall Spangler7d6898d2010-06-11 09:22:13 -0700559 if (!body_sig) {
560 error("Error calculating body signature\n");
561 return 1;
562 }
563
564 /* Create preamble */
Bill Richardsona08b5c92010-06-30 21:59:43 -0700565 preamble = CreateKernelPreamble(bp->kernel_version,
Randall Spangler7d6898d2010-06-11 09:22:13 -0700566 CROS_32BIT_ENTRY_ADDR,
Bill Richardsona08b5c92010-06-30 21:59:43 -0700567 bp->bootloader_address,
568 bp->bootloader_size,
Randall Spangler7d6898d2010-06-11 09:22:13 -0700569 body_sig,
570 pad - key_block_size,
571 signing_key);
572 if (!preamble) {
573 error("Error creating preamble.\n");
574 return 1;
575 }
576
577 /* Write the output file */
Bill Richardson249677d2010-06-23 11:16:37 -0700578 Debug("writing %s...\n", outfile);
Randall Spangler7d6898d2010-06-11 09:22:13 -0700579 f = fopen(outfile, "wb");
580 if (!f) {
581 error("Can't open output file %s\n", outfile);
582 return 1;
583 }
Bill Richardson249677d2010-06-23 11:16:37 -0700584 Debug("0x%" PRIx64 " bytes of key_block\n", key_block_size);
585 Debug("0x%" PRIx64 " bytes of preamble\n", preamble->preamble_size);
Randall Spangler7d6898d2010-06-11 09:22:13 -0700586 i = ((1 != fwrite(key_block, key_block_size, 1, f)) ||
Bill Richardsona08b5c92010-06-30 21:59:43 -0700587 (1 != fwrite(preamble, preamble->preamble_size, 1, f)));
Randall Spangler7d6898d2010-06-11 09:22:13 -0700588 if (i) {
589 error("Can't write output file %s\n", outfile);
Bill Richardsona08b5c92010-06-30 21:59:43 -0700590 fclose(f);
Randall Spangler7d6898d2010-06-11 09:22:13 -0700591 unlink(outfile);
592 return 1;
593 }
594
Bill Richardsona08b5c92010-06-30 21:59:43 -0700595 if (!vblockonly) {
596 Debug("0x%" PRIx64 " bytes of blob\n", bp->blob_size);
597 i = (1 != fwrite(bp->blob, bp->blob_size, 1, f));
598 if (i) {
599 error("Can't write output file %s\n", outfile);
600 fclose(f);
601 unlink(outfile);
602 return 1;
603 }
604 }
605
606 fclose(f);
607
Randall Spangler7d6898d2010-06-11 09:22:13 -0700608 /* Success */
609 return 0;
610}
611
vbendebb2b0fcc2010-07-15 15:09:47 -0700612/*
613 * Replace kernel command line in a blob representing a kernel.
614 */
615static int ReplaceConfig(blob_t* bp, const char* config_file)
616{
617 uint8_t* new_conf;
618 uint64_t config_size;
Randall Spangler7d6898d2010-06-11 09:22:13 -0700619
vbendebb2b0fcc2010-07-15 15:09:47 -0700620 if (!config_file) {
621 return 0;
622 }
623
624 new_conf = ReadConfigFile(config_file, &config_size);
625 if (!new_conf) {
626 return 1;
627 }
628
629 /* fill the config buffer with zeros */
630 Memset(BpCmdLineLocation(bp), 0, CROS_CONFIG_SIZE);
631 Memcpy(BpCmdLineLocation(bp), new_conf, config_size);
632 Free(new_conf);
633 return 0;
634}
635
Will Drewry9342f882010-10-26 10:22:05 -0500636static int Verify(const char* infile, const char* signpubkey, int verbose,
637 const char* key_block_file) {
Randall Spangler7d6898d2010-06-11 09:22:13 -0700638
639 VbKeyBlockHeader* key_block;
640 VbKernelPreambleHeader* preamble;
641 VbPublicKey* data_key;
Bill Richardson4f36ef32010-08-09 17:50:14 -0700642 VbPublicKey* sign_key = NULL;
Randall Spangler7d6898d2010-06-11 09:22:13 -0700643 RSAPublicKey* rsa;
vbendebb2b0fcc2010-07-15 15:09:47 -0700644 blob_t* bp;
645 uint64_t now;
646 int rv = 1;
Randall Spangler7d6898d2010-06-11 09:22:13 -0700647
Bill Richardson4f36ef32010-08-09 17:50:14 -0700648 if (!infile) {
649 error("Must specify filename\n");
Randall Spangler7d6898d2010-06-11 09:22:13 -0700650 return 1;
651 }
652
653 /* Read public signing key */
Bill Richardson4f36ef32010-08-09 17:50:14 -0700654 if (signpubkey) {
655 sign_key = PublicKeyRead(signpubkey);
656 if (!sign_key) {
657 error("Error reading signpubkey.\n");
658 return 1;
659 }
Randall Spangler7d6898d2010-06-11 09:22:13 -0700660 }
661
662 /* Read blob */
vbendebb2b0fcc2010-07-15 15:09:47 -0700663 bp = OldBlob(infile);
664 if (!bp) {
Randall Spangler7d6898d2010-06-11 09:22:13 -0700665 error("Error reading input file\n");
666 return 1;
667 }
668
669 /* Verify key block */
vbendebb2b0fcc2010-07-15 15:09:47 -0700670 key_block = bp->key_block;
Randall Spangler138acfe2010-08-17 15:45:21 -0700671 if (0 != KeyBlockVerify(key_block, bp->blob_size, sign_key,
672 (sign_key ? 0 : 1))) {
Randall Spangler7d6898d2010-06-11 09:22:13 -0700673 error("Error verifying key block.\n");
vbendebb2b0fcc2010-07-15 15:09:47 -0700674 goto verify_exit;
Randall Spangler7d6898d2010-06-11 09:22:13 -0700675 }
vbendebb2b0fcc2010-07-15 15:09:47 -0700676 now = key_block->key_block_size;
Randall Spangler7d6898d2010-06-11 09:22:13 -0700677
Will Drewry9342f882010-10-26 10:22:05 -0500678 if (key_block_file) {
679 FILE* f = NULL;
680 f = fopen(key_block_file, "wb");
681 if (!f) {
682 error("Can't open key block file %s\n", key_block_file);
683 return 1;
684 }
685 if (1 != fwrite(key_block, key_block->key_block_size, 1, f)) {
686 error("Can't write key block file %s\n", key_block_file);
687 return 1;
688 }
689 fclose(f);
690 }
691
Randall Spangler7d6898d2010-06-11 09:22:13 -0700692 printf("Key block:\n");
693 data_key = &key_block->data_key;
Bill Richardson4f36ef32010-08-09 17:50:14 -0700694 if (verbose)
695 printf(" Signature: %s\n", sign_key ? "valid" : "ignored");
Bill Richardsona08b5c92010-06-30 21:59:43 -0700696 printf(" Size: 0x%" PRIx64 "\n", key_block->key_block_size);
Bill Richardson60bcbe32010-09-09 14:53:56 -0700697 printf(" Flags: %" PRIu64 " ", key_block->key_block_flags);
698 if (key_block->key_block_flags & KEY_BLOCK_FLAG_DEVELOPER_0)
699 printf(" !DEV");
700 if (key_block->key_block_flags & KEY_BLOCK_FLAG_DEVELOPER_1)
701 printf(" DEV");
702 if (key_block->key_block_flags & KEY_BLOCK_FLAG_RECOVERY_0)
703 printf(" !REC");
704 if (key_block->key_block_flags & KEY_BLOCK_FLAG_RECOVERY_1)
705 printf(" REC");
706 printf("\n");
Randall Spangler7d6898d2010-06-11 09:22:13 -0700707 printf(" Data key algorithm: %" PRIu64 " %s\n", data_key->algorithm,
708 (data_key->algorithm < kNumAlgorithms ?
709 algo_strings[data_key->algorithm] : "(invalid)"));
710 printf(" Data key version: %" PRIu64 "\n", data_key->key_version);
Bill Richardson60bcbe32010-09-09 14:53:56 -0700711 printf(" Data key sha1sum: ");
712 PrintPubKeySha1Sum(data_key);
713 printf("\n");
Randall Spangler7d6898d2010-06-11 09:22:13 -0700714
715 rsa = PublicKeyToRSA(&key_block->data_key);
716 if (!rsa) {
717 error("Error parsing data key.\n");
vbendebb2b0fcc2010-07-15 15:09:47 -0700718 goto verify_exit;
Randall Spangler7d6898d2010-06-11 09:22:13 -0700719 }
720
721 /* Verify preamble */
vbendebb2b0fcc2010-07-15 15:09:47 -0700722 preamble = bp->preamble;
Randall Spangler87c13d82010-07-19 10:35:40 -0700723 if (0 != VerifyKernelPreamble(
Bill Richardson4f36ef32010-08-09 17:50:14 -0700724 preamble, bp->blob_size - key_block->key_block_size, rsa)) {
Randall Spangler7d6898d2010-06-11 09:22:13 -0700725 error("Error verifying preamble.\n");
vbendebb2b0fcc2010-07-15 15:09:47 -0700726 goto verify_exit;
Randall Spangler7d6898d2010-06-11 09:22:13 -0700727 }
728 now += preamble->preamble_size;
729
730 printf("Preamble:\n");
Bill Richardsona08b5c92010-06-30 21:59:43 -0700731 printf(" Size: 0x%" PRIx64 "\n", preamble->preamble_size);
Randall Spangler7d6898d2010-06-11 09:22:13 -0700732 printf(" Header version: %" PRIu32 ".%" PRIu32"\n",
733 preamble->header_version_major, preamble->header_version_minor);
734 printf(" Kernel version: %" PRIu64 "\n", preamble->kernel_version);
Bill Richardson249677d2010-06-23 11:16:37 -0700735 printf(" Body load address: 0x%" PRIx64 "\n", preamble->body_load_address);
736 printf(" Body size: 0x%" PRIx64 "\n",
Randall Spangler7d6898d2010-06-11 09:22:13 -0700737 preamble->body_signature.data_size);
Randall Spangler87c13d82010-07-19 10:35:40 -0700738 printf(" Bootloader address: 0x%" PRIx64 "\n",
739 preamble->bootloader_address);
Bill Richardson249677d2010-06-23 11:16:37 -0700740 printf(" Bootloader size: 0x%" PRIx64 "\n", preamble->bootloader_size);
Randall Spangler7d6898d2010-06-11 09:22:13 -0700741
742 /* Verify body */
Randall Spangler87c13d82010-07-19 10:35:40 -0700743 if (0 != VerifyData(bp->blob, bp->blob_size, &preamble->body_signature,
744 rsa)) {
Randall Spangler7d6898d2010-06-11 09:22:13 -0700745 error("Error verifying kernel body.\n");
vbendebb2b0fcc2010-07-15 15:09:47 -0700746 goto verify_exit;
Randall Spangler7d6898d2010-06-11 09:22:13 -0700747 }
748 printf("Body verification succeeded.\n");
vbendebb2b0fcc2010-07-15 15:09:47 -0700749
750 rv = 0;
751
752 if (!verbose) {
753 goto verify_exit;
754 }
755
756 printf("Config:\n%s\n", BpCmdLineLocation(bp));
757
758verify_exit:
759 FreeBlob(bp);
760 return rv;
Randall Spangler7d6898d2010-06-11 09:22:13 -0700761}
762
763
764int main(int argc, char* argv[]) {
Randall Spangler7d6898d2010-06-11 09:22:13 -0700765 char* filename = NULL;
Bill Richardsona08b5c92010-06-30 21:59:43 -0700766 char* oldfile = NULL;
Randall Spangler7d6898d2010-06-11 09:22:13 -0700767 char* key_block_file = NULL;
768 char* signpubkey = NULL;
769 char* signprivate = NULL;
vbendeb00b90882010-10-21 13:46:16 -0700770 int version = -1;
Randall Spangler7d6898d2010-06-11 09:22:13 -0700771 char* vmlinuz = NULL;
772 char* bootloader = NULL;
773 char* config_file = NULL;
Bill Richardsona08b5c92010-06-30 21:59:43 -0700774 int vblockonly = 0;
vbendebb2b0fcc2010-07-15 15:09:47 -0700775 int verbose = 0;
Bill Richardsona08b5c92010-06-30 21:59:43 -0700776 uint64_t pad = DEFAULT_PADDING;
Randall Spangler7d6898d2010-06-11 09:22:13 -0700777 int mode = 0;
778 int parse_error = 0;
779 char* e;
Bill Richardsona08b5c92010-06-30 21:59:43 -0700780 int i,r;
781 blob_t *bp;
Randall Spangler7d6898d2010-06-11 09:22:13 -0700782
Bill Richardsona08b5c92010-06-30 21:59:43 -0700783
784 char *progname = strrchr(argv[0], '/');
785 if (progname)
786 progname++;
787 else
788 progname = argv[0];
789
vbendebb2b0fcc2010-07-15 15:09:47 -0700790 while (((i = getopt_long(argc, argv, ":", long_opts, NULL)) != -1) &&
791 !parse_error) {
Randall Spangler7d6898d2010-06-11 09:22:13 -0700792 switch (i) {
vbendebb2b0fcc2010-07-15 15:09:47 -0700793 default:
Randall Spangler7d6898d2010-06-11 09:22:13 -0700794 case '?':
795 /* Unhandled option */
Randall Spangler7d6898d2010-06-11 09:22:13 -0700796 parse_error = 1;
797 break;
798
Bill Richardson4f36ef32010-08-09 17:50:14 -0700799 case 0:
800 /* silently handled option */
801 break;
802
Randall Spangler7d6898d2010-06-11 09:22:13 -0700803 case OPT_MODE_PACK:
Bill Richardsona08b5c92010-06-30 21:59:43 -0700804 case OPT_MODE_REPACK:
Randall Spangler7d6898d2010-06-11 09:22:13 -0700805 case OPT_MODE_VERIFY:
vbendebb2b0fcc2010-07-15 15:09:47 -0700806 if (mode && (mode != i)) {
807 fprintf(stderr, "Only single mode can be specified\n");
808 parse_error = 1;
809 break;
810 }
Randall Spangler7d6898d2010-06-11 09:22:13 -0700811 mode = i;
812 filename = optarg;
813 break;
814
Bill Richardsona08b5c92010-06-30 21:59:43 -0700815 case OPT_OLDBLOB:
816 oldfile = optarg;
817 break;
818
Randall Spangler7d6898d2010-06-11 09:22:13 -0700819 case OPT_KEYBLOCK:
820 key_block_file = optarg;
821 break;
822
823 case OPT_SIGNPUBKEY:
824 signpubkey = optarg;
825 break;
826
827 case OPT_SIGNPRIVATE:
828 signprivate = optarg;
829 break;
830
831 case OPT_VMLINUZ:
832 vmlinuz = optarg;
833 break;
834
835 case OPT_BOOTLOADER:
836 bootloader = optarg;
837 break;
838
839 case OPT_CONFIG:
840 config_file = optarg;
841 break;
842
Bill Richardsona08b5c92010-06-30 21:59:43 -0700843 case OPT_VBLOCKONLY:
844 vblockonly = 1;
845 break;
846
Randall Spangler7d6898d2010-06-11 09:22:13 -0700847 case OPT_VERSION:
848 version = strtoul(optarg, &e, 0);
849 if (!*optarg || (e && *e)) {
Bill Richardsona08b5c92010-06-30 21:59:43 -0700850 fprintf(stderr, "Invalid --version\n");
Randall Spangler7d6898d2010-06-11 09:22:13 -0700851 parse_error = 1;
852 }
853 break;
854
855 case OPT_PAD:
856 pad = strtoul(optarg, &e, 0);
857 if (!*optarg || (e && *e)) {
Bill Richardsona08b5c92010-06-30 21:59:43 -0700858 fprintf(stderr, "Invalid --pad\n");
Randall Spangler7d6898d2010-06-11 09:22:13 -0700859 parse_error = 1;
860 }
861 break;
vbendebb2b0fcc2010-07-15 15:09:47 -0700862
863 case OPT_VERBOSE:
864 verbose = 1;
865 break;
Randall Spangler7d6898d2010-06-11 09:22:13 -0700866 }
867 }
868
869 if (parse_error)
Bill Richardsona08b5c92010-06-30 21:59:43 -0700870 return PrintHelp(progname);
Randall Spangler7d6898d2010-06-11 09:22:13 -0700871
872 switch(mode) {
873 case OPT_MODE_PACK:
Bill Richardsona08b5c92010-06-30 21:59:43 -0700874 bp = NewBlob(version, vmlinuz, bootloader, config_file);
875 if (!bp)
876 return 1;
877 r = Pack(filename, key_block_file, signprivate, bp, pad, vblockonly);
878 FreeBlob(bp);
879 return r;
880
881 case OPT_MODE_REPACK:
vbendeb00b90882010-10-21 13:46:16 -0700882 if (!config_file && !key_block_file && (version<0)) {
vbendebb2b0fcc2010-07-15 15:09:47 -0700883 fprintf(stderr,
vbendeb858fffb2010-10-06 09:51:44 -0700884 "You must supply at least one of "
885 "--config, --keyblock or --version\n");
vbendebb2b0fcc2010-07-15 15:09:47 -0700886 return 1;
887 }
888
Bill Richardsona08b5c92010-06-30 21:59:43 -0700889 bp = OldBlob(oldfile);
890 if (!bp)
891 return 1;
vbendebb2b0fcc2010-07-15 15:09:47 -0700892 r = ReplaceConfig(bp, config_file);
893 if (!r) {
vbendeb00b90882010-10-21 13:46:16 -0700894 if (version >= 0) {
895 bp->kernel_version = (uint64_t) version;
vbendeb858fffb2010-10-06 09:51:44 -0700896 }
vbendebb2b0fcc2010-07-15 15:09:47 -0700897 r = Pack(filename, key_block_file, signprivate, bp, pad, vblockonly);
898 }
Bill Richardsona08b5c92010-06-30 21:59:43 -0700899 FreeBlob(bp);
900 return r;
901
Randall Spangler7d6898d2010-06-11 09:22:13 -0700902 case OPT_MODE_VERIFY:
Will Drewry9342f882010-10-26 10:22:05 -0500903 return Verify(filename, signpubkey, verbose, key_block_file);
Bill Richardsona08b5c92010-06-30 21:59:43 -0700904
Randall Spangler7d6898d2010-06-11 09:22:13 -0700905 default:
Bill Richardsona08b5c92010-06-30 21:59:43 -0700906 fprintf(stderr,
907 "You must specify a mode: --pack, --repack or --verify\n");
908 return PrintHelp(progname);
Randall Spangler7d6898d2010-06-11 09:22:13 -0700909 }
910}