diff options
author | Bill Richardson <wfrichar@chromium.org> | 2014-07-15 12:52:19 -0700 |
---|---|---|
committer | chrome-internal-fetch <chrome-internal-fetch@google.com> | 2014-07-31 22:46:27 +0000 |
commit | 6f3961507e73f90cec665896dece884e86be560a (patch) | |
tree | 0f11e2ca91c3c5be6ed30748b0d9224c6ede456a | |
parent | b52d7b7acb14743747489ed7323b416bc001503b (diff) | |
download | vboot-6f3961507e73f90cec665896dece884e86be560a.tar.gz |
futility: Add remaining vboot binary utilities
This change adds these formerly external utilities into the futility binary:
dev_sign_file
dump_kernel_config
gbb_utility
vbutil_firmware
vbutil_kernel
These target binaries will remain independent of futility, since they are
not directly related to verified boot:
cgpt
crossystem
tpm_init_temp_fix
tpmc
Also, dumpRSAPublicKey is removed from the target, since it is only used on
the build host to create new keypairs.
This change also add several additional tests.
BUG=chromium:224734
BRANCH=ToT
CQ-DEPEND=CL:210391,CL:210568,CL:210587
TEST=manual
make runtests
make clean
Also build and test:
- normal image
- test image
- recovery image
- firmware shellball
Note that this CL depends on simultaneous changes to the chromeos-initramfs
ebuild.
Change-Id: If791b5e9b5aac218ceafa9f45fc1785f16b91a64
Signed-off-by: Bill Richardson <wfrichar@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/210403
-rw-r--r-- | Makefile | 114 | ||||
-rw-r--r-- | futility/cmd_dev_sign_file.c (renamed from utility/dev_sign_file.c) | 8 | ||||
-rw-r--r-- | futility/cmd_dump_kernel_config.c (renamed from utility/dump_kernel_config.c) | 8 | ||||
-rw-r--r-- | futility/cmd_foo.c | 21 | ||||
-rw-r--r-- | futility/cmd_gbb_utility.c | 611 | ||||
-rw-r--r-- | futility/cmd_hey.c | 20 | ||||
-rw-r--r-- | futility/cmd_vbutil_firmware.c (renamed from utility/vbutil_firmware.c) | 8 | ||||
-rw-r--r-- | futility/cmd_vbutil_kernel.c (renamed from utility/vbutil_kernel.c) | 30 | ||||
-rw-r--r-- | futility/dump_kernel_config_lib.c (renamed from utility/dump_kernel_config_lib.c) | 2 | ||||
-rw-r--r-- | futility/futility.c | 112 | ||||
-rw-r--r-- | futility/kernel_blob.h (renamed from utility/include/kernel_blob.h) | 2 | ||||
-rw-r--r-- | tests/futility/binary_editor.c | 39 | ||||
-rwxr-xr-x | tests/futility/run_test_scripts.sh | 61 | ||||
-rwxr-xr-x | tests/futility/test_dump_fmap.sh | 13 | ||||
-rwxr-xr-x | tests/futility/test_gbb_utility.sh | 206 | ||||
-rwxr-xr-x | tests/futility/test_main.sh | 40 | ||||
-rw-r--r-- | utility/gbb_utility.cc | 4 |
17 files changed, 1010 insertions, 289 deletions
@@ -1,4 +1,4 @@ -# Copyright (c) 2013 The Chromium OS Authors. All rights reserved. +# Copyright 2013 The Chromium OS Authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. @@ -43,23 +43,17 @@ export BUILD # Stuff for 'make install' INSTALL = install DESTDIR = /usr/local/bin -OLDDIR = old_bins # Where exactly do the pieces go? -# FT_DIR = futility target directory - where it will be on the target -# F_DIR = futility install directory - where it gets put right now -# UB_DIR = userspace binary directory for futility's exec() targets -# VB_DIR = target vboot directory - for dev-mode-only helpers, keys, etc. +# UB_DIR = utility binary directory +# VB_DIR = vboot binary directory for dev-mode-only scripts ifeq (${MINIMAL},) -# Host install just puts everything in one place -FT_DIR=${DESTDIR} -F_DIR=${DESTDIR} -UB_DIR=${DESTDIR}/${OLDDIR} +# Host install just puts everything where it's told +UB_DIR=${DESTDIR} +VB_DIR=${DESTDIR} else -# Target install puts things into DESTDIR subdirectories -FT_DIR=/usr/bin -F_DIR=${DESTDIR}${FT_DIR} -UB_DIR=${F_DIR}/${OLDDIR} +# Target install puts things into subdirectories under DESTDIR +UB_DIR=${DESTDIR}/usr/bin VB_DIR=${DESTDIR}/usr/share/vboot/bin endif @@ -143,10 +137,6 @@ CC ?= gcc CFLAGS += -DCHROMEOS_ENVIRONMENT -Wall -Werror ${DEBUG_FLAGS} endif -ifneq (${OLDDIR},) -CFLAGS += -DOLDDIR=${OLDDIR} -endif - ifneq (${DEBUG},) CFLAGS += -DVBOOT_DEBUG endif @@ -365,6 +355,7 @@ UTILLIB_SRCS = \ cgpt/flash_ts.c \ cgpt/flash_ts_drv.c \ firmware/lib/cgptlib/mtdlib.c \ + futility/dump_kernel_config_lib.c \ host/arch/${ARCH}/lib/crossystem_arch.c \ host/lib/crossystem.c \ host/lib/file_keys.c \ @@ -375,8 +366,7 @@ UTILLIB_SRCS = \ host/lib/host_misc.c \ host/lib/util_misc.c \ host/lib/host_signature.c \ - host/lib/signature_digest.c \ - utility/dump_kernel_config_lib.c + host/lib/signature_digest.c UTILLIB_OBJS = ${UTILLIB_SRCS:%.c=${BUILD}/%.o} ALL_OBJS += ${UTILLIB_OBJS} @@ -403,10 +393,10 @@ HOSTLIB_SRCS = \ firmware/stub/tpm_lite_stub.c \ firmware/stub/utility_stub.c \ firmware/stub/vboot_api_stub_init.c \ + futility/dump_kernel_config_lib.c \ host/arch/${ARCH}/lib/crossystem_arch.c \ host/lib/crossystem.c \ - host/lib/host_misc.c \ - utility/dump_kernel_config_lib.c + host/lib/host_misc.c HOSTLIB_OBJS = ${HOSTLIB_SRCS:%.c=${BUILD}/%.o} ALL_OBJS += ${HOSTLIB_OBJS} @@ -429,7 +419,7 @@ TINYHOSTLIB_SRCS = \ firmware/lib/cgptlib/mtdlib.c \ firmware/lib/utility_string.c \ firmware/stub/utility_stub.c \ - utility/dump_kernel_config_lib.c + futility/dump_kernel_config_lib.c TINYHOSTLIB_OBJS = ${TINYHOSTLIB_SRCS:%.c=${BUILD}/%.o} @@ -477,22 +467,18 @@ endif # These utilities should be linked statically. UTIL_NAMES_STATIC = \ - utility/crossystem \ - utility/gbb_utility + utility/crossystem UTIL_NAMES = ${UTIL_NAMES_STATIC} \ - utility/dev_sign_file \ - utility/dump_kernel_config \ - utility/dumpRSAPublicKey \ utility/tpm_init_temp_fix \ - utility/tpmc \ - utility/vbutil_firmware \ - utility/vbutil_kernel + utility/tpmc +# TODO: Do we still need eficompress and efidecompress for anything? ifeq (${MINIMAL},) UTIL_NAMES += \ utility/bmpblk_font \ utility/bmpblk_utility \ + utility/dumpRSAPublicKey \ utility/eficompress \ utility/efidecompress \ utility/load_kernel_test \ @@ -533,50 +519,32 @@ FUTIL_BIN = ${BUILD}/futility/futility # But we still need both static (tiny) and dynamic (with openssl) versions. FUTIL_STATIC_BIN = ${FUTIL_BIN}_s -# These are the executables to be replaced with symlinks. -FUTIL_OLD = \ - bmpblk_font \ - bmpblk_utility \ - cgpt \ - chromeos-tpm-recovery \ - crossystem \ - dev_debug_vboot \ - dev_make_keypair \ +# These are the executables that are now built in to futility. We'll create +# symlinks for these so the old names will still work. +# TODO: Do we still need dev_sign_file for anything? +FUTIL_BUILTIN = \ dev_sign_file \ - dumpRSAPublicKey \ dump_fmap \ dump_kernel_config \ - eficompress \ - efidecompress \ - enable_dev_usb_boot \ gbb_utility \ - load_kernel_test \ - make_dev_firmware.sh \ - make_dev_ssd.sh \ - pad_digest_utility \ - resign_firmwarefd.sh \ - set_gbb_flags.sh \ - signature_digest_utility \ - tpm-nvsize \ - tpm_init_temp_fix \ - tpmc \ vbutil_firmware \ vbutil_kernel \ vbutil_key \ - vbutil_keyblock \ - vbutil_what_keys \ - verify_data + vbutil_keyblock FUTIL_STATIC_SRCS = \ futility/futility.c \ futility/cmd_dump_fmap.c \ - futility/cmd_foo.c + futility/cmd_gbb_utility.c FUTIL_SRCS = \ $(FUTIL_STATIC_SRCS) \ + futility/cmd_dev_sign_file.c \ + futility/cmd_dump_kernel_config.c \ + futility/cmd_vbutil_firmware.c \ + futility/cmd_vbutil_kernel.c \ futility/cmd_vbutil_key.c \ - futility/cmd_vbutil_keyblock.c \ - futility/cmd_hey.c + futility/cmd_vbutil_keyblock.c FUTIL_LDS = futility/futility.lds @@ -628,6 +596,7 @@ TEST_NAMES = \ tests/vboot_firmware_tests \ tests/vboot_kernel_tests \ tests/vboot_nvstorage_test \ + tests/futility/binary_editor \ tests/futility/test_not_really ifdef REGION_READ @@ -906,15 +875,10 @@ utils_install: ${UTIL_BINS} ${UTIL_SCRIPTS} .PHONY: signing_install signing_install: ${SIGNING_SCRIPTS} ${SIGNING_SCRIPTS_DEV} ${SIGNING_COMMON} @$(PRINTF) " INSTALL SIGNING\n" - ${Q}mkdir -p ${UB_DIR} + ${Q}mkdir -p ${UB_DIR} ${VB_DIR} ${Q}${INSTALL} -t ${UB_DIR} ${SIGNING_SCRIPTS} - ${Q}${INSTALL} -t ${UB_DIR} ${SIGNING_SCRIPTS_DEV} - ${Q}${INSTALL} -t ${UB_DIR} -m 'u=rw,go=r,a-s' ${SIGNING_COMMON} -ifneq (${VB_DIR},) - ${Q}mkdir -p ${VB_DIR} - ${Q}for prog in $(notdir ${SIGNING_SCRIPTS_DEV}); do \ - ln -sf "${FT_DIR}/futility" "${VB_DIR}/$$prog"; done -endif + ${Q}${INSTALL} -t ${VB_DIR} ${SIGNING_SCRIPTS_DEV} + ${Q}${INSTALL} -t ${VB_DIR} -m 'u=rw,go=r,a-s' ${SIGNING_COMMON} # ---------------------------------------------------------------------------- # new Firmware Utility @@ -934,10 +898,10 @@ ${FUTIL_BIN}: ${FUTIL_LDS} ${FUTIL_OBJS} ${UTILLIB} .PHONY: futil_install futil_install: ${FUTIL_BIN} @$(PRINTF) " INSTALL futility\n" - ${Q}mkdir -p ${F_DIR} - ${Q}${INSTALL} -t ${F_DIR} ${FUTIL_BIN} ${FUTIL_STATIC_BIN} - ${Q}for prog in ${FUTIL_OLD}; do \ - ln -sf futility "${F_DIR}/$$prog"; done + ${Q}mkdir -p ${UB_DIR} + ${Q}${INSTALL} -t ${UB_DIR} ${FUTIL_BIN} ${FUTIL_STATIC_BIN} + ${Q}for prog in ${FUTIL_BUILTIN}; do \ + ln -sf futility "${UB_DIR}/$$prog"; done # ---------------------------------------------------------------------------- # Utility to generate TLCL structure definition header file. @@ -1004,9 +968,6 @@ ${BUILD}/%.o: %.cc # ---------------------------------------------------------------------------- # Here are the special tweaks to the generic rules. -# GBB utility needs C++ linker. TODO: It shouldn't. -${BUILD}/utility/gbb_utility: LD = ${CXX} - # Because we play some clever linker script games to add new commands without # changing any header files, futility must be linked with ld.bfd, not gold. ${FUTIL_BIN}: LDFLAGS += -fuse-ld=bfd @@ -1018,9 +979,6 @@ CRYPTO_LIBS := $(shell ${PKG_CONFIG} --libs libcrypto) ${BUILD}/utility/dumpRSAPublicKey: LDLIBS += ${CRYPTO_LIBS} ${BUILD}/utility/pad_digest_utility: LDLIBS += ${CRYPTO_LIBS} ${BUILD}/utility/signature_digest_utility: LDLIBS += ${CRYPTO_LIBS} -${BUILD}/utility/dev_sign_file: LDLIBS += ${CRYPTO_LIBS} -${BUILD}/utility/vbutil_firmware: LDLIBS += ${CRYPTO_LIBS} -${BUILD}/utility/vbutil_kernel: LDLIBS += ${CRYPTO_LIBS} ${BUILD}/host/linktest/main: LDLIBS += ${CRYPTO_LIBS} ${BUILD}/tests/vb2_common2_tests: LDLIBS += ${CRYPTO_LIBS} diff --git a/utility/dev_sign_file.c b/futility/cmd_dev_sign_file.c index b20423ff..3b2b640b 100644 --- a/utility/dev_sign_file.c +++ b/futility/cmd_dev_sign_file.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2011 The Chromium OS Authors. All rights reserved. +/* Copyright 2011 The Chromium OS Authors. All rights reserved. * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. * @@ -18,6 +18,7 @@ #include <unistd.h> #include "cryptolib.h" +#include "futility.h" #include "host_common.h" #include "kernel_blob.h" #include "vboot_common.h" @@ -273,7 +274,7 @@ static int Verify(const char* filename, const char* vblock_file, } -int main(int argc, char* argv[]) { +int do_dev_sign_file(int argc, char* argv[]) { char* filename = NULL; char* keyblock_file = NULL; char* signprivate_file = NULL; @@ -353,3 +354,6 @@ int main(int argc, char* argv[]) { /* NOTREACHED */ return 1; } + +DECLARE_FUTIL_COMMAND(dev_sign_file, do_dev_sign_file, + "Sign or verify dev-mode files (DEPRECATED)"); diff --git a/utility/dump_kernel_config.c b/futility/cmd_dump_kernel_config.c index ec0ee5ab..2d15f99f 100644 --- a/utility/dump_kernel_config.c +++ b/futility/cmd_dump_kernel_config.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2012 The Chromium OS Authors. All rights reserved. +/* Copyright 2012 The Chromium OS Authors. All rights reserved. * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. * @@ -11,6 +11,7 @@ #include <sys/mman.h> #include <unistd.h> +#include "futility.h" #include "vboot_host.h" enum { @@ -33,7 +34,7 @@ static int PrintHelp(void) { return 1; } -int main(int argc, char* argv[]) { +int do_dump_kernel_config(int argc, char* argv[]) { char *infile = NULL; char *config = NULL; uint64_t kernel_body_load_address = USE_PREAMBLE_LOAD_ADDR; @@ -87,3 +88,6 @@ int main(int argc, char* argv[]) { free(config); return 0; } + +DECLARE_FUTIL_COMMAND(dump_kernel_config, do_dump_kernel_config, + "Prints the kernel command line"); diff --git a/futility/cmd_foo.c b/futility/cmd_foo.c deleted file mode 100644 index e008b3a0..00000000 --- a/futility/cmd_foo.c +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright (c) 2013 The Chromium OS Authors. All rights reserved. - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#include <stdio.h> - -#include "futility.h" - -static int do_something(int argc, char *argv[]) -{ - int i; - printf("this is %s\n", __func__); - for (i = 0; i < argc; i++) - printf("argv[%d] = %s\n", i, argv[i]); - return 0; -} - -DECLARE_FUTIL_COMMAND(foo, do_something, "invoke a foo"); -DECLARE_FUTIL_COMMAND(bar, do_something, "go to bar"); diff --git a/futility/cmd_gbb_utility.c b/futility/cmd_gbb_utility.c new file mode 100644 index 00000000..a8554562 --- /dev/null +++ b/futility/cmd_gbb_utility.c @@ -0,0 +1,611 @@ +/* + * Copyright 2014 The Chromium OS Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ +#include <errno.h> +#include <getopt.h> +#include <inttypes.h> +#include <stddef.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <unistd.h> + +#include "futility.h" +#include "gbb_header.h" + +static void help_and_quit(const char *prog) { + fprintf(stderr, "\n" + "Usage: %s [-g|-s|-c] [OPTIONS] bios_file [output_file]\n" + "\n" + "GET MODE:\n" + "-g, --get (default)\tGet (read) from bios_file, " + "with following options:\n" + " --hwid \tReport hardware id (default).\n" + " --flags \tReport header flags.\n" + " -k, --rootkey=FILE \tFile name to export Root Key.\n" + " -b, --bmpfv=FILE \tFile name to export Bitmap FV.\n" + " -r --recoverykey=FILE\tFile name to export Recovery Key.\n" + "\n" + "SET MODE:\n" + "-s, --set \tSet (write) to bios_file, " + "with following options:\n" + " -o, --output=FILE \tNew file name for ouptput.\n" + " --hwid=HWID \tThe new hardware id to be changed.\n" + " --flags=FLAGS \tThe new (numeric) flags value.\n" + " -k, --rootkey=FILE \tFile name of new Root Key.\n" + " -b, --bmpfv=FILE \tFile name of new Bitmap FV.\n" + " -r --recoverykey=FILE\tFile name of new Recovery Key.\n" + "\n" + "CREATE MODE:\n" + "-c, --create=hwid_size,rootkey_size,bmpfv_size,recoverykey_size\n" + " \tCreate a GBB blob by given size list.\n" + "SAMPLE:\n" + " %s -g bios.bin\n" + " %s --set --hwid='New Model' -k key.bin bios.bin newbios.bin\n" + " %s -c 0x100,0x1000,0x03DE80,0x1000 gbb.blob\n\n", + prog, prog, prog, prog); + exit(1); +} + +/* Command line options */ +static struct option long_opts[] = { + /* name hasarg *flag val */ + {"get", 0, NULL, 'g' }, + {"set", 0, NULL, 's' }, + {"create", 1, NULL, 'c' }, + {"output", 1, NULL, 'o' }, + {"rootkey", 1, NULL, 'k' }, + {"bmpfv", 1, NULL, 'b' }, + {"recoverykey", 1, NULL, 'R' }, + {"hwid", 2, NULL, 'i' }, + {"flags", 2, NULL, 'L' }, + { NULL, 0, NULL, 0 }, +}; +static char *short_opts = ":gsc:o:k:b:R:r:h:i:L:f:"; + +static int errorcnt; + +static int ValidGBB(GoogleBinaryBlockHeader *gbb, size_t maxlen) +{ + uint32_t i; + char *s; + + if (gbb->major_version != GBB_MAJOR_VER) + goto bad; + if (gbb->header_size != GBB_HEADER_SIZE || gbb->header_size > maxlen) + goto bad; + if (gbb->hwid_offset < GBB_HEADER_SIZE) + goto bad; + if (gbb->hwid_offset + gbb->hwid_size > maxlen) + goto bad; + if (gbb->hwid_size) { + /* Make sure the HWID is null-terminated (assumes ASCII, not unicode). */ + s = (char *)((char *)gbb + gbb->hwid_offset); + for (i = 0; i < gbb->hwid_size; i++) + if (*s++ == '\0') + break; + if (i >= gbb->hwid_size) + goto bad; + } + if (gbb->rootkey_offset < GBB_HEADER_SIZE) + goto bad; + if (gbb->rootkey_offset + gbb->rootkey_size > maxlen) + goto bad; + if (gbb->bmpfv_offset < GBB_HEADER_SIZE) + goto bad; + if (gbb->bmpfv_offset + gbb->bmpfv_size > maxlen) + goto bad; + if (gbb->recovery_key_offset < GBB_HEADER_SIZE) + goto bad; + if (gbb->recovery_key_offset + gbb->recovery_key_size > maxlen) + goto bad; + + return 1; + +bad: + errorcnt++; + return 0; +} + +#define GBB_SEARCH_STRIDE 4 +GoogleBinaryBlockHeader *FindGbbHeader(uint8_t *ptr, size_t size) +{ + size_t i; + GoogleBinaryBlockHeader *tmp, *gbb_header = NULL; + int count = 0; + + for (i = 0; + i <= size - GBB_SEARCH_STRIDE; + i += GBB_SEARCH_STRIDE) { + if (0 != memcmp(ptr + i, GBB_SIGNATURE, GBB_SIGNATURE_SIZE)) + continue; + + /* Found something. See if it's any good. */ + tmp = (GoogleBinaryBlockHeader *)(ptr + i); + if (ValidGBB(tmp, size - i)) + if (!count++) + gbb_header = tmp; + } + + switch (count) { + case 0: + errorcnt++; + return NULL; + case 1: + return gbb_header; + default: + fprintf(stderr, "ERROR: multiple GBB headers found\n"); + errorcnt++; + return NULL; + } +} + +static uint8_t *create_gbb(const char *desc, off_t *sizeptr) +{ + char *str, *sizes, *param, *e = NULL; + size_t size = GBB_HEADER_SIZE; + int i = 0; + /* Danger Will Robinson! four entries ==> four paramater blocks */ + uint32_t val[] = {0, 0, 0, 0}; + uint8_t *buf; + GoogleBinaryBlockHeader *gbb; + + sizes = strdup(desc); + if (!sizes) { + errorcnt++; + fprintf(stderr, "ERROR: strdup() failed: %s\n", strerror(errno)); + return NULL; + } + + for (str = sizes; (param = strtok(str, ", ")) != NULL; str = NULL) { + val[i] = (uint32_t)strtoul(param, &e, 0); + if (e && *e) { + errorcnt++; + fprintf(stderr, "ERROR: invalid creation parameter: \"%s\"\n", param); + free(sizes); + return NULL; + } + size += val[i++]; + if (i > ARRAY_SIZE(val)) + break; + } + + buf = (uint8_t *)calloc(1, size); + if (!buf) { + errorcnt++; + fprintf(stderr, "ERROR: can't malloc %zu bytes: %s\n", + size, strerror(errno)); + free(sizes); + return NULL; + } else if (sizeptr) { + *sizeptr = size; + } + + gbb = (GoogleBinaryBlockHeader *)buf; + memcpy(gbb->signature, GBB_SIGNATURE, GBB_SIGNATURE_SIZE); + gbb->major_version = GBB_MAJOR_VER; + gbb->minor_version = GBB_MINOR_VER; + gbb->header_size = GBB_HEADER_SIZE; + gbb->flags = 0; + + i = GBB_HEADER_SIZE; + gbb->hwid_offset = i; + gbb->hwid_size = val[0]; + i += val[0]; + + gbb->rootkey_offset = i; + gbb->rootkey_size = val[1]; + i += val[1]; + + gbb->bmpfv_offset = i; + gbb->bmpfv_size = val[2]; + i += val[2]; + + gbb->recovery_key_offset = i; + gbb->recovery_key_size = val[3]; + i += val[1]; + + free(sizes); + return buf; +} + +uint8_t *read_entire_file(const char *filename, off_t *sizeptr) +{ + FILE *fp = NULL; + uint8_t *buf = NULL; + struct stat sb; + + fp = fopen(filename, "rb"); + if (!fp) { + fprintf(stderr, "ERROR: Unable to open %s for reading: %s\n", + filename, strerror(errno)); + goto fail; + } + + if (0 != fstat(fileno(fp), &sb)) { + fprintf(stderr, "ERROR: can't fstat %s: %s\n", + filename, strerror(errno)); + goto fail; + } + if (sizeptr) + *sizeptr = sb.st_size; + + buf = (uint8_t *)malloc(sb.st_size); + if (!buf) { + fprintf(stderr, "ERROR: can't malloc %" PRIi64 " bytes: %s\n", + sb.st_size, strerror(errno)); + goto fail; + } + + if (1 != fread(buf, sb.st_size, 1, fp)) { + fprintf(stderr, "ERROR: Unable to read from %s: %s\n", + filename, strerror(errno)); + goto fail; + } + + if (fp && 0 != fclose(fp)) { + fprintf(stderr, "ERROR: Unable to close %s: %s\n", + filename, strerror(errno)); + goto fail; + } + + return buf; + +fail: + errorcnt++; + + if (buf) + free(buf); + + if (fp && 0 != fclose(fp)) + fprintf(stderr, "ERROR: Unable to close %s: %s\n", + filename, strerror(errno)); + return NULL; +} + +static int write_to_file(const char *msg, const char *filename, + uint8_t *start, size_t size) +{ + FILE *fp; + int r = 0; + + fp = fopen(filename, "wb"); + if (!fp) { + fprintf(stderr, "ERROR: Unable to open %s for writing: %s\n", + filename, strerror(errno)); + errorcnt++; + return errno; + } + + /* Don't write zero bytes */ + if (size && 1 != fwrite(start, size, 1, fp)) { + fprintf(stderr, "ERROR: Unable to write to %s: %s\n", + filename, strerror(errno)); + errorcnt++; + r = errno; + } + + if (0 != fclose(fp)) { + fprintf(stderr, "ERROR: Unable to close %s: %s\n", + filename, strerror(errno)); + errorcnt++; + if (!r) + r = errno; + } + + if (!r && msg) + printf("%s %s\n", msg, filename); + + return r; +} + +static int read_from_file(const char *msg, const char *filename, + uint8_t *start, uint32_t size) +{ + FILE *fp; + struct stat sb; + size_t count; + int r = 0; + + fp = fopen(filename, "rb"); + if (!fp) { + fprintf(stderr, "ERROR: Unable to open %s for reading: %s\n", + filename, strerror(errno)); + errorcnt++; + return errno; + } + + if (0 != fstat(fileno(fp), &sb)) { + fprintf(stderr, "ERROR: can't fstat %s: %s\n", + filename, strerror(errno)); + errorcnt++; + r = errno; + goto done_close; + } + + if (sb.st_size > size) { + fprintf(stderr, "ERROR: file %s exceeds capacity (%" PRIu32 ")\n", + filename, size); + errorcnt++; + r = errno; + goto done_close; + } + + /* It's okay if we read less than size. That's just the max. */ + count = fread(start, 1, size, fp); + if (ferror(fp)) { + fprintf(stderr, "ERROR: Read %zu/%" PRIi64 " bytes from %s: %s\n", + count, sb.st_size, filename, strerror(errno)); + errorcnt++; + r = errno; + } + +done_close: + if (0 != fclose(fp)) { + fprintf(stderr, "ERROR: Unable to close %s: %s\n", + filename, strerror(errno)); + errorcnt++; + if (!r) + r = errno; + } + + if (!r && msg) + printf(" - import %s from %s: success\n", msg, filename); + + return r; +} + +static int do_gbb_utility(int argc, char *argv[]) +{ + enum do_what_now {DO_GET, DO_SET, DO_CREATE} mode = DO_GET; + char *infile = NULL; + char *outfile = NULL; + char *opt_create = NULL; + char *opt_rootkey = NULL; + char *opt_bmpfv = NULL; + char *opt_recoverykey = NULL; + char *opt_hwid = NULL; + char *opt_flags = NULL; + int sel_hwid = 0; + int sel_flags = 0; + uint8_t *inbuf = NULL; + off_t filesize; + uint8_t *outbuf = NULL; + GoogleBinaryBlockHeader *gbb; + uint8_t *gbb_base; + int i; + + opterr = 0; /* quiet, you */ + while ((i = getopt_long(argc, argv, short_opts, long_opts, 0)) != -1) { + switch (i) { + case 'g': + mode = DO_GET; + break; + case 's': + mode = DO_SET; + break; + case 'c': + mode = DO_CREATE; + opt_create = optarg; + break; + case 'o': + outfile = optarg; + break; + case 'k': + opt_rootkey = optarg; + break; + case 'b': + opt_bmpfv = optarg; + break; + case 'R': case 'r': + opt_recoverykey = optarg; + break; + case 'i': case 'h': + /* --hwid is optional: might be null, which could be okay */ + opt_hwid = optarg; + sel_hwid = 1; + break; + case 'L': case 'f': + /* --flags is optional: might be null, which could be okay */ + opt_flags = optarg; + sel_flags = 1; + break; + case '?': + errorcnt++; + if (optopt) + fprintf(stderr, "ERROR: unrecognized option: -%c\n", optopt); + else if (argv[optind - 1] ) + fprintf(stderr, "ERROR: unrecognized option (possibly \"%s\")\n", + argv[optind - 1]); + else + fprintf(stderr, "ERROR: unrecognized option\n"); + break; + case ':': + errorcnt++; + if (argv[optind - 1]) + fprintf(stderr, "ERROR: missing argument to -%c (%s)\n", + optopt, argv[optind - 1]); + else + fprintf(stderr, "ERROR: missing argument to -%c\n", optopt); + break; + default: + errorcnt++; + fprintf(stderr, "ERROR: unexpected error while parsing options\n"); + } + } + + /* Problems? */ + if (errorcnt) + help_and_quit(argv[0]); + + /* Now try to do something */ + switch (mode) { + case DO_GET: + if (argc - optind < 1) { + fprintf(stderr, "\nERROR: missing input filename\n"); + help_and_quit(argv[0]); + } else { + infile = argv[optind++]; + } + + /* With no args, show the HWID */ + if (!opt_rootkey && !opt_bmpfv && !opt_recoverykey && !sel_flags) + sel_hwid = 1; + + inbuf = read_entire_file(infile, &filesize); + if (!inbuf) + break; + + gbb = FindGbbHeader(inbuf, filesize); + if (!gbb) { + fprintf(stderr, "ERROR: No GBB found in %s\n", infile); + break; + } + gbb_base = (uint8_t *)gbb; + + /* Get the stuff */ + if (sel_hwid) + printf("hardware_id: %s\n", + gbb->hwid_size ? (char *)(gbb_base + gbb->hwid_offset) : ""); + if (sel_flags) + printf("flags: 0x%08x\n", gbb->flags); + if (opt_rootkey) + write_to_file(" - exported root_key to file:", opt_rootkey, + gbb_base + gbb->rootkey_offset, + gbb->rootkey_size); + if (opt_bmpfv) + write_to_file(" - exported bmp_fv to file:", opt_bmpfv, + gbb_base + gbb->bmpfv_offset, + gbb->bmpfv_size); + if (opt_recoverykey) + write_to_file(" - exported recovery_key to file:", opt_recoverykey, + gbb_base + gbb->recovery_key_offset, + gbb->recovery_key_size); + break; + + case DO_SET: + if (argc - optind < 1) { + fprintf(stderr, "\nERROR: missing input filename\n"); + help_and_quit(argv[0]); + } + infile = argv[optind++]; + if (!outfile) + outfile = (argc - optind < 1) ? infile : argv[optind++]; + + if (sel_hwid && !opt_hwid) { + fprintf(stderr, "\nERROR: missing new HWID value\n"); + help_and_quit(argv[0]); + } + if (sel_flags && (!opt_flags || !*opt_flags)) { + fprintf(stderr, "\nERROR: missing new flags value\n"); + help_and_quit(argv[0]); + } + + /* With no args, we'll either copy it unchanged or do nothing */ + inbuf = read_entire_file(infile, &filesize); + if (!inbuf) + break; + + gbb = FindGbbHeader(inbuf, filesize); + if (!gbb) { + fprintf(stderr, "ERROR: No GBB found in %s\n", infile); + break; + } + gbb_base = (uint8_t *)gbb; + + outbuf = (uint8_t *)malloc(filesize); + if (!outbuf) { + errorcnt++; + fprintf(stderr, "ERROR: can't malloc %" PRIi64 " bytes: %s\n", + filesize, strerror(errno)); + break; + } + + /* Switch pointers to outbuf */ + memcpy(outbuf, inbuf, filesize); + gbb = FindGbbHeader(outbuf, filesize); + if (!gbb) { + fprintf(stderr, "INTERNAL ERROR: No GBB found in outbuf\n"); + exit(1); + } + gbb_base = (uint8_t *)gbb; + + if (opt_hwid) { + if (strlen(opt_hwid) + 1 > gbb->hwid_size) { + fprintf(stderr, + "ERROR: null-terminated HWID exceeds capacity (%d)\n", + gbb->hwid_size); + errorcnt++; + } else { + strcpy((char *)(gbb_base + gbb->hwid_offset), opt_hwid); + } + } + + if (opt_flags) { + char *e = NULL; + uint32_t val; + val = (uint32_t)strtoul(opt_flags, &e, 0); + if (e && *e) { + fprintf(stderr, "ERROR: invalid flags value: %s\n", opt_flags); + errorcnt++; + } else { + gbb->flags = val; + } + } + + if (opt_rootkey) + read_from_file("root_key", opt_rootkey, + gbb_base + gbb->rootkey_offset, + gbb->rootkey_size); + if (opt_bmpfv) + read_from_file("bmp_fv", opt_bmpfv, + gbb_base + gbb->bmpfv_offset, + gbb->bmpfv_size); + if (opt_recoverykey) + read_from_file("recovery_key", opt_recoverykey, + gbb_base + gbb->recovery_key_offset, + gbb->recovery_key_size); + + /* Write it out if there are no problems. */ + if (!errorcnt) + write_to_file("successfully saved new image to:", + outfile, outbuf, filesize); + + break; + + case DO_CREATE: + if (!outfile) { + if (argc - optind < 1) { + fprintf(stderr, "\nERROR: missing output filename\n"); + help_and_quit(argv[0]); + } + outfile = argv[optind++]; + } + /* Parse the creation args */ + outbuf = create_gbb(opt_create, &filesize); + if (!outbuf) { + fprintf(stderr, + "\nERROR: unable to parse creation spec (%s)\n", opt_create); + help_and_quit(argv[0]); + } + if (!errorcnt) + write_to_file("successfully created new GBB to:", + outfile, outbuf, filesize); + break; + } + + + if (inbuf) + free(inbuf); + if (outbuf) + free(outbuf); + return !!errorcnt; +} + +DECLARE_FUTIL_COMMAND(gbb_utility, do_gbb_utility, + "Utility to manage Google Binary Block (GBB)"); diff --git a/futility/cmd_hey.c b/futility/cmd_hey.c deleted file mode 100644 index efd47d39..00000000 --- a/futility/cmd_hey.c +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Copyright (c) 2013 The Chromium OS Authors. All rights reserved. - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#include <stdio.h> - -#include "futility.h" - -static int do_something(int argc, char *argv[]) -{ - int i; - printf("this is %s\n", __func__); - for (i = 0; i < argc; i++) - printf("argv[%d] = %s\n", i, argv[i]); - return 0; -} - -DECLARE_FUTIL_COMMAND(hey, do_something, "shout"); diff --git a/utility/vbutil_firmware.c b/futility/cmd_vbutil_firmware.c index f11d7b54..b58fa2be 100644 --- a/utility/vbutil_firmware.c +++ b/futility/cmd_vbutil_firmware.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2011 The Chromium OS Authors. All rights reserved. +/* Copyright 2011 The Chromium OS Authors. All rights reserved. * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. * @@ -13,6 +13,7 @@ #include <unistd.h> #include "cryptolib.h" +#include "futility.h" #include "host_common.h" #include "kernel_blob.h" #include "util_misc.h" @@ -290,7 +291,7 @@ static int Verify(const char* infile, const char* signpubkey, } -int main(int argc, char* argv[]) { +int do_vbutil_firmware(int argc, char* argv[]) { char* filename = NULL; char* key_block_file = NULL; @@ -371,3 +372,6 @@ int main(int argc, char* argv[]) { return PrintHelp(); } } + +DECLARE_FUTIL_COMMAND(vbutil_firmware, do_vbutil_firmware, + "Verified boot firmware utility"); diff --git a/utility/vbutil_kernel.c b/futility/cmd_vbutil_kernel.c index 5fed77ac..d62f72dc 100644 --- a/utility/vbutil_kernel.c +++ b/futility/cmd_vbutil_kernel.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2012 The Chromium OS Authors. All rights reserved. +/* Copyright 2012 The Chromium OS Authors. All rights reserved. * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. * @@ -21,6 +21,7 @@ #include <unistd.h> #include "cryptolib.h" +#include "futility.h" #include "host_common.h" #include "kernel_blob.h" #include "util_misc.h" @@ -211,21 +212,21 @@ static unsigned int find_cmdline_start(char *input, unsigned int max_len) { /* Here are globals containing all the bits & pieces I'm working on. */ /* The individual parts that go into the kernel blob */ -uint8_t *g_kernel_data; -uint64_t g_kernel_size; -uint8_t *g_param_data; -uint64_t g_param_size; -uint8_t *g_config_data; -uint64_t g_config_size; -uint8_t *g_bootloader_data; -uint64_t g_bootloader_size; -uint64_t g_bootloader_address; +static uint8_t *g_kernel_data; +static uint64_t g_kernel_size; +static uint8_t *g_param_data; +static uint64_t g_param_size; +static uint8_t *g_config_data; +static uint64_t g_config_size; +static uint8_t *g_bootloader_data; +static uint64_t g_bootloader_size; +static uint64_t g_bootloader_address; /* The individual parts of the verification blob (including the data that * immediately follows the headers) */ -VbKeyBlockHeader* g_keyblock; -VbKernelPreambleHeader* g_preamble; +static VbKeyBlockHeader* g_keyblock; +static VbKernelPreambleHeader* g_preamble; /****************************************************************************/ @@ -695,7 +696,7 @@ static int Verify(uint8_t* kernel_blob, /****************************************************************************/ -int main(int argc, char* argv[]) { +int do_vbutil_kernel(int argc, char* argv[]) { char* filename = NULL; char* oldfile = NULL; char* keyblock_file = NULL; @@ -966,3 +967,6 @@ int main(int argc, char* argv[]) { fprintf(stderr, "You must specify a mode: --pack, --repack or --verify\n"); return PrintHelp(progname); } + +DECLARE_FUTIL_COMMAND(vbutil_kernel, do_vbutil_kernel, + "Verified boot kernel utility"); diff --git a/utility/dump_kernel_config_lib.c b/futility/dump_kernel_config_lib.c index 819ca921..63c2899f 100644 --- a/utility/dump_kernel_config_lib.c +++ b/futility/dump_kernel_config_lib.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2012 The Chromium OS Authors. All rights reserved. +/* Copyright 2012 The Chromium OS Authors. All rights reserved. * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. * diff --git a/futility/futility.c b/futility/futility.c index 5e852c33..deef3061 100644 --- a/futility/futility.c +++ b/futility/futility.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013 The Chromium OS Authors. All rights reserved. + * Copyright 2013 The Chromium OS Authors. All rights reserved. * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ @@ -18,13 +18,6 @@ #define MYNAME "futility" #define MYNAME_S MYNAME "_s" -#ifdef OLDDIR -#define XSTR(A) STR(A) -#define STR(A) #A -#define SUBDIR XSTR(OLDDIR) -#else -#define SUBDIR "old_bins" -#endif /* File to use for logging, if present */ #define LOGFILE "/tmp/futility.log" @@ -39,21 +32,15 @@ static const char * const usage= "\n\ Usage: " MYNAME " PROGRAM|COMMAND [args...]\n\ \n\ This is the unified firmware utility, which will eventually replace\n\ -all the distinct userspace tools formerly produced by the\n\ +most of the distinct verified boot tools formerly produced by the\n\ vboot_reference package.\n\ \n\ -When symlinked under the name of one of those previous tools, it can\n\ -do one of two things: either it will fully implement the original\n\ -behavior, or (until that functionality is complete) it will just exec\n\ -the original binary.\n\ -\n\ -In either case it can append some usage information to " LOGFILE "\n\ -to help improve coverage and correctness.\n\ +When symlinked under the name of one of those previous tools, should\n\ +fully implement the original behavior. It can also be invoked directly\n\ +as " MYNAME ", followed by the original name as the first argument.\n\ \n\ -If you invoke it directly instead of via a symlink, it requires one\n\ -argument, which is the name of the old binary to exec. That binary\n\ -must be located in a directory named \"" SUBDIR "\" underneath\n\ -the " MYNAME " executable.\n\ +In either case it will append some usage information to " LOGFILE "\n\ +(iff that file exists), to help improve coverage and correctness.\n\ \n"; static int do_help(int argc, char *argv[]) @@ -63,7 +50,7 @@ static int do_help(int argc, char *argv[]) fputs(usage, stdout); - printf("The following commands are built-in:\n"); + printf("The following commands are built-in:\n\n"); for (cmd = futil_cmds; *cmd; cmd++) printf(" %-20s %s\n", (*cmd)->name, (*cmd)->shorthelp); @@ -77,16 +64,19 @@ static int do_help(int argc, char *argv[]) return 0; } -DECLARE_FUTIL_COMMAND(help, do_help, "show a bit of help"); +DECLARE_FUTIL_COMMAND(help, do_help, + "Show a bit of help (you're looking at it)"); -/* Deprecated functions can't be invoked through symlinks. */ +/* + * These are built-in functions that we'd like to abandon completely someday. + * TODO: If no one complains, get rid of them. + */ static char *dep_cmds[] = { - "eficompress", - "efidecompress", + "dev_sign_file", }; static const char * const dep_usage= "\n\ -The program \"%s\" is deprecated.\n\ +The program \"%s\" is deprecated and may go away soon.\n\ \n\ If you feel this is in error, please open a bug at\n\ \n\ @@ -223,25 +213,21 @@ static void log_args(int argc, char *argv[]) /******************************************************************************/ /* Here we go */ -#ifdef COVERAGE -void __gcov_flush(void); -#endif - int main(int argc, char *argv[], char *envp[]) { - char *progname; + char *fullname, *progname; char truename[PATH_MAX]; - char oldname[PATH_MAX]; char buf[80]; pid_t myproc; ssize_t r; - char *s; struct futil_cmd_t **cmd; int i; + int via_symlink = 0; log_args(argc, argv); /* How were we invoked? */ + fullname = strdup(argv[0]); progname = strrchr(argv[0], '/'); if (progname) progname++; @@ -259,18 +245,18 @@ int main(int argc, char *argv[], char *envp[]) argc--; argv++; - /* So now what name do we want to invoke? */ + /* So now what function do we want to invoke? */ progname = strrchr(argv[0], '/'); if (progname) progname++; else progname = argv[0]; } else { /* Invoked by symlink */ - - /* Block any deprecated functions. */ - for (i = 0; i < ARRAY_SIZE(dep_cmds); i++) - if (0 == strcmp(dep_cmds[i], progname)) - deprecated(progname); + via_symlink = 1; + /* Block any deprecated functions. */ + for (i = 0; i < ARRAY_SIZE(dep_cmds); i++) + if (0 == strcmp(dep_cmds[i], progname)) + deprecated(progname); } /* See if it's asking for something we know how to do ourselves */ @@ -278,9 +264,14 @@ int main(int argc, char *argv[], char *envp[]) if (0 == strcmp((*cmd)->name, progname)) return (*cmd)->handler(argc, argv); - /* Nope, it must be wrapped */ + /* Nope */ + if (!via_symlink) { + do_help(0, 0); + exit(1); + } + + /* Complain about bogus symlink */ - /* The old binaries live under the true executable. Find out where that is. */ myproc = getpid(); snprintf(buf, sizeof(buf), "/proc/%d/exe", myproc); r = readlink(buf, truename, PATH_MAX - 1); @@ -288,37 +279,18 @@ int main(int argc, char *argv[], char *envp[]) fprintf(stderr, "%s is lost: %s => %s: %s\n", MYNAME, argv[0], buf, strerror(errno)); exit(1); - } else if (r == PATH_MAX - 1) { - /* Yes, it might _just_ fit, but we'll count that as wrong anyway. We can't - * determine the right size using the example in the readlink manpage, - * because the /proc symlink returns an st_size of 0. */ - fprintf(stderr, "%s is too long: %s => %s\n", MYNAME, argv[0], buf); - exit(1); } - truename[r] = '\0'; - s = strrchr(truename, '/'); /* Find the true directory */ - if (s) { - *s = '\0'; - } else { /* I don't think this can happen */ - fprintf(stderr, "%s says %s doesn't make sense\n", MYNAME, truename); - exit(1); - } - /* If the old binary path doesn't fit, just give up. */ - r = snprintf(oldname, PATH_MAX, "%s/%s/%s", truename, SUBDIR, progname); - if (r >= PATH_MAX) { - fprintf(stderr, "%s/%s/%s is too long\n", truename, SUBDIR, progname); - exit(1); - } - fflush(0); -#ifdef COVERAGE - /* Write gcov data prior to exec. */ - __gcov_flush(); -#endif - execve(oldname, argv, envp); - - fprintf(stderr, "%s failed to exec %s: %s\n", MYNAME, - oldname, strerror(errno)); + fprintf(stderr, "\n\ +The program\n\n %s\n\nis a symlink to\n\n %s\n\ +\n\ +However, " MYNAME " doesn't know how to implement that function.\n\ +\n\ +This is probably an error in your installation. If the problem persists\n\ +after a fresh checkout/build/install, please open a bug at\n\ +\n\ + http://dev.chromium.org/for-testers/bug-reporting-guidelines\n\ +\n", fullname, truename); return 1; } diff --git a/utility/include/kernel_blob.h b/futility/kernel_blob.h index f9175f65..173d48d2 100644 --- a/utility/include/kernel_blob.h +++ b/futility/kernel_blob.h @@ -1,4 +1,4 @@ -// Copyright (c) 2010 The Chromium OS Authors. All rights reserved. +// Copyright 2010 The Chromium OS Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. // diff --git a/tests/futility/binary_editor.c b/tests/futility/binary_editor.c new file mode 100644 index 00000000..f6a971d3 --- /dev/null +++ b/tests/futility/binary_editor.c @@ -0,0 +1,39 @@ +/* + * Copyright 2014 The Chromium OS Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + * + * This is a very simple binary editor, used to create corrupted structs for + * testing. It copies stdin to stdout, replacing bytes beginning at the given + * offset with the specified 8-bit values. + * + * There is NO conversion checking of the arguments. + */ + +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> + +int main(int argc, char *argv[]) +{ + uint32_t offset, curpos, curarg; + int c; + + if (argc < 3) { + fprintf(stderr, "Need two or more args: OFFSET VAL [VAL...]\n"); + return 1; + } + + offset = (uint32_t)strtoul(argv[1], 0, 0); + curarg = 2; + for ( curpos = 0; (c = fgetc(stdin)) != EOF; curpos++) { + + if (curpos == offset && curarg < argc) { + c = (uint8_t)strtoul(argv[curarg++], 0, 0); + offset++; + } + + fputc(c, stdout); + } + return 0; +} diff --git a/tests/futility/run_test_scripts.sh b/tests/futility/run_test_scripts.sh index f8c25ef9..7c8d86b5 100755 --- a/tests/futility/run_test_scripts.sh +++ b/tests/futility/run_test_scripts.sh @@ -1,5 +1,5 @@ #!/bin/bash -eu -# Copyright (c) 2013 The Chromium OS Authors. All rights reserved. +# Copyright 2013 The Chromium OS Authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. @@ -13,7 +13,6 @@ BINDIR="$1" shift FUTILITY="$BINDIR/futility" -OLDDIR="$BINDIR/old_bins" # The Makefile should export the $BUILD directory, but if it's not just warn @@ -28,7 +27,6 @@ OUTDIR="${BUILD}/tests/futility_test_results" # Let each test know where to find things... export FUTILITY -export OLDDIR export SCRIPTDIR export OUTDIR @@ -36,6 +34,7 @@ export OUTDIR TESTS=" ${SCRIPTDIR}/test_main.sh ${SCRIPTDIR}/test_dump_fmap.sh +${SCRIPTDIR}/test_gbb_utility.sh " @@ -44,62 +43,6 @@ pass=0 progs=0 ############################################################################## -# But first, we'll just test the wrapped functions. This will go away when -# everything is built in (chromium:196079). - -# Here are the old programs to be wrapped -# TODO(crbug.com/224734): dev_debug_vboot isn't tested right now. -PROGS=${*:-cgpt crossystem dev_sign_file dumpRSAPublicKey - dump_kernel_config enable_dev_usb_boot gbb_utility - tpm_init_temp_fix tpmc vbutil_firmware vbutil_kernel - vbutil_what_keys} - -# For now just compare results of invoking each program with no args. -# TODO: Create true rigorous tests for every program. -echo "-- old_bins --" -for i in $PROGS; do - : $(( progs++ )) - - # Try the real thing first - echo -n "$i ... " - rc=$("${OLDDIR}/$i" \ - 1>"${OUTDIR}/$i.stdout.orig" 2>"${OUTDIR}/$i.stderr.orig" \ - || echo "$?") - echo "${rc:-0}" > "${OUTDIR}/$i.return.orig" - - # Then try the symlink - rc=$("$BINDIR/$i" 1>"${OUTDIR}/$i.stdout.link" \ - 2>"${OUTDIR}/$i.stderr.link" || echo "$?") - echo "${rc:-0}" > "${OUTDIR}/$i.return.link" - - # And finally try the explicit wrapper - rc=$("$FUTILITY" "$i" 1>"${OUTDIR}/$i.stdout.futil" \ - 2>"${OUTDIR}/$i.stderr.futil" || echo "$?") - echo "${rc:-0}" > "${OUTDIR}/$i.return.futil" - - # Different? - if cmp -s "${OUTDIR}/$i.return.orig" "${OUTDIR}/$i.return.link" && - cmp -s "${OUTDIR}/$i.stdout.orig" "${OUTDIR}/$i.stdout.link" && - cmp -s "${OUTDIR}/$i.stderr.orig" "${OUTDIR}/$i.stderr.link" && - cmp -s "${OUTDIR}/$i.return.orig" "${OUTDIR}/$i.return.futil" && - cmp -s "${OUTDIR}/$i.stdout.orig" "${OUTDIR}/$i.stdout.futil" && - cmp -s "${OUTDIR}/$i.stderr.orig" "${OUTDIR}/$i.stderr.futil" ; then - green "passed" - : $(( pass++ )) - rm -f ${OUTDIR}/$i.{stdout,stderr,return}.{orig,link,futil} - else - red "failed" - fi -done - -# How many wrapped executables are left to incorporate? Did we check them all? -xprogs=$(find ${OLDDIR} -type f -perm /111 | wc -l) -if [ $xprogs -gt 0 ]; then - yellow "${progs}/${xprogs} wrapped executables tested" -fi - - -############################################################################## # Invoke the scripts that test the builtin functions. echo "-- builtin --" diff --git a/tests/futility/test_dump_fmap.sh b/tests/futility/test_dump_fmap.sh index d8e37c10..b74c0de0 100755 --- a/tests/futility/test_dump_fmap.sh +++ b/tests/futility/test_dump_fmap.sh @@ -1,13 +1,13 @@ #!/bin/bash -eux -# Copyright (c) 2013 The Chromium OS Authors. All rights reserved. +# Copyright 2013 The Chromium OS Authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. me=${0##*/} +TMP="$me.tmp" # Work in scratch directory cd "$OUTDIR" -TMP="$me.tmp" # Good FMAP "$FUTILITY" dump_fmap -f "${SCRIPTDIR}/data_fmap.bin" > "$TMP" @@ -19,17 +19,18 @@ cmp "${SCRIPTDIR}/data_fmap_expect_p.txt" "$TMP" "$FUTILITY" dump_fmap -h "${SCRIPTDIR}/data_fmap.bin" > "$TMP" cmp "${SCRIPTDIR}/data_fmap_expect_h.txt" "$TMP" + # This should fail because the input file is truncated and doesn't really # contain the stuff that the FMAP claims it does. -! "$FUTILITY" dump_fmap -x "${SCRIPTDIR}/data_fmap.bin" FMAP +if "$FUTILITY" dump_fmap -x "${SCRIPTDIR}/data_fmap.bin" FMAP; then false; fi # However, this should work. "$FUTILITY" dump_fmap -x "${SCRIPTDIR}/data_fmap.bin" SI_DESC > "$TMP" cmp "${SCRIPTDIR}/data_fmap_expect_x.txt" "$TMP" -# This FMAP has problems, and will fail. -! "$FUTILITY" dump_fmap -h "${SCRIPTDIR}/data_fmap2.bin" > "$TMP" +# This FMAP has problems, and should fail. +if "$FUTILITY" dump_fmap -h "${SCRIPTDIR}/data_fmap2.bin" > "$TMP"; then false; fi cmp "${SCRIPTDIR}/data_fmap2_expect_h.txt" "$TMP" "$FUTILITY" dump_fmap -hh "${SCRIPTDIR}/data_fmap2.bin" > "$TMP" @@ -40,5 +41,5 @@ cmp "${SCRIPTDIR}/data_fmap2_expect_hhH.txt" "$TMP" # cleanup -rm -f "$TMP" FMAP SI_DESC +rm -f ${TMP}* FMAP SI_DESC exit 0 diff --git a/tests/futility/test_gbb_utility.sh b/tests/futility/test_gbb_utility.sh new file mode 100755 index 00000000..26e5ef1a --- /dev/null +++ b/tests/futility/test_gbb_utility.sh @@ -0,0 +1,206 @@ +#!/bin/bash -eux +# Copyright 2014 The Chromium OS Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +me=${0##*/} +TMP="$me.tmp" + +# Work in scratch directory +cd "$OUTDIR" + +# Helper utility to modify binary blobs +REPLACE="${BUILD_RUN}/tests/futility/binary_editor" + +# First, let's test the basic functionality + +# For simplicity, we'll use the same size for all properties. +${FUTILITY} gbb_utility -c 16,0x10,16,0x10 ${TMP}.blob + +# Flags +${FUTILITY} gbb_utility -s --flags=0xdeadbeef ${TMP}.blob +${FUTILITY} gbb_utility -g --flags ${TMP}.blob | grep -i 0xdeadbeef + +# HWID length should include the terminating null - this is too long +if ${FUTILITY} gbb_utility -s -i "0123456789ABCDEF" ${TMP}.blob; then false; fi +# This works +${FUTILITY} gbb_utility -s -i "0123456789ABCDE" ${TMP}.blob +# Read it back? +${FUTILITY} gbb_utility -g ${TMP}.blob | grep "0123456789ABCDE" + +# Same kind of tests for the other fields, but they need binary files. + +# too long +dd if=/dev/urandom bs=17 count=1 of=${TMP}.data1.toolong +dd if=/dev/urandom bs=17 count=1 of=${TMP}.data2.toolong +dd if=/dev/urandom bs=17 count=1 of=${TMP}.data3.toolong +if ${FUTILITY} gbb_utility -s --rootkey ${TMP}.data1.toolong ${TMP}.blob; then false; fi +if ${FUTILITY} gbb_utility -s --recoverykey ${TMP}.data2.toolong ${TMP}.blob; then false; fi +if ${FUTILITY} gbb_utility -s --bmpfv ${TMP}.data3.toolong ${TMP}.blob; then false; fi + +# shorter than max should be okay, though +dd if=/dev/urandom bs=10 count=1 of=${TMP}.data1.short +dd if=/dev/urandom bs=10 count=1 of=${TMP}.data2.short +dd if=/dev/urandom bs=10 count=1 of=${TMP}.data3.short +${FUTILITY} gbb_utility -s \ + --rootkey ${TMP}.data1.short \ + --recoverykey ${TMP}.data2.short \ + --bmpfv ${TMP}.data3.short ${TMP}.blob +# read 'em back +${FUTILITY} gbb_utility -g \ + --rootkey ${TMP}.read1 \ + --recoverykey ${TMP}.read2 \ + --bmpfv ${TMP}.read3 ${TMP}.blob +# Verify (but remember, it's short) +cmp -n 10 ${TMP}.data1.short ${TMP}.read1 +cmp -n 10 ${TMP}.data2.short ${TMP}.read2 +cmp -n 10 ${TMP}.data3.short ${TMP}.read3 + +# Okay +dd if=/dev/urandom bs=16 count=1 of=${TMP}.data1 +dd if=/dev/urandom bs=16 count=1 of=${TMP}.data2 +dd if=/dev/urandom bs=16 count=1 of=${TMP}.data3 +${FUTILITY} gbb_utility -s --rootkey ${TMP}.data1 ${TMP}.blob +${FUTILITY} gbb_utility -s --recoverykey ${TMP}.data2 ${TMP}.blob +${FUTILITY} gbb_utility -s --bmpfv ${TMP}.data3 ${TMP}.blob + +# Read 'em back. +${FUTILITY} gbb_utility -g --rootkey ${TMP}.read1 ${TMP}.blob +${FUTILITY} gbb_utility -g --recoverykey ${TMP}.read2 ${TMP}.blob +${FUTILITY} gbb_utility -g --bmpfv ${TMP}.read3 ${TMP}.blob +# Verify +cmp ${TMP}.data1 ${TMP}.read1 +cmp ${TMP}.data2 ${TMP}.read2 +cmp ${TMP}.data3 ${TMP}.read3 + + +# Okay, creating GBB blobs seems to work. Now let's make sure that corrupted +# blobs are rejected. + +# Danger Will Robinson! We assume that ${TMP}.blob has this binary struct: +# +# Field Offset Value +# +# signature: 0x0000 $GBB +# major_version: 0x0004 0x0001 +# minor_version: 0x0006 0x0001 +# header_size: 0x0008 0x00000080 +# flags: 0x000c 0xdeadbeef +# hwid_offset: 0x0010 0x00000080 +# hwid_size: 0x0014 0x00000010 +# rootkey_offset: 0x0018 0x00000090 +# rootkey_size: 0x001c 0x00000010 +# bmpfv_offset: 0x0020 0x000000a0 +# bmpfv_size: 0x0024 0x00000010 +# recovery_key_offset: 0x0028 0x000000b0 +# recovery_key_size: 0x002c 0x00000010 +# pad: 0x0030 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +# 0x0040 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +# 0x0050 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +# 0x0060 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +# 0x0070 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +# (HWID) 0x0080 30 31 32 33 34 35 36 37 38 39 41 42 43 44 45 00 +# (rootkey) 0x0090 xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx +# (bmpfv) 0x00a0 xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx +# (recovery_key) 0x00b0 xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx +# 0x00c0 <EOF> +# + +# bad major_version +cat ${TMP}.blob | ${REPLACE} 0x4 2 > ${TMP}.blob.bad +if ${FUTILITY} gbb_utility ${TMP}.blob.bad; then false; fi + +# header size too large +cat ${TMP}.blob | ${REPLACE} 0x8 0x81 > ${TMP}.blob.bad +if ${FUTILITY} gbb_utility ${TMP}.blob.bad; then false; fi + +# header size too small +cat ${TMP}.blob | ${REPLACE} 0x8 0x7f > ${TMP}.blob.bad +if ${FUTILITY} gbb_utility ${TMP}.blob.bad; then false; fi + +# HWID not null-terminated is invalid +cat ${TMP}.blob | ${REPLACE} 0x8f 0x41 > ${TMP}.blob.bad +if ${FUTILITY} gbb_utility ${TMP}.blob.bad; then false; fi + +# HWID of length zero is okay +cat ${TMP}.blob | ${REPLACE} 0x14 0x00 > ${TMP}.blob.ok +${FUTILITY} gbb_utility ${TMP}.blob.ok +# And HWID of length 1 consisting only of '\0' is okay, too. +cat ${TMP}.blob | ${REPLACE} 0x14 0x01 | ${REPLACE} 0x80 0x00 > ${TMP}.blob.ok +${FUTILITY} gbb_utility ${TMP}.blob.ok + +# zero-length HWID not null-terminated is invalid +cat ${TMP}.blob | ${REPLACE} 0x8f 0x41 > ${TMP}.blob.bad +if ${FUTILITY} gbb_utility ${TMP}.blob.bad; then false; fi + +# hwid_offset < GBB_HEADER_SIZE is invalid +cat ${TMP}.blob | ${REPLACE} 0x10 0x7f > ${TMP}.blob.bad +if ${FUTILITY} gbb_utility ${TMP}.blob.bad; then false; fi +cat ${TMP}.blob | ${REPLACE} 0x10 0x00 > ${TMP}.blob.bad +if ${FUTILITY} gbb_utility ${TMP}.blob.bad; then false; fi + +# rootkey_offset < GBB_HEADER_SIZE is invalid +cat ${TMP}.blob | ${REPLACE} 0x18 0x7f > ${TMP}.blob.bad +if ${FUTILITY} gbb_utility ${TMP}.blob.bad; then false; fi +cat ${TMP}.blob | ${REPLACE} 0x18 0x00 > ${TMP}.blob.bad +if ${FUTILITY} gbb_utility ${TMP}.blob.bad; then false; fi + +# bmpfv_offset < GBB_HEADER_SIZE is invalid +cat ${TMP}.blob | ${REPLACE} 0x20 0x7f > ${TMP}.blob.bad +if ${FUTILITY} gbb_utility ${TMP}.blob.bad; then false; fi +cat ${TMP}.blob | ${REPLACE} 0x20 0x00 > ${TMP}.blob.bad +if ${FUTILITY} gbb_utility ${TMP}.blob.bad; then false; fi + +# recovery_key_offset < GBB_HEADER_SIZE is invalid +cat ${TMP}.blob | ${REPLACE} 0x28 0x7f > ${TMP}.blob.bad +if ${FUTILITY} gbb_utility ${TMP}.blob.bad; then false; fi +cat ${TMP}.blob | ${REPLACE} 0x28 0x00 > ${TMP}.blob.bad +if ${FUTILITY} gbb_utility ${TMP}.blob.bad; then false; fi + +# hwid: offset + size == end of file is okay; beyond is invalid +cat ${TMP}.blob | ${REPLACE} 0x14 0x40 > ${TMP}.blob.bad +${FUTILITY} gbb_utility -g ${TMP}.blob.bad +cat ${TMP}.blob | ${REPLACE} 0x14 0x41 > ${TMP}.blob.bad +if ${FUTILITY} gbb_utility -g ${TMP}.blob.bad; then false; fi + +# rootkey: offset + size == end of file is okay; beyond is invalid +cat ${TMP}.blob | ${REPLACE} 0x1c 0x30 > ${TMP}.blob.bad +${FUTILITY} gbb_utility -g ${TMP}.blob.bad +cat ${TMP}.blob | ${REPLACE} 0x1c 0x31 > ${TMP}.blob.bad +if ${FUTILITY} gbb_utility -g ${TMP}.blob.bad; then false; fi + +# bmpfv: offset + size == end of file is okay; beyond is invalid +cat ${TMP}.blob | ${REPLACE} 0x24 0x20 > ${TMP}.blob.bad +${FUTILITY} gbb_utility -g ${TMP}.blob.bad +cat ${TMP}.blob | ${REPLACE} 0x24 0x21 > ${TMP}.blob.bad +if ${FUTILITY} gbb_utility -g ${TMP}.blob.bad; then false; fi + +# recovery_key: offset + size == end of file is okay; beyond is invalid +cat ${TMP}.blob | ${REPLACE} 0x2c 0x10 > ${TMP}.blob.bad +${FUTILITY} gbb_utility -g ${TMP}.blob.bad +cat ${TMP}.blob | ${REPLACE} 0x2c 0x11 > ${TMP}.blob.bad +if ${FUTILITY} gbb_utility -g ${TMP}.blob.bad; then false; fi + +# hwid_size == 0 doesn't complain, but can't be set +cat ${TMP}.blob | ${REPLACE} 0x14 0x00 > ${TMP}.blob.bad +${FUTILITY} gbb_utility -g ${TMP}.blob.bad +if ${FUTILITY} gbb_utility -s -i "A" ${TMP}.blob.bad; then false; fi + +# rootkey_size == 0 gives warning, gets nothing, can't be set +cat ${TMP}.blob | ${REPLACE} 0x1c 0x00 > ${TMP}.blob.bad +${FUTILITY} gbb_utility -g --rootkey ${TMP}.read1 ${TMP}.blob.bad +if ${FUTILITY} gbb_utility -s --rootkey ${TMP}.data1 ${TMP}.blob.bad; then false; fi + +# bmpfv_size == 0 gives warning, gets nothing, can't be set +cat ${TMP}.blob | ${REPLACE} 0x24 0x00 > ${TMP}.blob.bad +${FUTILITY} gbb_utility -g --bmpfv ${TMP}.read3 ${TMP}.blob.bad +if ${FUTILITY} gbb_utility -s --bmpfv ${TMP}.data3 ${TMP}.blob.bad; then false; fi + +# recovery_key_size == 0 gives warning, gets nothing, can't be set +cat ${TMP}.blob | ${REPLACE} 0x2c 0x00 > ${TMP}.blob.bad +${FUTILITY} gbb_utility -g --recoverykey ${TMP}.read2 ${TMP}.blob.bad +if ${FUTILITY} gbb_utility -s --recoverykey ${TMP}.data2 ${TMP}.blob.bad; then false; fi + +# cleanup +rm -f ${TMP}* +exit 0 diff --git a/tests/futility/test_main.sh b/tests/futility/test_main.sh index 9bf7ca6d..62177e68 100755 --- a/tests/futility/test_main.sh +++ b/tests/futility/test_main.sh @@ -1,26 +1,32 @@ #!/bin/bash -eux -# Copyright (c) 2014 The Chromium OS Authors. All rights reserved. +# Copyright 2014 The Chromium OS Authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. me=${0##*/} +TMP="$me.tmp" # Work in scratch directory cd "$OUTDIR" -TMP="$me.tmp" - -# Built-in do-nothing commands. -# TODO(crbug.com/224734): Remove these when we have enough built-in commands -"$FUTILITY" foo hi -"$FUTILITY" bar there -"$FUTILITY" hey boys # No args returns nonzero exit code "$FUTILITY" && false -"$FUTILITY" help > "$TMP" -grep Usage "$TMP" -# TODO(crbug.com/224734): Make sure all built-in commands have help, too. +# Make sure all built-in commands are listed and have help +expected=\ +'dev_sign_file +dump_fmap +dump_kernel_config +gbb_utility +help +vbutil_firmware +vbutil_kernel +vbutil_key +vbutil_keyblock' +got=$("$FUTILITY" help | + egrep '^[[:space:]]+[^[:space:]]+[[:space:]]+[^[:space:]]+' | + awk '{print $1}') +[ "$expected" = "$got" ] # It's weird but okay if the command is a full path. "$FUTILITY" /fake/path/to/help > "$TMP" @@ -35,6 +41,16 @@ touch "$LOG" grep "$FUTILITY" "$LOG" rm "$LOG" +# Make sure deprecated functions fail via symlink +ln -sf "$FUTILITY" dev_sign_file +if ./dev_sign_file 2>${TMP}.outmsg ; then false; fi +grep deprecated ${TMP}.outmsg +# They may still fail when invoked through futility (this one does), +# but with a different error message. +"$FUTILITY" dev_sign_file 1>${TMP}.outmsg2 2>&1 || true +if grep deprecated ${TMP}.outmsg2; then false; fi + + # cleanup -rm -f "$TMP" +rm -f ${TMP}* exit 0 diff --git a/utility/gbb_utility.cc b/utility/gbb_utility.cc index 980521c5..2a8c2347 100644 --- a/utility/gbb_utility.cc +++ b/utility/gbb_utility.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2011 The Chromium OS Authors. All rights reserved. +// Copyright 2011 The Chromium OS Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. // @@ -609,7 +609,7 @@ static bool parse_creation_param(const string &input_string, if (*parsed && *parsed != ',') return false; output_vector->push_back(param); - input = parsed + 1; + input = *parsed ? parsed + 1 : parsed; } while (*input); return true; |