blob: f4c682ee10cd91ac9619154cb8c14c94fc9e67bf [file] [log] [blame]
Bill Richardson15dc6fc2014-09-02 14:45:44 -07001/*
2 * Copyright 2014 The Chromium OS Authors. All rights reserved.
3 * Use of this source code is governed by a BSD-style license that can be
4 * found in the LICENSE file.
5 */
6#include <errno.h>
7#include <fcntl.h>
8#include <getopt.h>
9#include <inttypes.h>
10#include <limits.h>
11#include <stddef.h>
12#include <stdint.h>
13#include <stdio.h>
14#include <stdlib.h>
15#include <string.h>
16#include <sys/stat.h>
17#include <sys/types.h>
18#include <unistd.h>
19
20#include "bmpblk_header.h"
21#include "fmap.h"
22#include "futility.h"
23#include "gbb_header.h"
24#include "host_common.h"
Bill Richardson5f2696d2014-09-23 22:03:56 -070025#include "kernel_blob.h"
Bill Richardson15dc6fc2014-09-02 14:45:44 -070026#include "traversal.h"
27#include "util_misc.h"
Bill Richardson5f2696d2014-09-23 22:03:56 -070028#include "vb1_helper.h"
Bill Richardson15dc6fc2014-09-02 14:45:44 -070029#include "vboot_common.h"
30
31/* Local values for cb_area_s._flags */
32enum callback_flags {
33 AREA_IS_VALID = 0x00000001,
34};
35
36/* Local structure for args, etc. */
Bill Richardson5f2696d2014-09-23 22:03:56 -070037static struct local_data_s {
Bill Richardson15dc6fc2014-09-02 14:45:44 -070038 VbPrivateKey *signprivate;
39 VbKeyBlockHeader *keyblock;
40 VbPublicKey *kernel_subkey;
41 VbPrivateKey *devsignprivate;
42 VbKeyBlockHeader *devkeyblock;
43 uint32_t version;
Bill Richardson5f2696d2014-09-23 22:03:56 -070044 int version_specified;
Bill Richardson15dc6fc2014-09-02 14:45:44 -070045 uint32_t flags;
Bill Richardsona19b00d2014-09-04 23:20:43 -070046 int flags_specified;
Bill Richardson15dc6fc2014-09-02 14:45:44 -070047 char *loemdir;
48 char *loemid;
Bill Richardson5f2696d2014-09-23 22:03:56 -070049 uint8_t *bootloader_data;
50 uint64_t bootloader_size;
51 uint8_t *config_data;
52 uint64_t config_size;
53 enum arch_t arch;
54 uint32_t kloadaddr;
55 uint32_t padding;
56 int vblockonly;
57 char *outfile;
58 int create_new_outfile;
Bill Richardson15dc6fc2014-09-02 14:45:44 -070059} option = {
60 .version = 1,
Bill Richardson5f2696d2014-09-23 22:03:56 -070061 .arch = ARCH_UNSPECIFIED,
62 .kloadaddr = CROS_32BIT_ENTRY_ADDR,
63 .padding = 65536,
Bill Richardson15dc6fc2014-09-02 14:45:44 -070064};
65
66
Bill Richardson5f2696d2014-09-23 22:03:56 -070067/* Helper to complain about invalid args. Returns num errors discovered */
68static int no_opt_if(int expr, const char *optname)
Bill Richardson15dc6fc2014-09-02 14:45:44 -070069{
Bill Richardson5f2696d2014-09-23 22:03:56 -070070 if (expr) {
71 fprintf(stderr, "Missing --%s option\n", optname);
72 return 1;
73 }
74 return 0;
Bill Richardson15dc6fc2014-09-02 14:45:44 -070075}
76
Bill Richardson5f2696d2014-09-23 22:03:56 -070077/* This wraps/signs a public key, producing a keyblock. */
78int futil_cb_sign_pubkey(struct futil_traverse_state_s *state)
Bill Richardson15dc6fc2014-09-02 14:45:44 -070079{
Bill Richardson5f2696d2014-09-23 22:03:56 -070080 fprintf(stderr, "Don't know how to sign %s yet\n", state->name);
Bill Richardson15dc6fc2014-09-02 14:45:44 -070081 return 1;
82}
83
84/*
85 * This handles FW_MAIN_A and FW_MAIN_B while processing a BIOS image.
Bill Richardson15dc6fc2014-09-02 14:45:44 -070086 * The data in state->my_area is just the RW firmware blob, so there's nothing
87 * useful to show about it. We'll just mark it as present so when we encounter
88 * corresponding VBLOCK area, we'll have this to verify.
89 */
90int futil_cb_sign_fw_main(struct futil_traverse_state_s *state)
91{
92 state->my_area->_flags |= AREA_IS_VALID;
93 return 0;
94}
95
Bill Richardson779796f2014-09-23 11:47:40 -070096/*
97 * This handles VBLOCK_A and VBLOCK_B while processing a BIOS image.
98 * We don't do any signing here. We just check to see if the VBLOCK
99 * area contains a firmware preamble.
100 */
Bill Richardson5f2696d2014-09-23 22:03:56 -0700101int futil_cb_sign_fw_vblock(struct futil_traverse_state_s *state)
Bill Richardson15dc6fc2014-09-02 14:45:44 -0700102{
103 VbKeyBlockHeader *key_block = (VbKeyBlockHeader *)state->my_area->buf;
Bill Richardsone0519752014-09-03 14:20:10 -0700104 uint32_t len = state->my_area->len;
Bill Richardson15dc6fc2014-09-02 14:45:44 -0700105
Bill Richardsone0519752014-09-03 14:20:10 -0700106 /*
107 * If we have a valid keyblock and fw_preamble, then we can use them to
108 * determine the size of the firmware body. Otherwise, we'll have to
109 * just sign the whole region.
110 */
111 if (VBOOT_SUCCESS != KeyBlockVerify(key_block, len, NULL, 1)) {
112 fprintf(stderr, "Warning: %s keyblock is invalid. "
113 "Signing the entire FW FMAP region...\n",
Bill Richardson779796f2014-09-23 11:47:40 -0700114 state->name);
Bill Richardsone0519752014-09-03 14:20:10 -0700115 goto whatever;
116 }
117
118 RSAPublicKey *rsa = PublicKeyToRSA(&key_block->data_key);
119 if (!rsa) {
120 fprintf(stderr, "Warning: %s public key is invalid. "
121 "Signing the entire FW FMAP region...\n",
122 state->name);
123 goto whatever;
124 }
Bill Richardson15dc6fc2014-09-02 14:45:44 -0700125 uint32_t more = key_block->key_block_size;
126 VbFirmwarePreambleHeader *preamble =
127 (VbFirmwarePreambleHeader *)(state->my_area->buf + more);
128 uint32_t fw_size = preamble->body_signature.data_size;
Bill Richardsone0519752014-09-03 14:20:10 -0700129 struct cb_area_s *fw_body_area = 0;
Bill Richardson15dc6fc2014-09-02 14:45:44 -0700130
131 switch (state->component) {
132 case CB_FMAP_VBLOCK_A:
133 fw_body_area = &state->cb_area[CB_FMAP_FW_MAIN_A];
Bill Richardson08efd1e2014-09-04 22:53:41 -0700134 /* Preserve the flags if they're not specified */
Bill Richardsona19b00d2014-09-04 23:20:43 -0700135 if (!option.flags_specified)
Bill Richardson08efd1e2014-09-04 22:53:41 -0700136 option.flags = preamble->flags;
Bill Richardson15dc6fc2014-09-02 14:45:44 -0700137 break;
138 case CB_FMAP_VBLOCK_B:
139 fw_body_area = &state->cb_area[CB_FMAP_FW_MAIN_B];
140 break;
141 default:
142 DIE;
143 }
144
145 if (fw_size > fw_body_area->len) {
146 fprintf(stderr,
147 "%s says the firmware is larger than we have\n",
Bill Richardson779796f2014-09-23 11:47:40 -0700148 state->name);
Bill Richardson15dc6fc2014-09-02 14:45:44 -0700149 return 1;
150 }
151
152 /* Update the firmware size */
153 fw_body_area->len = fw_size;
154
Bill Richardsone0519752014-09-03 14:20:10 -0700155whatever:
Bill Richardson15dc6fc2014-09-02 14:45:44 -0700156 state->my_area->_flags |= AREA_IS_VALID;
157
158 return 0;
159}
160
Bill Richardson5f2696d2014-09-23 22:03:56 -0700161int futil_cb_create_kernel_part(struct futil_traverse_state_s *state)
162{
163 uint8_t *vmlinuz_data, *kblob_data, *vblock_data;
164 uint64_t vmlinuz_size, kblob_size, vblock_size;
165 int rv;
166
167 vmlinuz_data = state->my_area->buf;
168 vmlinuz_size = state->my_area->len;
169
170 kblob_data = CreateKernelBlob(
171 vmlinuz_data, vmlinuz_size,
172 option.arch, option.kloadaddr,
173 option.config_data, option.config_size,
174 option.bootloader_data, option.bootloader_size,
175 &kblob_size);
176 if (!kblob_data) {
177 fprintf(stderr, "Unable to create kernel blob\n");
178 return 1;
179 }
180 Debug("kblob_size = 0x%" PRIx64 "\n", kblob_size);
181
182 vblock_data = SignKernelBlob(kblob_data, kblob_size, option.padding,
183 option.version, option.kloadaddr,
184 option.keyblock, option.signprivate,
185 &vblock_size);
186 if (!vblock_data) {
187 fprintf(stderr, "Unable to sign kernel blob\n");
188 free(kblob_data);
189 return 1;
190 }
191 Debug("vblock_size = 0x%" PRIx64 "\n", vblock_size);
192
193 /* We should be creating a completely new output file.
194 * If not, something's wrong. */
195 if (!option.create_new_outfile)
196 DIE;
197
198 if (option.vblockonly)
199 rv = WriteSomeParts(option.outfile,
200 vblock_data, vblock_size,
201 NULL, 0);
202 else
203 rv = WriteSomeParts(option.outfile,
204 vblock_data, vblock_size,
205 kblob_data, kblob_size);
206
207 free(vblock_data);
208 free(kblob_data);
209 return rv;
210}
211
212int futil_cb_resign_kernel_part(struct futil_traverse_state_s *state)
213{
214 uint8_t *kpart_data, *kblob_data, *vblock_data;
215 uint64_t kpart_size, kblob_size, vblock_size;
216 VbKeyBlockHeader *keyblock = NULL;
217 VbKernelPreambleHeader *preamble = NULL;
218 int rv = 0;
219
220 kpart_data = state->my_area->buf;
221 kpart_size = state->my_area->len;
222
223 /* Note: This just sets some static pointers. It doesn't malloc. */
224 kblob_data = UnpackKPart(kpart_data, kpart_size, option.padding,
225 &keyblock, &preamble, &kblob_size);
226
227 if (!kblob_data) {
228 fprintf(stderr, "Unable to unpack kernel partition\n");
229 return 1;
230 }
231
232 /*
233 * We don't let --kloadaddr change when resigning, because the original
234 * vbutil_kernel program didn't do it right. Since obviously no one
235 * ever noticed, we'll maintain bug-compatibility by just not allowing
236 * it here either. To enable it, we'd need to update the zeropage
237 * table's cmd_line_ptr as well as the preamble.
238 */
239 option.kloadaddr = preamble->body_load_address;
240
241 /* Replace the config if asked */
242 if (option.config_data &&
243 0 != UpdateKernelBlobConfig(kblob_data, kblob_size,
244 option.config_data,
245 option.config_size)) {
246 fprintf(stderr, "Unable to update config\n");
247 return 1;
248 }
249
250 /* Preserve the version unless a new one is given */
251 if (!option.version_specified)
252 option.version = preamble->kernel_version;
253
254 /* Replace the keyblock if asked */
255 if (option.keyblock)
256 keyblock = option.keyblock;
257
258 /* Compute the new signature */
259 vblock_data = SignKernelBlob(kblob_data, kblob_size, option.padding,
260 option.version, option.kloadaddr,
261 keyblock, option.signprivate,
262 &vblock_size);
263 if (!vblock_data) {
264 fprintf(stderr, "Unable to sign kernel blob\n");
265 return 1;
266 }
267 Debug("vblock_size = 0x%" PRIx64 "\n", vblock_size);
268
269 if (option.vblockonly) {
270 /* If we're only writing the vblock, then we should be doing it
271 * into a new file. */
272 rv = WriteSomeParts(option.outfile,
273 vblock_data, vblock_size,
274 NULL, 0);
275 } else {
276 /* If we're writing the whole thing, then the output is
277 * the same size (and possibly the same file) as the input.
278 * Either way, it's mmap'ed so modifications to the buffer
279 * will get flushed to disk when we close the file. */
280 Memcpy(kpart_data, vblock_data, vblock_size);
281 }
282
283 free(vblock_data);
284 return rv;
285}
286
287
288int futil_cb_sign_raw_firmware(struct futil_traverse_state_s *state)
289{
290 fprintf(stderr, "Don't know how to sign %s yet\n", state->name);
291 return 1;
292}
293
294
Bill Richardson15dc6fc2014-09-02 14:45:44 -0700295int futil_cb_sign_begin(struct futil_traverse_state_s *state)
296{
297 if (state->in_type == FILE_TYPE_UNKNOWN) {
298 fprintf(stderr, "Unable to determine type of %s\n",
299 state->in_filename);
300 return 1;
301 }
302
303 return 0;
304}
305
306static int write_new_preamble(struct cb_area_s *vblock,
307 struct cb_area_s *fw_body,
308 VbPrivateKey *signkey,
309 VbKeyBlockHeader *keyblock)
310{
311 VbSignature *body_sig;
312 VbFirmwarePreambleHeader *preamble;
313
314 body_sig = CalculateSignature(fw_body->buf, fw_body->len, signkey);
315 if (!body_sig) {
316 fprintf(stderr, "Error calculating body signature\n");
317 return 1;
318 }
319
320 preamble = CreateFirmwarePreamble(option.version,
321 option.kernel_subkey,
322 body_sig,
323 signkey,
324 option.flags);
325 if (!preamble) {
326 fprintf(stderr, "Error creating firmware preamble.\n");
327 free(body_sig);
328 return 1;
329 }
330
331 /* Write the new keyblock */
332 uint32_t more = keyblock->key_block_size;
333 memcpy(vblock->buf, keyblock, more);
334 /* and the new preamble */
335 memcpy(vblock->buf + more, preamble, preamble->preamble_size);
336
337 free(preamble);
338 free(body_sig);
339
340 return 0;
341}
342
343static int write_loem(const char *ab, struct cb_area_s *vblock)
344{
345 char filename[PATH_MAX];
346 int n;
347 n = snprintf(filename, sizeof(filename), "%s/vblock_%s.%s",
348 option.loemdir ? option.loemdir : ".",
349 ab, option.loemid);
350 if (n >= sizeof(filename)) {
351 fprintf(stderr, "LOEM args produce bogus filename\n");
352 return 1;
353 }
354
355 FILE *fp = fopen(filename, "w");
356 if (!fp) {
357 fprintf(stderr, "Can't open %s for writing: %s\n",
358 filename, strerror(errno));
359 return 1;
360 }
361
362 if (1 != fwrite(vblock->buf, vblock->len, 1, fp)) {
363 fprintf(stderr, "Can't write to %s: %s\n",
364 filename, strerror(errno));
365 fclose(fp);
366 return 1;
367 }
368 if (fclose(fp)) {
369 fprintf(stderr, "Failed closing loem output: %s\n",
370 strerror(errno));
371 return 1;
372 }
373
374 return 0;
375}
376
Bill Richardson5f2696d2014-09-23 22:03:56 -0700377/* This signs a full BIOS image after it's been traversed. */
378static int sign_bios_at_end(struct futil_traverse_state_s *state)
Bill Richardson15dc6fc2014-09-02 14:45:44 -0700379{
380 struct cb_area_s *vblock_a = &state->cb_area[CB_FMAP_VBLOCK_A];
381 struct cb_area_s *vblock_b = &state->cb_area[CB_FMAP_VBLOCK_B];
382 struct cb_area_s *fw_a = &state->cb_area[CB_FMAP_FW_MAIN_A];
383 struct cb_area_s *fw_b = &state->cb_area[CB_FMAP_FW_MAIN_B];
384 int retval = 0;
385
386 if (state->errors ||
387 !(vblock_a->_flags & AREA_IS_VALID) ||
388 !(vblock_b->_flags & AREA_IS_VALID) ||
389 !(fw_a->_flags & AREA_IS_VALID) ||
390 !(fw_b->_flags & AREA_IS_VALID)) {
391 fprintf(stderr, "Something's wrong. Not changing anything\n");
392 return 1;
393 }
394
395 /* Do A & B differ ? */
396 if (fw_a->len != fw_b->len ||
397 memcmp(fw_a->buf, fw_b->buf, fw_a->len)) {
398 /* Yes, must use DEV keys for A */
399 if (!option.devsignprivate || !option.devkeyblock) {
400 fprintf(stderr,
401 "FW A & B differ. DEV keys are required.\n");
402 return 1;
403 }
404 retval |= write_new_preamble(vblock_a, fw_a,
405 option.devsignprivate,
406 option.devkeyblock);
407 } else {
408 retval |= write_new_preamble(vblock_a, fw_a,
409 option.signprivate,
410 option.keyblock);
411 }
412
413 /* FW B is always normal keys */
414 retval |= write_new_preamble(vblock_b, fw_b,
415 option.signprivate,
416 option.keyblock);
417
418
419
420
421 if (option.loemid) {
422 retval |= write_loem("A", vblock_a);
423 retval |= write_loem("B", vblock_b);
424 }
425
426 return retval;
427}
428
Bill Richardson5f2696d2014-09-23 22:03:56 -0700429int futil_cb_sign_end(struct futil_traverse_state_s *state)
430{
431 switch (state->in_type) {
432 case FILE_TYPE_BIOS_IMAGE:
433 case FILE_TYPE_OLD_BIOS_IMAGE:
434 return sign_bios_at_end(state);
435
436 default:
437 /* Any other cleanup needed? */
438 break;
439 }
440
441 return state->errors;
442}
443
Bill Richardson15dc6fc2014-09-02 14:45:44 -0700444static const char usage[] = "\n"
Bill Richardson5f2696d2014-09-23 22:03:56 -0700445 "Usage: " MYNAME " %s [PARAMS] INFILE [OUTFILE]\n"
Bill Richardson15dc6fc2014-09-02 14:45:44 -0700446 "\n"
Bill Richardson5f2696d2014-09-23 22:03:56 -0700447 "Where INFILE is a\n"
Bill Richardson15dc6fc2014-09-02 14:45:44 -0700448 "\n"
Bill Richardson5f2696d2014-09-23 22:03:56 -0700449 " complete firmware image (bios.bin)\n"
450 " raw linux kernel; OUTFILE is a kernel partition image\n"
451 " kernel partition image (/dev/sda2, /dev/mmcblk0p2)\n";
452
453static const char usage_bios[] = "\n"
454 "-----------------------------------------------------------------\n"
455 "To sign a complete firmware image (bios.bin):\n"
456 "\n"
457 "Required PARAMS:\n"
Bill Richardson15dc6fc2014-09-02 14:45:44 -0700458 " -s|--signprivate FILE.vbprivk The private firmware data key\n"
459 " -b|--keyblock FILE.keyblock The keyblock containing the\n"
460 " public firmware data key\n"
461 " -k|--kernelkey FILE.vbpubk The public kernel subkey\n"
Bill Richardson5f2696d2014-09-23 22:03:56 -0700462 " [--infile] INFILE Input firmware image (modified\n"
463 " in place if no OUTFILE given)\n"
Bill Richardson15dc6fc2014-09-02 14:45:44 -0700464 "\n"
465 "These are required if the A and B firmware differ:\n"
466 " -S|--devsign FILE.vbprivk The DEV private firmware data key\n"
467 " -B|--devkeyblock FILE.keyblock The keyblock containing the\n"
468 " DEV public firmware data key\n"
469 "\n"
Bill Richardson5f2696d2014-09-23 22:03:56 -0700470 "Optional PARAMS:\n"
Bill Richardson08efd1e2014-09-04 22:53:41 -0700471 " -v|--version NUM The firmware version number"
472 " (default %d)\n"
473 " -f|--flags NUM The preamble flags value"
474 " (default is\n"
475 " unchanged, or 0 if unknown)\n"
Bill Richardson15dc6fc2014-09-02 14:45:44 -0700476 " -d|--loemdir DIR Local OEM output vblock directory\n"
477 " -l|--loemid STRING Local OEM vblock suffix\n"
Bill Richardson5f2696d2014-09-23 22:03:56 -0700478 " [--outfile] OUTFILE Output firmware image\n";
479
480static const char usage_new_kpart[] = "\n"
481 "-----------------------------------------------------------------\n"
482 "To create a new kernel parition image (/dev/sda2, /dev/mmcblk0p2):\n"
483 "\n"
484 "Required PARAMS:\n"
485 " -s|--signprivate FILE.vbprivk"
486 " The private key to sign the kernel blob\n"
487 " -b|--keyblock FILE.keyblock The keyblock containing the public\n"
488 " key to verify the kernel blob\n"
489 " -v|--version NUM The kernel version number\n"
490 " --bootloader FILE Bootloader stub\n"
491 " --config FILE The kernel commandline file\n"
492 " --arch ARCH The CPU architecture (one of\n"
493 " x86|amd64, arm|aarch64, mips)\n"
494 " [--vmlinuz] INFILE Linux kernel bzImage file\n"
495 " [--outfile] OUTFILE Output kernel partition or vblock\n"
496 "\n"
497 "Optional PARAMS:\n"
498 " --kloadaddr NUM"
499 " RAM address to load the kernel body\n"
500 " (default 0x%x)\n"
501 " --pad NUM The vblock padding size in bytes\n"
502 " (default 0x%x)\n"
503 " --vblockonly Emit just the vblock (requires a\n"
504 " distinct outfile)\n";
505
506static const char usage_old_kpart[] = "\n"
507 "-----------------------------------------------------------------\n"
508 "To resign an existing kernel parition (/dev/sda2, /dev/mmcblk0p2):\n"
509 "\n"
510 "Required PARAMS:\n"
511 " -s|--signprivate FILE.vbprivk"
512 " The private key to sign the kernel blob\n"
513 " [--infile] INFILE Input kernel partition (modified\n"
514 " in place if no OUTFILE given)\n"
515 "\n"
516 "Optional PARAMS:\n"
517 " -b|--keyblock FILE.keyblock The keyblock containing the public\n"
518 " key to verify the kernel blob\n"
519 " -v|--version NUM The kernel version number\n"
520 " --config FILE The kernel commandline file\n"
521 " --pad NUM The vblock padding size in bytes\n"
522 " (default 0x%x)\n"
523 " [--outfile] OUTFILE Output kernel partition or vblock\n"
524 " --vblockonly Emit just the vblock (requires a\n"
525 " distinct OUTFILE)\n"
Bill Richardson15dc6fc2014-09-02 14:45:44 -0700526 "\n";
527
Bill Richardson779796f2014-09-23 11:47:40 -0700528static void print_help(const char *prog)
Bill Richardson15dc6fc2014-09-02 14:45:44 -0700529{
Bill Richardson5f2696d2014-09-23 22:03:56 -0700530 printf(usage, prog);
531 printf(usage_bios, option.version);
532 printf(usage_new_kpart, option.kloadaddr, option.padding);
533 printf(usage_old_kpart, option.padding);
Bill Richardson15dc6fc2014-09-02 14:45:44 -0700534}
535
Bill Richardson5f2696d2014-09-23 22:03:56 -0700536enum no_short_opts {
537 OPT_FV = 1000,
538 OPT_INFILE, /* aka "--vmlinuz" */
539 OPT_OUTFILE,
540 OPT_BOOTLOADER,
541 OPT_CONFIG,
542 OPT_ARCH,
543 OPT_KLOADADDR,
544 OPT_PADDING,
545};
546
Bill Richardson15dc6fc2014-09-02 14:45:44 -0700547static const struct option long_opts[] = {
548 /* name hasarg *flag val */
Bill Richardson5f2696d2014-09-23 22:03:56 -0700549 {"signprivate", 1, NULL, 's'},
550 {"keyblock", 1, NULL, 'b'},
551 {"kernelkey", 1, NULL, 'k'},
552 {"devsign", 1, NULL, 'S'},
553 {"devkeyblock", 1, NULL, 'B'},
554 {"version", 1, NULL, 'v'},
555 {"flags", 1, NULL, 'f'},
556 {"loemdir", 1, NULL, 'd'},
557 {"loemid", 1, NULL, 'l'},
558 {"fv", 1, NULL, OPT_FV},
559 {"infile", 1, NULL, OPT_INFILE},
560 {"datapubkey", 1, NULL, OPT_INFILE}, /* alias */
561 {"vmlinuz", 1, NULL, OPT_INFILE}, /* alias */
562 {"outfile", 1, NULL, OPT_OUTFILE},
563 {"bootloader", 1, NULL, OPT_BOOTLOADER},
564 {"config", 1, NULL, OPT_CONFIG},
565 {"arch", 1, NULL, OPT_ARCH},
566 {"kloadaddr", 1, NULL, OPT_KLOADADDR},
567 {"pad", 1, NULL, OPT_PADDING},
568 {"vblockonly", 0, &option.vblockonly, 1},
569 {"debug", 0, &debugging_enabled, 1},
570 {NULL, 0, NULL, 0},
Bill Richardson15dc6fc2014-09-02 14:45:44 -0700571};
572static char *short_opts = ":s:b:k:S:B:v:f:d:l:";
573
574static int do_sign(int argc, char *argv[])
575{
576 char *infile = 0;
Bill Richardson5f2696d2014-09-23 22:03:56 -0700577 int i;
578 int ifd = -1;
Bill Richardson15dc6fc2014-09-02 14:45:44 -0700579 int errorcnt = 0;
580 struct futil_traverse_state_s state;
Bill Richardsonb0f1cc52014-09-24 00:23:56 -0700581 uint8_t *buf;
582 uint32_t buf_len;
Bill Richardson5f2696d2014-09-23 22:03:56 -0700583 char *e = 0;
584 enum futil_file_type type;
585 int inout_file_count = 0;
586 int mapping;
Bill Richardson15dc6fc2014-09-02 14:45:44 -0700587
588 opterr = 0; /* quiet, you */
589 while ((i = getopt_long(argc, argv, short_opts, long_opts, 0)) != -1) {
590 switch (i) {
591 case 's':
592 option.signprivate = PrivateKeyRead(optarg);
593 if (!option.signprivate) {
594 fprintf(stderr, "Error reading %s\n", optarg);
595 errorcnt++;
596 }
597 break;
598 case 'b':
599 option.keyblock = KeyBlockRead(optarg);
600 if (!option.keyblock) {
601 fprintf(stderr, "Error reading %s\n", optarg);
602 errorcnt++;
603 }
604 break;
605 case 'k':
606 option.kernel_subkey = PublicKeyRead(optarg);
607 if (!option.kernel_subkey) {
608 fprintf(stderr, "Error reading %s\n", optarg);
609 errorcnt++;
610 }
611 break;
612 case 'S':
613 option.devsignprivate = PrivateKeyRead(optarg);
614 if (!option.devsignprivate) {
615 fprintf(stderr, "Error reading %s\n", optarg);
616 errorcnt++;
617 }
618 break;
619 case 'B':
620 option.devkeyblock = KeyBlockRead(optarg);
621 if (!option.devkeyblock) {
622 fprintf(stderr, "Error reading %s\n", optarg);
623 errorcnt++;
624 }
625 break;
626 case 'v':
Bill Richardson5f2696d2014-09-23 22:03:56 -0700627 option.version_specified = 1;
Bill Richardson15dc6fc2014-09-02 14:45:44 -0700628 option.version = strtoul(optarg, &e, 0);
629 if (!*optarg || (e && *e)) {
630 fprintf(stderr,
631 "Invalid --version \"%s\"\n", optarg);
632 errorcnt++;
633 }
634 break;
635
636 case 'f':
Bill Richardsona19b00d2014-09-04 23:20:43 -0700637 option.flags_specified = 1;
Bill Richardson15dc6fc2014-09-02 14:45:44 -0700638 option.flags = strtoul(optarg, &e, 0);
639 if (!*optarg || (e && *e)) {
640 fprintf(stderr,
641 "Invalid --flags \"%s\"\n", optarg);
642 errorcnt++;
643 }
Bill Richardsona19b00d2014-09-04 23:20:43 -0700644 break;
Bill Richardson15dc6fc2014-09-02 14:45:44 -0700645 case 'd':
646 option.loemdir = optarg;
647 break;
648 case 'l':
649 option.loemid = optarg;
650 break;
Bill Richardson5f2696d2014-09-23 22:03:56 -0700651 case OPT_INFILE: /* aka "--vmlinuz" */
652 inout_file_count++;
653 infile = optarg;
654 break;
655 case OPT_OUTFILE:
656 inout_file_count++;
657 option.outfile = optarg;
658 break;
659 case OPT_BOOTLOADER:
660 option.bootloader_data = ReadFile(
661 optarg, &option.bootloader_size);
662 if (!option.bootloader_data) {
663 fprintf(stderr,
664 "Error reading bootloader file: %s\n",
665 strerror(errno));
666 errorcnt++;
667 }
668 Debug("bootloader file size=0x%" PRIx64 "\n",
669 option.bootloader_size);
670 break;
671 case OPT_CONFIG:
672 option.config_data = ReadConfigFile(
673 optarg, &option.config_size);
674 if (!option.config_data) {
675 fprintf(stderr,
676 "Error reading config file: %s\n",
677 strerror(errno));
678 errorcnt++;
679 }
680 break;
681 case OPT_ARCH:
682 /* check the first 3 characters to also match x86_64 */
683 if ((!strncasecmp(optarg, "x86", 3)) ||
684 (!strcasecmp(optarg, "amd64")))
685 option.arch = ARCH_X86;
686 else if ((!strcasecmp(optarg, "arm")) ||
687 (!strcasecmp(optarg, "aarch64")))
688 option.arch = ARCH_ARM;
689 else if (!strcasecmp(optarg, "mips"))
690 option.arch = ARCH_MIPS;
691 else {
692 fprintf(stderr,
693 "Unknown architecture: \"%s\"\n",
694 optarg);
695 errorcnt++;
696 }
697 break;
698 case OPT_KLOADADDR:
699 option.kloadaddr = strtoul(optarg, &e, 0);
700 if (!*optarg || (e && *e)) {
701 fprintf(stderr,
702 "Invalid --kloadaddr \"%s\"\n", optarg);
703 errorcnt++;
704 }
705 break;
706 case OPT_PADDING:
707 option.padding = strtoul(optarg, &e, 0);
708 if (!*optarg || (e && *e)) {
709 fprintf(stderr,
710 "Invalid --padding \"%s\"\n", optarg);
711 errorcnt++;
712 }
713 break;
Bill Richardson15dc6fc2014-09-02 14:45:44 -0700714 case '?':
715 if (optopt)
716 fprintf(stderr, "Unrecognized option: -%c\n",
717 optopt);
718 else
Bill Richardson5f2696d2014-09-23 22:03:56 -0700719 fprintf(stderr, "Unrecognized option: %s\n",
720 argv[optind - 1]);
Bill Richardson15dc6fc2014-09-02 14:45:44 -0700721 errorcnt++;
722 break;
723 case ':':
724 fprintf(stderr, "Missing argument to -%c\n", optopt);
725 errorcnt++;
726 break;
Bill Richardson5f2696d2014-09-23 22:03:56 -0700727 case 0: /* handled option */
728 break;
Bill Richardson15dc6fc2014-09-02 14:45:44 -0700729 default:
Bill Richardson5f2696d2014-09-23 22:03:56 -0700730 Debug("i=%d\n", i);
Bill Richardson15dc6fc2014-09-02 14:45:44 -0700731 DIE;
732 }
733 }
734
Bill Richardson5f2696d2014-09-23 22:03:56 -0700735 /* If we don't have an input file already, we need one */
736 if (!infile) {
737 if (argc - optind <= 0) {
738 errorcnt++;
739 fprintf(stderr, "ERROR: missing input filename\n");
740 goto done;
741 } else {
742 inout_file_count++;
743 infile = argv[optind++];
744 }
745 }
746
747 /* What are we looking at? */
748 type = futil_what_file_type(infile);
749
750 /* We may be able to infer the type based on the other args */
751 if (type == FILE_TYPE_UNKNOWN) {
752 if (option.bootloader_data || option.config_data
753 || option.arch != ARCH_UNSPECIFIED)
754 type = FILE_TYPE_RAW_KERNEL;
755 }
756
757 /* Check the arguments for the type of thing we want to sign */
758 switch (type) {
759 case FILE_TYPE_UNKNOWN:
Bill Richardson15dc6fc2014-09-02 14:45:44 -0700760 fprintf(stderr,
Bill Richardson5f2696d2014-09-23 22:03:56 -0700761 "Unable to determine the type of the input file\n");
Bill Richardson15dc6fc2014-09-02 14:45:44 -0700762 errorcnt++;
Bill Richardson5f2696d2014-09-23 22:03:56 -0700763 goto done;
764 case FILE_TYPE_KEYBLOCK:
765 fprintf(stderr, "Resigning a keyblock is kind of pointless.\n");
766 fprintf(stderr, "Just create a new one.\n");
Bill Richardson15dc6fc2014-09-02 14:45:44 -0700767 errorcnt++;
Bill Richardson15dc6fc2014-09-02 14:45:44 -0700768 break;
Bill Richardson5f2696d2014-09-23 22:03:56 -0700769 case FILE_TYPE_FW_PREAMBLE:
770 fprintf(stderr,
771 "%s IS a signature. Sign the firmware instead\n",
772 infile);
Bill Richardson15dc6fc2014-09-02 14:45:44 -0700773 break;
Bill Richardson5f2696d2014-09-23 22:03:56 -0700774 case FILE_TYPE_GBB:
775 fprintf(stderr, "There's no way to sign a GBB\n");
776 errorcnt++;
777 break;
778 case FILE_TYPE_BIOS_IMAGE:
779 case FILE_TYPE_OLD_BIOS_IMAGE:
780 errorcnt += no_opt_if(!option.signprivate, "signprivate");
781 errorcnt += no_opt_if(!option.keyblock, "keyblock");
782 errorcnt += no_opt_if(!option.kernel_subkey, "kernelkey");
783 break;
784 case FILE_TYPE_KERN_PREAMBLE:
785 errorcnt += no_opt_if(!option.signprivate, "signprivate");
786 if (option.vblockonly)
787 option.create_new_outfile = 1;
788 break;
789 case FILE_TYPE_RAW_KERNEL:
790 option.create_new_outfile = 1;
791 errorcnt += no_opt_if(!option.signprivate, "signprivate");
792 errorcnt += no_opt_if(!option.keyblock, "keyblock");
793 errorcnt += no_opt_if(!option.version_specified, "version");
794 errorcnt += no_opt_if(!option.bootloader_data, "bootloader");
795 errorcnt += no_opt_if(!option.config_data, "config");
796 errorcnt += no_opt_if(option.arch == ARCH_UNSPECIFIED, "arch");
Bill Richardson15dc6fc2014-09-02 14:45:44 -0700797 break;
798 default:
Bill Richardson5f2696d2014-09-23 22:03:56 -0700799 DIE;
Bill Richardson15dc6fc2014-09-02 14:45:44 -0700800 }
801
Bill Richardson5f2696d2014-09-23 22:03:56 -0700802 /* If we don't have an output file, we may need one */
803 if (!option.outfile) {
804 if (argc - optind > 0) {
805 /* We have an outfile arg, so use it. */
806 inout_file_count++;
807 option.outfile = argv[optind++];
808 } else {
809 if (option.create_new_outfile) {
810 /* A distinct outfile is required */
811 errorcnt++;
812 fprintf(stderr, "Missing output filename\n");
813 goto done;
814 } else {
815 /* We'll just modify the input file */
816 option.outfile = infile;
817 }
818 }
Bill Richardson15dc6fc2014-09-02 14:45:44 -0700819 }
820
Bill Richardson5f2696d2014-09-23 22:03:56 -0700821 Debug("type=%d\n", type);
822 Debug("option.create_new_outfile=%d\n", option.create_new_outfile);
823 Debug("inout_file_count=%d\n", inout_file_count);
824 Debug("infile=%s\n", infile);
825 Debug("option.outfile=%s\n", option.outfile);
826
827 if (argc - optind > 0) {
Bill Richardsonb0f1cc52014-09-24 00:23:56 -0700828 errorcnt++;
Bill Richardson5f2696d2014-09-23 22:03:56 -0700829 fprintf(stderr, "ERROR: too many arguments left over\n");
Bill Richardsonb0f1cc52014-09-24 00:23:56 -0700830 }
831
Bill Richardson5f2696d2014-09-23 22:03:56 -0700832 if (errorcnt)
833 goto done;
834
Bill Richardson15dc6fc2014-09-02 14:45:44 -0700835 memset(&state, 0, sizeof(state));
Bill Richardson15dc6fc2014-09-02 14:45:44 -0700836 state.op = FUTIL_OP_SIGN;
837
Bill Richardson5f2696d2014-09-23 22:03:56 -0700838 if (option.create_new_outfile) {
839 /* The input is read-only, the output is write-only. */
840 mapping = MAP_RO;
841 state.in_filename = infile;
842 ifd = open(infile, O_RDONLY);
843 if (ifd < 0) {
844 errorcnt++;
845 fprintf(stderr, "Can't open %s for reading: %s\n",
846 infile, strerror(errno));
847 goto done;
848 }
849 } else {
850 /* We'll read-modify-write the output file */
851 mapping = MAP_RW;
852 state.in_filename = option.outfile;
853 if (inout_file_count > 1)
854 futil_copy_file_or_die(infile, option.outfile);
855 ifd = open(option.outfile, O_RDWR);
856 if (ifd < 0) {
857 errorcnt++;
858 fprintf(stderr, "Can't open %s for writing: %s\n",
859 option.outfile, strerror(errno));
860 goto done;
861 }
862 }
Bill Richardson15dc6fc2014-09-02 14:45:44 -0700863
Bill Richardson5f2696d2014-09-23 22:03:56 -0700864 if (0 != futil_map_file(ifd, mapping, &buf, &buf_len)) {
Bill Richardson15dc6fc2014-09-02 14:45:44 -0700865 errorcnt++;
Bill Richardson5f2696d2014-09-23 22:03:56 -0700866 goto done;
867 }
868
869 errorcnt += futil_traverse(buf, buf_len, &state, type);
870
871 errorcnt += futil_unmap_file(ifd, MAP_RW, buf, buf_len);
872
873done:
874 if (ifd >= 0 && close(ifd)) {
875 errorcnt++;
876 fprintf(stderr, "Error when closing ifd: %s\n",
877 strerror(errno));
Bill Richardson15dc6fc2014-09-02 14:45:44 -0700878 }
879
880 if (option.signprivate)
881 free(option.signprivate);
882 if (option.keyblock)
883 free(option.keyblock);
884 if (option.kernel_subkey)
885 free(option.kernel_subkey);
886
Bill Richardson5f2696d2014-09-23 22:03:56 -0700887 if (errorcnt)
888 fprintf(stderr, "Use --help for usage instructions\n");
889
Bill Richardson15dc6fc2014-09-02 14:45:44 -0700890 return !!errorcnt;
891}
892
Bill Richardson779796f2014-09-23 11:47:40 -0700893DECLARE_FUTIL_COMMAND(sign, do_sign,
Bill Richardson5f2696d2014-09-23 22:03:56 -0700894 "Sign / resign various binary components",
Bill Richardson779796f2014-09-23 11:47:40 -0700895 print_help);