d13749bf5136283af712c560a30110e403cb5d00
[vboot.git] / futility / cmd_vbutil_kernel.c
1 /*
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  * Verified boot kernel utility
7  */
8
9 #include <errno.h>
10 #include <fcntl.h>
11 #include <getopt.h>
12 #include <inttypes.h>           /* For PRIu64 */
13 #ifndef HAVE_MACOS
14 #include <linux/fs.h>           /* For BLKGETSIZE64 */
15 #endif
16 #include <stdarg.h>
17 #include <stdio.h>
18 #include <string.h>
19 #include <sys/ioctl.h>
20 #include <sys/stat.h>
21 #include <unistd.h>
22
23 #include "file_type.h"
24 #include "futility.h"
25 #include "host_common.h"
26 #include "kernel_blob.h"
27 #include "vb1_helper.h"
28
29 static void Fatal(const char *format, ...)
30 {
31         va_list ap;
32         va_start(ap, format);
33         fprintf(stderr, "ERROR: ");
34         vfprintf(stderr, format, ap);
35         va_end(ap);
36         exit(1);
37 }
38
39 /* Global opts */
40 static int opt_verbose;
41 static int opt_vblockonly;
42 static uint64_t opt_pad = 65536;
43
44 /* Command line options */
45 enum {
46         OPT_MODE_PACK = 1000,
47         OPT_MODE_REPACK,
48         OPT_MODE_VERIFY,
49         OPT_MODE_GET_VMLINUZ,
50         OPT_ARCH,
51         OPT_OLDBLOB,
52         OPT_KLOADADDR,
53         OPT_KEYBLOCK,
54         OPT_SIGNPUBKEY,
55         OPT_SIGNPRIVATE,
56         OPT_VERSION,
57         OPT_VMLINUZ,
58         OPT_BOOTLOADER,
59         OPT_CONFIG,
60         OPT_VBLOCKONLY,
61         OPT_PAD,
62         OPT_VERBOSE,
63         OPT_MINVERSION,
64         OPT_VMLINUZ_OUT,
65         OPT_FLAGS,
66         OPT_HELP,
67 };
68
69 static const struct option long_opts[] = {
70         {"pack", 1, 0, OPT_MODE_PACK},
71         {"repack", 1, 0, OPT_MODE_REPACK},
72         {"verify", 1, 0, OPT_MODE_VERIFY},
73         {"get-vmlinuz", 1, 0, OPT_MODE_GET_VMLINUZ},
74         {"arch", 1, 0, OPT_ARCH},
75         {"oldblob", 1, 0, OPT_OLDBLOB},
76         {"kloadaddr", 1, 0, OPT_KLOADADDR},
77         {"keyblock", 1, 0, OPT_KEYBLOCK},
78         {"signpubkey", 1, 0, OPT_SIGNPUBKEY},
79         {"signprivate", 1, 0, OPT_SIGNPRIVATE},
80         {"version", 1, 0, OPT_VERSION},
81         {"minversion", 1, 0, OPT_MINVERSION},
82         {"vmlinuz", 1, 0, OPT_VMLINUZ},
83         {"bootloader", 1, 0, OPT_BOOTLOADER},
84         {"config", 1, 0, OPT_CONFIG},
85         {"vblockonly", 0, 0, OPT_VBLOCKONLY},
86         {"pad", 1, 0, OPT_PAD},
87         {"verbose", 0, &opt_verbose, 1},
88         {"vmlinuz-out", 1, 0, OPT_VMLINUZ_OUT},
89         {"flags", 1, 0, OPT_FLAGS},
90         {"help", 0, 0, OPT_HELP},
91         {NULL, 0, 0, 0}
92 };
93
94
95
96 static const char usage[] =
97         "\n"
98         "Usage:  " MYNAME " %s --pack <file> [PARAMETERS]\n"
99         "\n"
100         "  Required parameters:\n"
101         "    --keyblock <file>         Key block in .keyblock format\n"
102         "    --signprivate <file>      Private key to sign kernel data,\n"
103         "                                in .vbprivk format\n"
104         "    --version <number>        Kernel version\n"
105         "    --vmlinuz <file>          Linux kernel bzImage file\n"
106         "    --bootloader <file>       Bootloader stub\n"
107         "    --config <file>           Command line file\n"
108         "    --arch <arch>             Cpu architecture (default x86)\n"
109         "\n"
110         "  Optional:\n"
111         "    --kloadaddr <address>     Assign kernel body load address\n"
112         "    --pad <number>            Verification padding size in bytes\n"
113         "    --vblockonly              Emit just the verification blob\n"
114         "    --flags NUM               Flags to be passed in the header\n"
115         "\nOR\n\n"
116         "Usage:  " MYNAME " %s --repack <file> [PARAMETERS]\n"
117         "\n"
118         "  Required parameters:\n"
119         "    --signprivate <file>      Private key to sign kernel data,\n"
120         "                                in .vbprivk format\n"
121         "    --oldblob <file>          Previously packed kernel blob\n"
122         "                                (including verfication blob)\n"
123         "\n"
124         "  Optional:\n"
125         "    --keyblock <file>         Key block in .keyblock format\n"
126         "    --config <file>           New command line file\n"
127         "    --version <number>        Kernel version\n"
128         "    --kloadaddr <address>     Assign kernel body load address\n"
129         "    --pad <number>            Verification blob size in bytes\n"
130         "    --vblockonly              Emit just the verification blob\n"
131         "\nOR\n\n"
132         "Usage:  " MYNAME " %s --verify <file> [PARAMETERS]\n"
133         "\n"
134         "  Optional:\n"
135         "    --signpubkey <file>"
136         "       Public key to verify kernel keyblock,\n"
137         "                                in .vbpubk format\n"
138         "    --verbose                 Print a more detailed report\n"
139         "    --keyblock <file>         Outputs the verified key block,\n"
140         "                                in .keyblock format\n"
141         "    --pad <number>            Verification padding size in bytes\n"
142         "    --minversion <number>     Minimum combined kernel key version\n"
143         "\nOR\n\n"
144         "Usage:  " MYNAME " %s --get-vmlinuz <file> [PARAMETERS]\n"
145         "\n"
146         "  Required parameters:\n"
147         "    --vmlinuz-out <file>      vmlinuz image output file\n"
148         "\n";
149
150
151 /* Print help and return error */
152 static void print_help(int argc, char *argv[])
153 {
154         printf(usage, argv[0], argv[0], argv[0], argv[0]);
155 }
156
157
158 /* Return an explanation when fread() fails. */
159 static const char *error_fread(FILE *fp)
160 {
161         const char *retval = "beats me why";
162         if (feof(fp))
163                 retval = "EOF";
164         else if (ferror(fp))
165                 retval = strerror(errno);
166         clearerr(fp);
167         return retval;
168 }
169
170
171 /* This reads a complete kernel partition into a buffer */
172 static uint8_t *ReadOldKPartFromFileOrDie(const char *filename,
173                                          uint64_t *size_ptr)
174 {
175         FILE *fp = NULL;
176         struct stat statbuf;
177         uint8_t *buf;
178         uint64_t file_size = 0;
179
180         if (0 != stat(filename, &statbuf))
181                 Fatal("Unable to stat %s: %s\n", filename, strerror(errno));
182
183         if (S_ISBLK(statbuf.st_mode)) {
184 #ifndef HAVE_MACOS
185                 int fd = open(filename, O_RDONLY);
186                 if (fd >= 0) {
187                         ioctl(fd, BLKGETSIZE64, &file_size);
188                         close(fd);
189                 }
190 #endif
191         } else {
192                 file_size = statbuf.st_size;
193         }
194         Debug("%s size is 0x%" PRIx64 "\n", filename, file_size);
195         if (file_size < opt_pad)
196                 Fatal("%s is too small to be a valid kernel blob\n");
197
198         Debug("Reading %s\n", filename);
199         fp = fopen(filename, "rb");
200         if (!fp)
201                 Fatal("Unable to open file %s: %s\n", filename,
202                       strerror(errno));
203
204         buf = malloc(file_size);
205         if (1 != fread(buf, file_size, 1, fp))
206                 Fatal("Unable to read entirety of %s: %s\n", filename,
207                       error_fread(fp));
208
209         if (size_ptr)
210                 *size_ptr = file_size;
211
212         return buf;
213 }
214
215 /****************************************************************************/
216
217 static int do_vbutil_kernel(int argc, char *argv[])
218 {
219         char *filename = NULL;
220         char *oldfile = NULL;
221         char *keyblock_file = NULL;
222         char *signpubkey_file = NULL;
223         char *signprivkey_file = NULL;
224         char *version_str = NULL;
225         int version = -1;
226         char *vmlinuz_file = NULL;
227         char *bootloader_file = NULL;
228         char *config_file = NULL;
229         char *vmlinuz_out_file = NULL;
230         enum arch_t arch = ARCH_X86;
231         uint64_t kernel_body_load_address = CROS_32BIT_ENTRY_ADDR;
232         int mode = 0;
233         int parse_error = 0;
234         uint64_t min_version = 0;
235         char *e;
236         int i = 0;
237         int errcount = 0;
238         int rv;
239         VbKeyBlockHeader *keyblock = NULL;
240         VbKeyBlockHeader *t_keyblock = NULL;
241         VbPrivateKey *signpriv_key = NULL;
242         VbPublicKey *signpub_key = NULL;
243         uint8_t *kpart_data = NULL;
244         uint64_t kpart_size = 0;
245         uint8_t *vmlinuz_buf = NULL;
246         uint64_t vmlinuz_size = 0;
247         uint8_t *t_config_data;
248         uint64_t t_config_size;
249         uint8_t *t_bootloader_data;
250         uint64_t t_bootloader_size;
251         uint64_t vmlinuz_header_size = 0;
252         uint64_t vmlinuz_header_address = 0;
253         uint64_t vmlinuz_header_offset = 0;
254         VbKernelPreambleHeader *preamble = NULL;
255         uint8_t *kblob_data = NULL;
256         uint64_t kblob_size = 0;
257         uint8_t *vblock_data = NULL;
258         uint64_t vblock_size = 0;
259         uint32_t flags = 0;
260         FILE *f;
261
262         while (((i = getopt_long(argc, argv, ":", long_opts, NULL)) != -1) &&
263                !parse_error) {
264                 switch (i) {
265                 default:
266                 case '?':
267                         /* Unhandled option */
268                         parse_error = 1;
269                         break;
270
271                 case 0:
272                         /* silently handled option */
273                         break;
274                 case OPT_HELP:
275                         print_help(argc, argv);
276                         return !!parse_error;
277
278                 case OPT_MODE_PACK:
279                 case OPT_MODE_REPACK:
280                 case OPT_MODE_VERIFY:
281                 case OPT_MODE_GET_VMLINUZ:
282                         if (mode && (mode != i)) {
283                                 fprintf(stderr,
284                                         "Only one mode can be specified\n");
285                                 parse_error = 1;
286                                 break;
287                         }
288                         mode = i;
289                         filename = optarg;
290                         break;
291
292                 case OPT_ARCH:
293                         /* check the first 3 characters to also detect x86_64 */
294                         if ((!strncasecmp(optarg, "x86", 3)) ||
295                             (!strcasecmp(optarg, "amd64")))
296                                 arch = ARCH_X86;
297                         else if ((!strcasecmp(optarg, "arm")) ||
298                                  (!strcasecmp(optarg, "aarch64")))
299                                 arch = ARCH_ARM;
300                         else if (!strcasecmp(optarg, "mips"))
301                                 arch = ARCH_MIPS;
302                         else {
303                                 fprintf(stderr,
304                                         "Unknown architecture string: %s\n",
305                                         optarg);
306                                 parse_error = 1;
307                         }
308                         break;
309
310                 case OPT_OLDBLOB:
311                         oldfile = optarg;
312                         break;
313
314                 case OPT_KLOADADDR:
315                         kernel_body_load_address = strtoul(optarg, &e, 0);
316                         if (!*optarg || (e && *e)) {
317                                 fprintf(stderr, "Invalid --kloadaddr\n");
318                                 parse_error = 1;
319                         }
320                         break;
321
322                 case OPT_KEYBLOCK:
323                         keyblock_file = optarg;
324                         break;
325
326                 case OPT_SIGNPUBKEY:
327                         signpubkey_file = optarg;
328                         break;
329
330                 case OPT_SIGNPRIVATE:
331                         signprivkey_file = optarg;
332                         break;
333
334                 case OPT_VMLINUZ:
335                         vmlinuz_file = optarg;
336                         break;
337
338                 case OPT_FLAGS:
339                         flags = (uint32_t)strtoul(optarg, &e, 0);
340                         if (!*optarg || (e && *e)) {
341                                 fprintf(stderr, "Invalid --flags\n");
342                                 parse_error = 1;
343                         }
344                         break;
345
346                 case OPT_BOOTLOADER:
347                         bootloader_file = optarg;
348                         break;
349
350                 case OPT_CONFIG:
351                         config_file = optarg;
352                         break;
353
354                 case OPT_VBLOCKONLY:
355                         opt_vblockonly = 1;
356                         break;
357
358                 case OPT_VERSION:
359                         version_str = optarg;
360                         version = strtoul(optarg, &e, 0);
361                         if (!*optarg || (e && *e)) {
362                                 fprintf(stderr, "Invalid --version\n");
363                                 parse_error = 1;
364                         }
365                         break;
366
367                 case OPT_MINVERSION:
368                         min_version = strtoul(optarg, &e, 0);
369                         if (!*optarg || (e && *e)) {
370                                 fprintf(stderr, "Invalid --minversion\n");
371                                 parse_error = 1;
372                         }
373                         break;
374
375                 case OPT_PAD:
376                         opt_pad = strtoul(optarg, &e, 0);
377                         if (!*optarg || (e && *e)) {
378                                 fprintf(stderr, "Invalid --pad\n");
379                                 parse_error = 1;
380                         }
381                         break;
382                 case OPT_VMLINUZ_OUT:
383                         vmlinuz_out_file = optarg;
384                 }
385         }
386
387         if (parse_error) {
388                 print_help(argc, argv);
389                 return 1;
390         }
391
392         switch (mode) {
393         case OPT_MODE_PACK:
394
395                 if (!keyblock_file)
396                         Fatal("Missing required keyblock file.\n");
397
398                 t_keyblock = (VbKeyBlockHeader *)ReadFile(keyblock_file, 0);
399                 if (!t_keyblock)
400                         Fatal("Error reading key block.\n");
401
402                 if (!signprivkey_file)
403                         Fatal("Missing required signprivate file.\n");
404
405                 signpriv_key = PrivateKeyRead(signprivkey_file);
406                 if (!signpriv_key)
407                         Fatal("Error reading signing key.\n");
408
409                 if (!config_file)
410                         Fatal("Missing required config file.\n");
411
412                 Debug("Reading %s\n", config_file);
413                 t_config_data =
414                         ReadConfigFile(config_file, &t_config_size);
415                 if (!t_config_data)
416                         Fatal("Error reading config file.\n");
417
418                 if (!bootloader_file)
419                         Fatal("Missing required bootloader file.\n");
420
421                 Debug("Reading %s\n", bootloader_file);
422                 t_bootloader_data = ReadFile(bootloader_file,
423                                              &t_bootloader_size);
424                 if (!t_bootloader_data)
425                         Fatal("Error reading bootloader file.\n");
426                 Debug(" bootloader file size=0x%" PRIx64 "\n",
427                       t_bootloader_size);
428
429                 if (!vmlinuz_file)
430                         Fatal("Missing required vmlinuz file.\n");
431                 Debug("Reading %s\n", vmlinuz_file);
432                 vmlinuz_buf = ReadFile(vmlinuz_file, &vmlinuz_size);
433                 if (!vmlinuz_buf)
434                         Fatal("Error reading vmlinuz file.\n");
435                 Debug(" vmlinuz file size=0x%" PRIx64 "\n",
436                       vmlinuz_size);
437                 if (!vmlinuz_size)
438                         Fatal("Empty vmlinuz file\n");
439
440                 kblob_data = CreateKernelBlob(
441                         vmlinuz_buf, vmlinuz_size,
442                         arch, kernel_body_load_address,
443                         t_config_data, t_config_size,
444                         t_bootloader_data, t_bootloader_size,
445                         &kblob_size);
446                 if (!kblob_data)
447                         Fatal("Unable to create kernel blob\n");
448
449                 Debug("kblob_size = 0x%" PRIx64 "\n", kblob_size);
450
451                 vblock_data = SignKernelBlob(kblob_data, kblob_size, opt_pad,
452                                              version, kernel_body_load_address,
453                                              t_keyblock, signpriv_key, flags,
454                                              &vblock_size);
455                 if (!vblock_data)
456                         Fatal("Unable to sign kernel blob\n");
457
458                 Debug("vblock_size = 0x%" PRIx64 "\n", vblock_size);
459
460                 if (opt_vblockonly)
461                         rv = WriteSomeParts(filename,
462                                             vblock_data, vblock_size,
463                                             NULL, 0);
464                 else
465                         rv = WriteSomeParts(filename,
466                                             vblock_data, vblock_size,
467                                             kblob_data, kblob_size);
468                 return rv;
469
470         case OPT_MODE_REPACK:
471
472                 /* Required */
473
474                 if (!signprivkey_file)
475                         Fatal("Missing required signprivate file.\n");
476
477                 signpriv_key = PrivateKeyRead(signprivkey_file);
478                 if (!signpriv_key)
479                         Fatal("Error reading signing key.\n");
480
481                 if (!oldfile)
482                         Fatal("Missing previously packed blob.\n");
483
484                 /* Load the kernel partition */
485                 kpart_data = ReadOldKPartFromFileOrDie(oldfile, &kpart_size);
486
487                 /* Make sure we have a kernel partition */
488                 if (FILE_TYPE_KERN_PREAMBLE !=
489                     futil_file_type_buf(kpart_data, kpart_size))
490                         Fatal("%s is not a kernel blob\n", oldfile);
491
492                 kblob_data = UnpackKPart(kpart_data, kpart_size, opt_pad,
493                                          &keyblock, &preamble, &kblob_size);
494
495                 if (!kblob_data)
496                         Fatal("Unable to unpack kernel partition\n");
497
498                 kernel_body_load_address = preamble->body_load_address;
499
500                 /* Update the config if asked */
501                 if (config_file) {
502                         Debug("Reading %s\n", config_file);
503                         t_config_data =
504                                 ReadConfigFile(config_file, &t_config_size);
505                         if (!t_config_data)
506                                 Fatal("Error reading config file.\n");
507                         if (0 != UpdateKernelBlobConfig(
508                                     kblob_data, kblob_size,
509                                     t_config_data, t_config_size))
510                                 Fatal("Unable to update config\n");
511                 }
512
513                 if (!version_str)
514                         version = preamble->kernel_version;
515
516                 if (VbKernelHasFlags(preamble) == VBOOT_SUCCESS)
517                         flags = preamble->flags;
518
519                 if (keyblock_file) {
520                         t_keyblock =
521                                 (VbKeyBlockHeader *)ReadFile(keyblock_file, 0);
522                         if (!t_keyblock)
523                                 Fatal("Error reading key block.\n");
524                 }
525
526                 /* Reuse previous body size */
527                 vblock_data = SignKernelBlob(kblob_data, kblob_size, opt_pad,
528                                              version, kernel_body_load_address,
529                                              t_keyblock ? t_keyblock : keyblock,
530                                              signpriv_key, flags, &vblock_size);
531                 if (!vblock_data)
532                         Fatal("Unable to sign kernel blob\n");
533
534                 if (opt_vblockonly)
535                         rv = WriteSomeParts(filename,
536                                             vblock_data, vblock_size,
537                                             NULL, 0);
538                 else
539                         rv = WriteSomeParts(filename,
540                                             vblock_data, vblock_size,
541                                             kblob_data, kblob_size);
542                 return rv;
543
544         case OPT_MODE_VERIFY:
545
546                 /* Optional */
547
548                 if (signpubkey_file) {
549                         signpub_key = PublicKeyRead(signpubkey_file);
550                         if (!signpub_key)
551                                 Fatal("Error reading public key.\n");
552                 }
553
554                 /* Do it */
555
556                 /* Load the kernel partition */
557                 kpart_data = ReadOldKPartFromFileOrDie(filename, &kpart_size);
558
559                 kblob_data = UnpackKPart(kpart_data, kpart_size, opt_pad,
560                                          0, 0, &kblob_size);
561                 if (!kblob_data)
562                         Fatal("Unable to unpack kernel partition\n");
563
564                 rv = VerifyKernelBlob(kblob_data, kblob_size,
565                                       signpub_key, keyblock_file, min_version);
566
567                 return rv;
568
569         case OPT_MODE_GET_VMLINUZ:
570
571                 if (!vmlinuz_out_file) {
572                         fprintf(stderr,
573                                 "USE: vbutil_kernel --get-vmlinuz <file> "
574                                 "--vmlinuz-out <file>\n");
575                         print_help(argc, argv);
576                         return 1;
577                 }
578
579                 kpart_data = ReadOldKPartFromFileOrDie(filename, &kpart_size);
580
581                 kblob_data = UnpackKPart(kpart_data, kpart_size, opt_pad,
582                                          &keyblock, &preamble, &kblob_size);
583
584                 if (!kblob_data)
585                         Fatal("Unable to unpack kernel partition\n");
586
587                 f = fopen(vmlinuz_out_file, "wb");
588                 if (!f) {
589                         VbExError("Can't open output file %s\n",
590                                   vmlinuz_out_file);
591                         return 1;
592                 }
593
594                 /* Now stick 16-bit header followed by kernel block into
595                    output */
596                 if (VbGetKernelVmlinuzHeader(preamble,
597                                              &vmlinuz_header_address,
598                                              &vmlinuz_header_size)
599                     != VBOOT_SUCCESS) {
600                         Fatal("Unable to retrieve Vmlinuz Header!");
601                 }
602
603                 if (vmlinuz_header_size) {
604                         // verify that the 16-bit header is included in the
605                         // kblob (to make sure that it's included in the
606                         // signature)
607                         if (VerifyVmlinuzInsideKBlob(preamble->body_load_address,
608                                                      kblob_size,
609                                                      vmlinuz_header_address,
610                                                      vmlinuz_header_size)) {
611                                 VbExError("Vmlinuz header not signed!\n");
612                                 fclose(f);
613                                 unlink(vmlinuz_out_file);
614                                 return 1;
615                         }
616                         // calculate the vmlinuz_header offset from
617                         // the beginning of the kpart_data.  The kblob doesn't
618                         // include the body_load_offset, but does include
619                         // the keyblock and preamble sections.
620                         vmlinuz_header_offset = vmlinuz_header_address -
621                                 preamble->body_load_address +
622                                 keyblock->key_block_size +
623                                 preamble->preamble_size;
624                         errcount |=
625                                 (1 != fwrite(kpart_data + vmlinuz_header_offset,
626                                              vmlinuz_header_size,
627                                              1,
628                                              f));
629                 }
630                 errcount |= (1 != fwrite(kblob_data,
631                                          kblob_size,
632                                          1,
633                                          f));
634                 if (errcount) {
635                         VbExError("Can't write output file %s\n",
636                                   vmlinuz_out_file);
637                         fclose(f);
638                         unlink(vmlinuz_out_file);
639                         return 1;
640                 }
641
642                 fclose(f);
643                 return 0;
644         }
645
646         fprintf(stderr,
647                 "You must specify a mode: "
648                 "--pack, --repack, --verify, or --get-vmlinuz\n");
649         print_help(argc, argv);
650         return 1;
651 }
652
653 DECLARE_FUTIL_COMMAND(vbutil_kernel, do_vbutil_kernel, VBOOT_VERSION_1_0,
654                       "Creates, signs, and verifies the kernel partition");