blob: 391f5ad284722d1b22776518e3d816f5599700ab [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"
Bill Richardsona08b5c92010-06-30 21:59:43 -0700115 "\n",
116 progname);
Randall Spangler7d6898d2010-06-11 09:22:13 -0700117 return 1;
118}
119
Bill Richardson249677d2010-06-23 11:16:37 -0700120static void Debug(const char *format, ...) {
121 if (!opt_debug)
122 return;
123
124 va_list ap;
125 va_start(ap, format);
126 fprintf(stderr, "DEBUG: ");
127 vfprintf(stderr, format, ap);
128 va_end(ap);
129}
130
Randall Spangler7d6898d2010-06-11 09:22:13 -0700131
132/* Return the smallest integral multiple of [alignment] that is equal
133 * to or greater than [val]. Used to determine the number of
134 * pages/sectors/blocks/whatever needed to contain [val]
135 * items/bytes/etc. */
136static uint64_t roundup(uint64_t val, uint64_t alignment) {
137 uint64_t rem = val % alignment;
138 if ( rem )
139 return val + (alignment - rem);
140 return val;
141}
142
143
144/* Match regexp /\b--\b/ to delimit the start of the kernel commandline. If we
145 * don't find one, we'll use the whole thing. */
146static unsigned int find_cmdline_start(char *input, unsigned int max_len) {
147 int start = 0;
148 int i;
149 for(i = 0; i < max_len - 1 && input[i]; i++) {
150 if ('-' == input[i] && '-' == input[i + 1]) { /* found a "--" */
151 if ((i == 0 || ' ' == input[i - 1]) && /* nothing before it */
152 (i + 2 >= max_len || ' ' == input[i+2])) { /* nothing after it */
153 start = i+2; /* note: hope there's a trailing '\0' */
154 break;
155 }
156 }
157 }
158 while(' ' == input[start]) /* skip leading spaces */
159 start++;
160
161 return start;
162}
163
164
Bill Richardsona08b5c92010-06-30 21:59:43 -0700165typedef struct blob_s {
166 /* Stuff needed by VbKernelPreambleHeader */
167 uint64_t kernel_version;
168 uint64_t bootloader_address;
169 uint64_t bootloader_size;
170 /* Raw kernel blob data */
171 uint64_t blob_size;
172 uint8_t *blob;
vbendebb2b0fcc2010-07-15 15:09:47 -0700173
174 /* these fields are not always initialized */
175 VbKernelPreambleHeader* preamble;
176 VbKeyBlockHeader* key_block;
177 uint8_t *buf;
178
Bill Richardsona08b5c92010-06-30 21:59:43 -0700179} blob_t;
Randall Spangler7d6898d2010-06-11 09:22:13 -0700180
vbendebb2b0fcc2010-07-15 15:09:47 -0700181/* Given a blob return the location of the kernel command line buffer. */
182static char* BpCmdLineLocation(blob_t *bp)
183{
184 return (char*)(bp->blob + bp->bootloader_address - CROS_32BIT_ENTRY_ADDR -
185 CROS_CONFIG_SIZE - CROS_PARAMS_SIZE);
186}
Bill Richardsona08b5c92010-06-30 21:59:43 -0700187
188static void FreeBlob(blob_t *bp) {
189 if (bp) {
190 if (bp->blob)
191 Free(bp->blob);
vbendebb2b0fcc2010-07-15 15:09:47 -0700192 if (bp->buf)
193 Free(bp->buf);
Bill Richardsona08b5c92010-06-30 21:59:43 -0700194 Free(bp);
195 }
196}
197
vbendebb2b0fcc2010-07-15 15:09:47 -0700198/*
199 * Read the kernel command line from a file. Get rid of \n characters along
200 * the way and verify that the line fits into a 4K buffer.
201 *
202 * Return the buffer contaning the line on success (and set the line length
203 * using the passed in parameter), or NULL in case something goes wrong.
204 */
205static uint8_t* ReadConfigFile(const char* config_file, uint64_t* config_size)
206{
207 uint8_t* config_buf;
208 int ii;
209
210 config_buf = ReadFile(config_file, config_size);
211 Debug(" config file size=0x%" PRIx64 "\n", *config_size);
212 if (CROS_CONFIG_SIZE <= *config_size) { /* need room for trailing '\0' */
213 error("Config file %s is too large (>= %d bytes)\n",
214 config_file, CROS_CONFIG_SIZE);
215 return NULL;
216 }
217
218 /* Replace newlines with spaces */
219 for (ii = 0; ii < *config_size; ii++) {
220 if ('\n' == config_buf[ii]) {
221 config_buf[ii] = ' ';
222 }
223 }
224 return config_buf;
225}
226
Bill Richardsona08b5c92010-06-30 21:59:43 -0700227/* Create a blob from its components */
228static blob_t *NewBlob(uint64_t version,
229 const char* vmlinuz,
230 const char* bootloader_file,
231 const char* config_file) {
232 blob_t *bp;
Randall Spangler7d6898d2010-06-11 09:22:13 -0700233 struct linux_kernel_header *lh = 0;
234 struct linux_kernel_params *params = 0;
Randall Spangler7d6898d2010-06-11 09:22:13 -0700235 uint8_t* config_buf;
236 uint64_t config_size;
237 uint8_t* bootloader_buf;
238 uint64_t bootloader_size;
Randall Spangler7d6898d2010-06-11 09:22:13 -0700239 uint8_t* kernel_buf;
240 uint64_t kernel_size;
241 uint64_t kernel32_start = 0;
242 uint64_t kernel32_size = 0;
243 uint32_t cmdline_addr;
244 uint8_t* blob = NULL;
Randall Spangler7d6898d2010-06-11 09:22:13 -0700245 uint64_t now = 0;
Randall Spangler7d6898d2010-06-11 09:22:13 -0700246
Randall Spangler7d6898d2010-06-11 09:22:13 -0700247 if (!vmlinuz || !bootloader_file || !config_file) {
248 error("Must specify all input files\n");
Bill Richardsona08b5c92010-06-30 21:59:43 -0700249 return 0;
Randall Spangler7d6898d2010-06-11 09:22:13 -0700250 }
251
Bill Richardsona08b5c92010-06-30 21:59:43 -0700252 bp = (blob_t *)Malloc(sizeof(blob_t));
253 if (!bp) {
254 error("Couldn't allocate bytes for blob_t.\n");
255 return 0;
Randall Spangler7d6898d2010-06-11 09:22:13 -0700256 }
vbendebb2b0fcc2010-07-15 15:09:47 -0700257
258 Memset(bp, 0, sizeof(*bp));
Bill Richardsona08b5c92010-06-30 21:59:43 -0700259 bp->kernel_version = version;
Randall Spangler7d6898d2010-06-11 09:22:13 -0700260
261 /* Read the config file */
Bill Richardson249677d2010-06-23 11:16:37 -0700262 Debug("Reading %s\n", config_file);
vbendebb2b0fcc2010-07-15 15:09:47 -0700263 config_buf = ReadConfigFile(config_file, &config_size);
Randall Spangler7d6898d2010-06-11 09:22:13 -0700264 if (!config_buf)
Bill Richardsona08b5c92010-06-30 21:59:43 -0700265 return 0;
Randall Spangler7d6898d2010-06-11 09:22:13 -0700266
267 /* Read the bootloader */
Bill Richardson249677d2010-06-23 11:16:37 -0700268 Debug("Reading %s\n", bootloader_file);
Randall Spangler7d6898d2010-06-11 09:22:13 -0700269 bootloader_buf = ReadFile(bootloader_file, &bootloader_size);
270 if (!bootloader_buf)
Bill Richardsona08b5c92010-06-30 21:59:43 -0700271 return 0;
Bill Richardson249677d2010-06-23 11:16:37 -0700272 Debug(" bootloader file size=0x%" PRIx64 "\n", bootloader_size);
Randall Spangler7d6898d2010-06-11 09:22:13 -0700273
274 /* Read the kernel */
Bill Richardson249677d2010-06-23 11:16:37 -0700275 Debug("Reading %s\n", vmlinuz);
Randall Spangler7d6898d2010-06-11 09:22:13 -0700276 kernel_buf = ReadFile(vmlinuz, &kernel_size);
277 if (!kernel_buf)
Bill Richardsona08b5c92010-06-30 21:59:43 -0700278 return 0;
Bill Richardson249677d2010-06-23 11:16:37 -0700279 Debug(" kernel file size=0x%" PRIx64 "\n", kernel_size);
Randall Spangler7d6898d2010-06-11 09:22:13 -0700280 if (!kernel_size) {
281 error("Empty kernel file\n");
Bill Richardsona08b5c92010-06-30 21:59:43 -0700282 return 0;
Randall Spangler7d6898d2010-06-11 09:22:13 -0700283 }
284
285 /* The first part of vmlinuz is a header, followed by a real-mode
286 * boot stub. We only want the 32-bit part. */
287 lh = (struct linux_kernel_header *)kernel_buf;
288 kernel32_start = (lh->setup_sects + 1) << 9;
289 if (kernel32_start >= kernel_size) {
290 error("Malformed kernel\n");
Bill Richardsona08b5c92010-06-30 21:59:43 -0700291 return 0;
Randall Spangler7d6898d2010-06-11 09:22:13 -0700292 }
293 kernel32_size = kernel_size - kernel32_start;
Bill Richardson249677d2010-06-23 11:16:37 -0700294 Debug(" kernel32_start=0x%" PRIx64 "\n", kernel32_start);
295 Debug(" kernel32_size=0x%" PRIx64 "\n", kernel32_size);
Randall Spangler7d6898d2010-06-11 09:22:13 -0700296
297 /* Allocate and zero the blob we need. */
Bill Richardsona08b5c92010-06-30 21:59:43 -0700298 bp->blob_size = roundup(kernel32_size, CROS_ALIGN) +
Randall Spangler7d6898d2010-06-11 09:22:13 -0700299 CROS_CONFIG_SIZE +
300 CROS_PARAMS_SIZE +
301 roundup(bootloader_size, CROS_ALIGN);
Bill Richardsona08b5c92010-06-30 21:59:43 -0700302 blob = (uint8_t *)Malloc(bp->blob_size);
303 Debug("blob_size=0x%" PRIx64 "\n", bp->blob_size);
Randall Spangler7d6898d2010-06-11 09:22:13 -0700304 if (!blob) {
Bill Richardsona08b5c92010-06-30 21:59:43 -0700305 error("Couldn't allocate %ld bytes.\n", bp->blob_size);
306 return 0;
Randall Spangler7d6898d2010-06-11 09:22:13 -0700307 }
Bill Richardsona08b5c92010-06-30 21:59:43 -0700308 Memset(blob, 0, bp->blob_size);
309 bp->blob = blob;
Randall Spangler7d6898d2010-06-11 09:22:13 -0700310
311 /* Copy the 32-bit kernel. */
Bill Richardson249677d2010-06-23 11:16:37 -0700312 Debug("kernel goes at blob+=0x%" PRIx64 "\n", now);
Randall Spangler7d6898d2010-06-11 09:22:13 -0700313 if (kernel32_size)
314 Memcpy(blob + now, kernel_buf + kernel32_start, kernel32_size);
315 now += roundup(now + kernel32_size, CROS_ALIGN);
316
Bill Richardson249677d2010-06-23 11:16:37 -0700317 Debug("config goes at blob+0x%" PRIx64 "\n", now);
Randall Spangler7d6898d2010-06-11 09:22:13 -0700318 /* Find the load address of the commandline. We'll need it later. */
319 cmdline_addr = CROS_32BIT_ENTRY_ADDR + now +
320 find_cmdline_start((char *)config_buf, config_size);
Bill Richardson249677d2010-06-23 11:16:37 -0700321 Debug(" cmdline_addr=0x%" PRIx64 "\n", cmdline_addr);
Randall Spangler7d6898d2010-06-11 09:22:13 -0700322
323 /* Copy the config. */
324 if (config_size)
325 Memcpy(blob + now, config_buf, config_size);
326 now += CROS_CONFIG_SIZE;
327
328 /* The zeropage data is next. Overlay the linux_kernel_header onto it, and
329 * tweak a few fields. */
Bill Richardson249677d2010-06-23 11:16:37 -0700330 Debug("params goes at blob+=0x%" PRIx64 "\n", now);
Randall Spangler7d6898d2010-06-11 09:22:13 -0700331 params = (struct linux_kernel_params *)(blob + now);
332 Memcpy(&(params->setup_sects), &(lh->setup_sects),
333 sizeof(*lh) - offsetof(struct linux_kernel_header, setup_sects));
334 params->boot_flag = 0;
335 params->ramdisk_image = 0; /* we don't support initrd */
336 params->ramdisk_size = 0;
337 params->type_of_loader = 0xff;
338 params->cmd_line_ptr = cmdline_addr;
Che-Liang Chiou475bf442010-08-23 11:20:44 +0800339 /* A fake e820 memory map with 2 entries */
340 params->n_e820_entry = 2;
341 params->e820_entries[0].start_addr = 0x00000000;
342 params->e820_entries[0].segment_size = 0x00001000;
343 params->e820_entries[0].segment_type = E820_TYPE_RAM;
344 params->e820_entries[1].start_addr = 0xfffff000;
345 params->e820_entries[1].segment_size = 0x00001000;
346 params->e820_entries[1].segment_type = E820_TYPE_RESERVED;
Randall Spangler7d6898d2010-06-11 09:22:13 -0700347 now += CROS_PARAMS_SIZE;
348
349 /* Finally, append the bootloader. Remember where it will load in
350 * memory, too. */
Bill Richardson249677d2010-06-23 11:16:37 -0700351 Debug("bootloader goes at blob+=0x%" PRIx64 "\n", now);
Bill Richardsona08b5c92010-06-30 21:59:43 -0700352 bp->bootloader_address = CROS_32BIT_ENTRY_ADDR + now;
353 bp->bootloader_size = roundup(bootloader_size, CROS_ALIGN);
354 Debug(" bootloader_address=0x%" PRIx64 "\n", bp->bootloader_address);
355 Debug(" bootloader_size=0x%" PRIx64 "\n", bp->bootloader_size);
Randall Spangler7d6898d2010-06-11 09:22:13 -0700356 if (bootloader_size)
357 Memcpy(blob + now, bootloader_buf, bootloader_size);
Bill Richardsona08b5c92010-06-30 21:59:43 -0700358 now += bp->bootloader_size;
Bill Richardson249677d2010-06-23 11:16:37 -0700359 Debug("end of blob is 0x%" PRIx64 "\n", now);
Randall Spangler7d6898d2010-06-11 09:22:13 -0700360
361 /* Free input buffers */
362 Free(kernel_buf);
363 Free(config_buf);
364 Free(bootloader_buf);
365
Bill Richardsona08b5c92010-06-30 21:59:43 -0700366 /* Success */
367 return bp;
368}
369
370
371/* Pull the blob_t stuff out of a prepacked kernel blob file */
372static blob_t *OldBlob(const char* filename) {
vbendebb2b0fcc2010-07-15 15:09:47 -0700373 FILE* fp = NULL;
374 blob_t *bp = NULL;
Bill Richardsona08b5c92010-06-30 21:59:43 -0700375 struct stat statbuf;
376 VbKeyBlockHeader* key_block;
377 VbKernelPreambleHeader* preamble;
378 uint64_t now = 0;
vbendebb2b0fcc2010-07-15 15:09:47 -0700379 uint8_t* buf = NULL;
380 int ret_error = 1;
Bill Richardsona08b5c92010-06-30 21:59:43 -0700381
382 if (!filename) {
383 error("Must specify prepacked blob to read\n");
384 return 0;
385 }
386
387 if (0 != stat(filename, &statbuf)) {
388 error("unable to stat %s: %s\n", filename, strerror(errno));
389 return 0;
390 }
391
392 Debug("%s size is 0x%" PRIx64 "\n", filename, statbuf.st_size);
393 if (statbuf.st_size < DEFAULT_PADDING) {
394 error("%s is too small to be a valid kernel blob\n");
395 return 0;
396 }
397
398 Debug("Reading %s\n", filename);
399 fp = fopen(filename, "rb");
400 if (!fp) {
401 error("Unable to open file %s: %s\n", filename, strerror(errno));
402 return 0;
403 }
404
vbendebb2b0fcc2010-07-15 15:09:47 -0700405 buf = Malloc(DEFAULT_PADDING);
406 if (!buf) {
407 error("Unable to allocate padding\n");
408 goto unwind_oldblob;
409 }
410
411 if (1 != fread(buf, DEFAULT_PADDING, 1, fp)) {
Bill Richardsona08b5c92010-06-30 21:59:43 -0700412 error("Unable to read header from %s: %s\n", filename, strerror(errno));
vbendebb2b0fcc2010-07-15 15:09:47 -0700413 goto unwind_oldblob;
Bill Richardsona08b5c92010-06-30 21:59:43 -0700414 }
415
416 /* Skip the key block */
417 key_block = (VbKeyBlockHeader*)buf;
418 Debug("Keyblock is 0x%" PRIx64 " bytes\n", key_block->key_block_size);
419 now += key_block->key_block_size;
420 if (now > statbuf.st_size) {
421 error("key_block_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 /* Skip the preamble */
426 preamble = (VbKernelPreambleHeader*)(buf + now);
427 Debug("Preamble is 0x%" PRIx64 " bytes\n", preamble->preamble_size);
428 now += preamble->preamble_size;
429 if (now > statbuf.st_size) {
430 error("preamble_size advances past the end of the blob\n");
vbendebb2b0fcc2010-07-15 15:09:47 -0700431 goto unwind_oldblob;
Bill Richardsona08b5c92010-06-30 21:59:43 -0700432 }
433
434 /* Go find the kernel blob */
435 Debug("kernel blob is at offset 0x%" PRIx64 "\n", now);
436 if (0 != fseek(fp, now, SEEK_SET)) {
437 error("Unable to seek to 0x%" PRIx64 " in %s: %s\n", now, filename,
438 strerror(errno));
vbendebb2b0fcc2010-07-15 15:09:47 -0700439 goto unwind_oldblob;
Bill Richardsona08b5c92010-06-30 21:59:43 -0700440 }
441
442 /* Remember what we've got */
443 bp = (blob_t *)Malloc(sizeof(blob_t));
444 if (!bp) {
445 error("Couldn't allocate bytes for blob_t.\n");
vbendebb2b0fcc2010-07-15 15:09:47 -0700446 goto unwind_oldblob;
Bill Richardsona08b5c92010-06-30 21:59:43 -0700447 }
448
vbendebb2b0fcc2010-07-15 15:09:47 -0700449 bp->buf = buf;
450 bp->key_block = key_block;
451 bp->preamble = preamble;
452
Bill Richardsona08b5c92010-06-30 21:59:43 -0700453 bp->kernel_version = preamble->kernel_version;
454 bp->bootloader_address = preamble->bootloader_address;
455 bp->bootloader_size = preamble->bootloader_size;
456 bp->blob_size = preamble->body_signature.data_size;
457
458 Debug(" kernel_version = %d\n", bp->kernel_version);
459 Debug(" bootloader_address = 0x%" PRIx64 "\n", bp->bootloader_address);
460 Debug(" bootloader_size = 0x%" PRIx64 "\n", bp->bootloader_size);
461 Debug(" blob_size = 0x%" PRIx64 "\n", bp->blob_size);
462
463 bp->blob = (uint8_t *)Malloc(bp->blob_size);
464 if (!bp->blob) {
465 error("Couldn't allocate 0x%" PRIx64 " bytes for blob_t.\n", bp->blob_size);
vbendebb2b0fcc2010-07-15 15:09:47 -0700466 goto unwind_oldblob;
Bill Richardsona08b5c92010-06-30 21:59:43 -0700467 }
468
469 /* read it in */
470 if (1 != fread(bp->blob, bp->blob_size, 1, fp)) {
471 error("Unable to read kernel blob from %s: %s\n", filename, strerror(errno));
vbendebb2b0fcc2010-07-15 15:09:47 -0700472 goto unwind_oldblob;
Bill Richardsona08b5c92010-06-30 21:59:43 -0700473 }
474
vbendebb2b0fcc2010-07-15 15:09:47 -0700475 ret_error = 0;
Bill Richardsona08b5c92010-06-30 21:59:43 -0700476
vbendebb2b0fcc2010-07-15 15:09:47 -0700477 /* done */
478unwind_oldblob:
479 fclose(fp);
480 if (ret_error) {
481 if (bp) {
482 FreeBlob(bp);
483 bp = NULL;
484 } else if (buf) {
485 Free(buf);
486 }
487 }
Bill Richardsona08b5c92010-06-30 21:59:43 -0700488 return bp;
489}
490
491
492/* Pack a .kernel */
493static int Pack(const char* outfile, const char* keyblock_file,
494 const char* signprivate, blob_t *bp, uint64_t pad,
495 int vblockonly) {
496 VbPrivateKey* signing_key;
497 VbSignature* body_sig;
498 VbKernelPreambleHeader* preamble;
499 VbKeyBlockHeader* key_block;
500 uint64_t key_block_size;
501 FILE* f;
502 uint64_t i;
503
504 if (!outfile) {
505 error("Must specify output filename\n");
506 return 1;
507 }
vbendebb2b0fcc2010-07-15 15:09:47 -0700508 if ((!keyblock_file && !bp->key_block) || !signprivate) {
Bill Richardsona08b5c92010-06-30 21:59:43 -0700509 error("Must specify all keys\n");
510 return 1;
511 }
512 if (!bp) {
513 error("Refusing to pack invalid kernel blob\n");
514 return 1;
Bill Richardsona08b5c92010-06-30 21:59:43 -0700515 }
vbendebb2b0fcc2010-07-15 15:09:47 -0700516
517 /* Get the key block and read the private key. */
518 if (keyblock_file) {
519 key_block = (VbKeyBlockHeader*)ReadFile(keyblock_file, &key_block_size);
520 if (!key_block) {
521 error("Error reading key block.\n");
522 return 1;
523 }
524 } else {
525 key_block = bp->key_block;
526 key_block_size = key_block->key_block_size;
527 }
528
Bill Richardsona08b5c92010-06-30 21:59:43 -0700529 if (pad < key_block->key_block_size) {
530 error("Pad too small\n");
531 return 1;
532 }
533
Bill Richardsonabf05502010-07-01 10:22:06 -0700534 signing_key = PrivateKeyRead(signprivate);
Bill Richardsona08b5c92010-06-30 21:59:43 -0700535 if (!signing_key) {
536 error("Error reading signing key.\n");
537 return 1;
538 }
539
Randall Spangler7d6898d2010-06-11 09:22:13 -0700540 /* Sign the kernel data */
Bill Richardsona08b5c92010-06-30 21:59:43 -0700541 body_sig = CalculateSignature(bp->blob, bp->blob_size, signing_key);
Randall Spangler7d6898d2010-06-11 09:22:13 -0700542 if (!body_sig) {
543 error("Error calculating body signature\n");
544 return 1;
545 }
546
547 /* Create preamble */
Bill Richardsona08b5c92010-06-30 21:59:43 -0700548 preamble = CreateKernelPreamble(bp->kernel_version,
Randall Spangler7d6898d2010-06-11 09:22:13 -0700549 CROS_32BIT_ENTRY_ADDR,
Bill Richardsona08b5c92010-06-30 21:59:43 -0700550 bp->bootloader_address,
551 bp->bootloader_size,
Randall Spangler7d6898d2010-06-11 09:22:13 -0700552 body_sig,
553 pad - key_block_size,
554 signing_key);
555 if (!preamble) {
556 error("Error creating preamble.\n");
557 return 1;
558 }
559
560 /* Write the output file */
Bill Richardson249677d2010-06-23 11:16:37 -0700561 Debug("writing %s...\n", outfile);
Randall Spangler7d6898d2010-06-11 09:22:13 -0700562 f = fopen(outfile, "wb");
563 if (!f) {
564 error("Can't open output file %s\n", outfile);
565 return 1;
566 }
Bill Richardson249677d2010-06-23 11:16:37 -0700567 Debug("0x%" PRIx64 " bytes of key_block\n", key_block_size);
568 Debug("0x%" PRIx64 " bytes of preamble\n", preamble->preamble_size);
Randall Spangler7d6898d2010-06-11 09:22:13 -0700569 i = ((1 != fwrite(key_block, key_block_size, 1, f)) ||
Bill Richardsona08b5c92010-06-30 21:59:43 -0700570 (1 != fwrite(preamble, preamble->preamble_size, 1, f)));
Randall Spangler7d6898d2010-06-11 09:22:13 -0700571 if (i) {
572 error("Can't write output file %s\n", outfile);
Bill Richardsona08b5c92010-06-30 21:59:43 -0700573 fclose(f);
Randall Spangler7d6898d2010-06-11 09:22:13 -0700574 unlink(outfile);
575 return 1;
576 }
577
Bill Richardsona08b5c92010-06-30 21:59:43 -0700578 if (!vblockonly) {
579 Debug("0x%" PRIx64 " bytes of blob\n", bp->blob_size);
580 i = (1 != fwrite(bp->blob, bp->blob_size, 1, f));
581 if (i) {
582 error("Can't write output file %s\n", outfile);
583 fclose(f);
584 unlink(outfile);
585 return 1;
586 }
587 }
588
589 fclose(f);
590
Randall Spangler7d6898d2010-06-11 09:22:13 -0700591 /* Success */
592 return 0;
593}
594
vbendebb2b0fcc2010-07-15 15:09:47 -0700595/*
596 * Replace kernel command line in a blob representing a kernel.
597 */
598static int ReplaceConfig(blob_t* bp, const char* config_file)
599{
600 uint8_t* new_conf;
601 uint64_t config_size;
Randall Spangler7d6898d2010-06-11 09:22:13 -0700602
vbendebb2b0fcc2010-07-15 15:09:47 -0700603 if (!config_file) {
604 return 0;
605 }
606
607 new_conf = ReadConfigFile(config_file, &config_size);
608 if (!new_conf) {
609 return 1;
610 }
611
612 /* fill the config buffer with zeros */
613 Memset(BpCmdLineLocation(bp), 0, CROS_CONFIG_SIZE);
614 Memcpy(BpCmdLineLocation(bp), new_conf, config_size);
615 Free(new_conf);
616 return 0;
617}
618
619static int Verify(const char* infile, const char* signpubkey, int verbose) {
Randall Spangler7d6898d2010-06-11 09:22:13 -0700620
621 VbKeyBlockHeader* key_block;
622 VbKernelPreambleHeader* preamble;
623 VbPublicKey* data_key;
Bill Richardson4f36ef32010-08-09 17:50:14 -0700624 VbPublicKey* sign_key = NULL;
Randall Spangler7d6898d2010-06-11 09:22:13 -0700625 RSAPublicKey* rsa;
vbendebb2b0fcc2010-07-15 15:09:47 -0700626 blob_t* bp;
627 uint64_t now;
628 int rv = 1;
Randall Spangler7d6898d2010-06-11 09:22:13 -0700629
Bill Richardson4f36ef32010-08-09 17:50:14 -0700630 if (!infile) {
631 error("Must specify filename\n");
Randall Spangler7d6898d2010-06-11 09:22:13 -0700632 return 1;
633 }
634
635 /* Read public signing key */
Bill Richardson4f36ef32010-08-09 17:50:14 -0700636 if (signpubkey) {
637 sign_key = PublicKeyRead(signpubkey);
638 if (!sign_key) {
639 error("Error reading signpubkey.\n");
640 return 1;
641 }
Randall Spangler7d6898d2010-06-11 09:22:13 -0700642 }
643
644 /* Read blob */
vbendebb2b0fcc2010-07-15 15:09:47 -0700645 bp = OldBlob(infile);
646 if (!bp) {
Randall Spangler7d6898d2010-06-11 09:22:13 -0700647 error("Error reading input file\n");
648 return 1;
649 }
650
651 /* Verify key block */
vbendebb2b0fcc2010-07-15 15:09:47 -0700652 key_block = bp->key_block;
Randall Spangler138acfe2010-08-17 15:45:21 -0700653 if (0 != KeyBlockVerify(key_block, bp->blob_size, sign_key,
654 (sign_key ? 0 : 1))) {
Randall Spangler7d6898d2010-06-11 09:22:13 -0700655 error("Error verifying key block.\n");
vbendebb2b0fcc2010-07-15 15:09:47 -0700656 goto verify_exit;
Randall Spangler7d6898d2010-06-11 09:22:13 -0700657 }
vbendebb2b0fcc2010-07-15 15:09:47 -0700658 now = key_block->key_block_size;
Randall Spangler7d6898d2010-06-11 09:22:13 -0700659
660 printf("Key block:\n");
661 data_key = &key_block->data_key;
Bill Richardson4f36ef32010-08-09 17:50:14 -0700662 if (verbose)
663 printf(" Signature: %s\n", sign_key ? "valid" : "ignored");
Bill Richardsona08b5c92010-06-30 21:59:43 -0700664 printf(" Size: 0x%" PRIx64 "\n", key_block->key_block_size);
Bill Richardson60bcbe32010-09-09 14:53:56 -0700665 printf(" Flags: %" PRIu64 " ", key_block->key_block_flags);
666 if (key_block->key_block_flags & KEY_BLOCK_FLAG_DEVELOPER_0)
667 printf(" !DEV");
668 if (key_block->key_block_flags & KEY_BLOCK_FLAG_DEVELOPER_1)
669 printf(" DEV");
670 if (key_block->key_block_flags & KEY_BLOCK_FLAG_RECOVERY_0)
671 printf(" !REC");
672 if (key_block->key_block_flags & KEY_BLOCK_FLAG_RECOVERY_1)
673 printf(" REC");
674 printf("\n");
Randall Spangler7d6898d2010-06-11 09:22:13 -0700675 printf(" Data key algorithm: %" PRIu64 " %s\n", data_key->algorithm,
676 (data_key->algorithm < kNumAlgorithms ?
677 algo_strings[data_key->algorithm] : "(invalid)"));
678 printf(" Data key version: %" PRIu64 "\n", data_key->key_version);
Bill Richardson60bcbe32010-09-09 14:53:56 -0700679 printf(" Data key sha1sum: ");
680 PrintPubKeySha1Sum(data_key);
681 printf("\n");
Randall Spangler7d6898d2010-06-11 09:22:13 -0700682
683 rsa = PublicKeyToRSA(&key_block->data_key);
684 if (!rsa) {
685 error("Error parsing data key.\n");
vbendebb2b0fcc2010-07-15 15:09:47 -0700686 goto verify_exit;
Randall Spangler7d6898d2010-06-11 09:22:13 -0700687 }
688
689 /* Verify preamble */
vbendebb2b0fcc2010-07-15 15:09:47 -0700690 preamble = bp->preamble;
Randall Spangler87c13d82010-07-19 10:35:40 -0700691 if (0 != VerifyKernelPreamble(
Bill Richardson4f36ef32010-08-09 17:50:14 -0700692 preamble, bp->blob_size - key_block->key_block_size, rsa)) {
Randall Spangler7d6898d2010-06-11 09:22:13 -0700693 error("Error verifying preamble.\n");
vbendebb2b0fcc2010-07-15 15:09:47 -0700694 goto verify_exit;
Randall Spangler7d6898d2010-06-11 09:22:13 -0700695 }
696 now += preamble->preamble_size;
697
698 printf("Preamble:\n");
Bill Richardsona08b5c92010-06-30 21:59:43 -0700699 printf(" Size: 0x%" PRIx64 "\n", preamble->preamble_size);
Randall Spangler7d6898d2010-06-11 09:22:13 -0700700 printf(" Header version: %" PRIu32 ".%" PRIu32"\n",
701 preamble->header_version_major, preamble->header_version_minor);
702 printf(" Kernel version: %" PRIu64 "\n", preamble->kernel_version);
Bill Richardson249677d2010-06-23 11:16:37 -0700703 printf(" Body load address: 0x%" PRIx64 "\n", preamble->body_load_address);
704 printf(" Body size: 0x%" PRIx64 "\n",
Randall Spangler7d6898d2010-06-11 09:22:13 -0700705 preamble->body_signature.data_size);
Randall Spangler87c13d82010-07-19 10:35:40 -0700706 printf(" Bootloader address: 0x%" PRIx64 "\n",
707 preamble->bootloader_address);
Bill Richardson249677d2010-06-23 11:16:37 -0700708 printf(" Bootloader size: 0x%" PRIx64 "\n", preamble->bootloader_size);
Randall Spangler7d6898d2010-06-11 09:22:13 -0700709
710 /* Verify body */
Randall Spangler87c13d82010-07-19 10:35:40 -0700711 if (0 != VerifyData(bp->blob, bp->blob_size, &preamble->body_signature,
712 rsa)) {
Randall Spangler7d6898d2010-06-11 09:22:13 -0700713 error("Error verifying kernel body.\n");
vbendebb2b0fcc2010-07-15 15:09:47 -0700714 goto verify_exit;
Randall Spangler7d6898d2010-06-11 09:22:13 -0700715 }
716 printf("Body verification succeeded.\n");
vbendebb2b0fcc2010-07-15 15:09:47 -0700717
718 rv = 0;
719
720 if (!verbose) {
721 goto verify_exit;
722 }
723
724 printf("Config:\n%s\n", BpCmdLineLocation(bp));
725
726verify_exit:
727 FreeBlob(bp);
728 return rv;
Randall Spangler7d6898d2010-06-11 09:22:13 -0700729}
730
731
732int main(int argc, char* argv[]) {
Randall Spangler7d6898d2010-06-11 09:22:13 -0700733 char* filename = NULL;
Bill Richardsona08b5c92010-06-30 21:59:43 -0700734 char* oldfile = NULL;
Randall Spangler7d6898d2010-06-11 09:22:13 -0700735 char* key_block_file = NULL;
736 char* signpubkey = NULL;
737 char* signprivate = NULL;
738 uint64_t version = 0;
739 char* vmlinuz = NULL;
740 char* bootloader = NULL;
741 char* config_file = NULL;
Bill Richardsona08b5c92010-06-30 21:59:43 -0700742 int vblockonly = 0;
vbendebb2b0fcc2010-07-15 15:09:47 -0700743 int verbose = 0;
Bill Richardsona08b5c92010-06-30 21:59:43 -0700744 uint64_t pad = DEFAULT_PADDING;
Randall Spangler7d6898d2010-06-11 09:22:13 -0700745 int mode = 0;
746 int parse_error = 0;
747 char* e;
Bill Richardsona08b5c92010-06-30 21:59:43 -0700748 int i,r;
749 blob_t *bp;
Randall Spangler7d6898d2010-06-11 09:22:13 -0700750
Bill Richardsona08b5c92010-06-30 21:59:43 -0700751
752 char *progname = strrchr(argv[0], '/');
753 if (progname)
754 progname++;
755 else
756 progname = argv[0];
757
vbendebb2b0fcc2010-07-15 15:09:47 -0700758 while (((i = getopt_long(argc, argv, ":", long_opts, NULL)) != -1) &&
759 !parse_error) {
Randall Spangler7d6898d2010-06-11 09:22:13 -0700760 switch (i) {
vbendebb2b0fcc2010-07-15 15:09:47 -0700761 default:
Randall Spangler7d6898d2010-06-11 09:22:13 -0700762 case '?':
763 /* Unhandled option */
Randall Spangler7d6898d2010-06-11 09:22:13 -0700764 parse_error = 1;
765 break;
766
Bill Richardson4f36ef32010-08-09 17:50:14 -0700767 case 0:
768 /* silently handled option */
769 break;
770
Randall Spangler7d6898d2010-06-11 09:22:13 -0700771 case OPT_MODE_PACK:
Bill Richardsona08b5c92010-06-30 21:59:43 -0700772 case OPT_MODE_REPACK:
Randall Spangler7d6898d2010-06-11 09:22:13 -0700773 case OPT_MODE_VERIFY:
vbendebb2b0fcc2010-07-15 15:09:47 -0700774 if (mode && (mode != i)) {
775 fprintf(stderr, "Only single mode can be specified\n");
776 parse_error = 1;
777 break;
778 }
Randall Spangler7d6898d2010-06-11 09:22:13 -0700779 mode = i;
780 filename = optarg;
781 break;
782
Bill Richardsona08b5c92010-06-30 21:59:43 -0700783 case OPT_OLDBLOB:
784 oldfile = optarg;
785 break;
786
Randall Spangler7d6898d2010-06-11 09:22:13 -0700787 case OPT_KEYBLOCK:
788 key_block_file = optarg;
789 break;
790
791 case OPT_SIGNPUBKEY:
792 signpubkey = optarg;
793 break;
794
795 case OPT_SIGNPRIVATE:
796 signprivate = optarg;
797 break;
798
799 case OPT_VMLINUZ:
800 vmlinuz = optarg;
801 break;
802
803 case OPT_BOOTLOADER:
804 bootloader = optarg;
805 break;
806
807 case OPT_CONFIG:
808 config_file = optarg;
809 break;
810
Bill Richardsona08b5c92010-06-30 21:59:43 -0700811 case OPT_VBLOCKONLY:
812 vblockonly = 1;
813 break;
814
Randall Spangler7d6898d2010-06-11 09:22:13 -0700815 case OPT_VERSION:
816 version = strtoul(optarg, &e, 0);
817 if (!*optarg || (e && *e)) {
Bill Richardsona08b5c92010-06-30 21:59:43 -0700818 fprintf(stderr, "Invalid --version\n");
Randall Spangler7d6898d2010-06-11 09:22:13 -0700819 parse_error = 1;
820 }
821 break;
822
823 case OPT_PAD:
824 pad = strtoul(optarg, &e, 0);
825 if (!*optarg || (e && *e)) {
Bill Richardsona08b5c92010-06-30 21:59:43 -0700826 fprintf(stderr, "Invalid --pad\n");
Randall Spangler7d6898d2010-06-11 09:22:13 -0700827 parse_error = 1;
828 }
829 break;
vbendebb2b0fcc2010-07-15 15:09:47 -0700830
831 case OPT_VERBOSE:
832 verbose = 1;
833 break;
Randall Spangler7d6898d2010-06-11 09:22:13 -0700834 }
835 }
836
837 if (parse_error)
Bill Richardsona08b5c92010-06-30 21:59:43 -0700838 return PrintHelp(progname);
Randall Spangler7d6898d2010-06-11 09:22:13 -0700839
840 switch(mode) {
841 case OPT_MODE_PACK:
Bill Richardsona08b5c92010-06-30 21:59:43 -0700842 bp = NewBlob(version, vmlinuz, bootloader, config_file);
843 if (!bp)
844 return 1;
845 r = Pack(filename, key_block_file, signprivate, bp, pad, vblockonly);
846 FreeBlob(bp);
847 return r;
848
849 case OPT_MODE_REPACK:
vbendeb858fffb2010-10-06 09:51:44 -0700850 if (!config_file && !key_block_file && !version) {
vbendebb2b0fcc2010-07-15 15:09:47 -0700851 fprintf(stderr,
vbendeb858fffb2010-10-06 09:51:44 -0700852 "You must supply at least one of "
853 "--config, --keyblock or --version\n");
vbendebb2b0fcc2010-07-15 15:09:47 -0700854 return 1;
855 }
856
Bill Richardsona08b5c92010-06-30 21:59:43 -0700857 bp = OldBlob(oldfile);
858 if (!bp)
859 return 1;
vbendebb2b0fcc2010-07-15 15:09:47 -0700860 r = ReplaceConfig(bp, config_file);
861 if (!r) {
vbendeb858fffb2010-10-06 09:51:44 -0700862 if (version) {
863 bp->kernel_version = version;
864 }
vbendebb2b0fcc2010-07-15 15:09:47 -0700865 r = Pack(filename, key_block_file, signprivate, bp, pad, vblockonly);
866 }
Bill Richardsona08b5c92010-06-30 21:59:43 -0700867 FreeBlob(bp);
868 return r;
869
Randall Spangler7d6898d2010-06-11 09:22:13 -0700870 case OPT_MODE_VERIFY:
vbendebb2b0fcc2010-07-15 15:09:47 -0700871 return Verify(filename, signpubkey, verbose);
Bill Richardsona08b5c92010-06-30 21:59:43 -0700872
Randall Spangler7d6898d2010-06-11 09:22:13 -0700873 default:
Bill Richardsona08b5c92010-06-30 21:59:43 -0700874 fprintf(stderr,
875 "You must specify a mode: --pack, --repack or --verify\n");
876 return PrintHelp(progname);
Randall Spangler7d6898d2010-06-11 09:22:13 -0700877 }
878}