avbctl: New tool to control AVB behavior at runtime.
am: 86fd178603
Change-Id: Idbe347a63286dcd2a17610df461818ba0c02cf59
diff --git a/Android.mk b/Android.mk
index 36b4bea..901fe02 100644
--- a/Android.mk
+++ b/Android.mk
@@ -80,6 +80,32 @@
libavb/avb_version.c
include $(BUILD_STATIC_LIBRARY)
+# Build avbctl for the target.
+include $(CLEAR_VARS)
+LOCAL_MODULE := avbctl
+LOCAL_MODULE_CLASS := EXECUTABLES
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)
+LOCAL_CPP_EXTENSION := .cc
+LOCAL_CLANG := true
+LOCAL_CFLAGS := $(avb_common_cflags) -DAVB_COMPILATION -DAVB_ENABLE_DEBUG
+LOCAL_CPPFLAGS := $(avb_common_cppflags)
+LOCAL_LDFLAGS := $(avb_common_ldflags)
+LOCAL_STATIC_LIBRARIES := \
+ libavb \
+ libfs_mgr
+LOCAL_SHARED_LIBRARIES := \
+ libbase \
+ libhidlbase \
+ libhidltransport \
+ libhwbinder \
+ libutils \
+ android.hardware.boot@1.0
+LOCAL_SRC_FILES := \
+ libavb_ab/avb_ab_flow.c \
+ libavb_user/avb_ops_user.c \
+ tools/avbctl/avbctl.cc
+include $(BUILD_EXECUTABLE)
+
# Build libavb for the host (for unit tests).
include $(CLEAR_VARS)
LOCAL_MODULE := libavb_host
@@ -196,10 +222,9 @@
LOCAL_MODULE_RELATIVE_PATH := hw
LOCAL_REQUIRED_MODULES := libavb
LOCAL_SRC_FILES := \
- boot_control/boot_control_avb.c \
- boot_control/avb_ops_device.c \
libavb_ab/avb_ab_flow.c \
- libavb/avb_sysdeps_posix.c
+ libavb_user/avb_ops_user.c \
+ boot_control/boot_control_avb.c
LOCAL_CLANG := true
LOCAL_CFLAGS := $(avb_common_cflags) -DAVB_COMPILATION
LOCAL_LDFLAGS := $(avb_common_ldflags)
diff --git a/README.md b/README.md
index 3e36f76..8c0d550 100644
--- a/README.md
+++ b/README.md
@@ -92,6 +92,9 @@
+ An A/B implementation for use in boot loaders.
* `libavb_atx/`
+ An Android Things Extension for validating public key metadata.
+* `libavb_user/`
+ + Contains an AvbOps implementation suitable for use in userspace
+ on the device (used in boot_control.avb and avbctl).
* `boot_control/`
+ An implemementation of the Android boot_control HAL for use with
boot loaders using `libavb_ab`.
@@ -105,6 +108,9 @@
* `test/`
+ Unit tests for `abvtool`, `libavb`, `libavb_ab`, and
`libavb_atx`.
+* `tools/avbctl/`
+ + Contains the source-code for a tool that can be used to control
+ AVB at runtime.
* `examples/uefi/`
+ Contains the source-code for a UEFI-based boot-loader utilizing
`libavb/` and `libavb_ab/`.
diff --git a/boot_control/boot_control_avb.c b/boot_control/boot_control_avb.c
index 8775d09..f0e89d2 100644
--- a/boot_control/boot_control_avb.c
+++ b/boot_control/boot_control_avb.c
@@ -29,17 +29,17 @@
#include <hardware/boot_control.h>
#include <hardware/hardware.h>
-#include "avb_ops_device.h"
+#include <libavb_user/libavb_user.h>
-static AvbABOps* ab_ops = NULL;
+static AvbOps* ops = NULL;
static void module_init(boot_control_module_t* module) {
- if (ab_ops != NULL) {
+ if (ops != NULL) {
return;
}
- ab_ops = avb_ops_device_new();
- if (ab_ops == NULL) {
+ ops = avb_ops_user_new();
+ if (ops == NULL) {
avb_error("Unable to allocate AvbOps instance.\n");
}
}
@@ -64,7 +64,7 @@
}
static int module_markBootSuccessful(boot_control_module_t* module) {
- if (avb_ab_mark_slot_successful(ab_ops, module_getCurrentSlot(module)) ==
+ if (avb_ab_mark_slot_successful(ops->ab_ops, module_getCurrentSlot(module)) ==
AVB_IO_RESULT_OK) {
return 0;
} else {
@@ -74,7 +74,7 @@
static int module_setActiveBootSlot(boot_control_module_t* module,
unsigned int slot) {
- if (avb_ab_mark_slot_active(ab_ops, slot) == AVB_IO_RESULT_OK) {
+ if (avb_ab_mark_slot_active(ops->ab_ops, slot) == AVB_IO_RESULT_OK) {
return 0;
} else {
return -EIO;
@@ -83,7 +83,7 @@
static int module_setSlotAsUnbootable(struct boot_control_module* module,
unsigned int slot) {
- if (avb_ab_mark_slot_unbootable(ab_ops, slot) == AVB_IO_RESULT_OK) {
+ if (avb_ab_mark_slot_unbootable(ops->ab_ops, slot) == AVB_IO_RESULT_OK) {
return 0;
} else {
return -EIO;
@@ -97,7 +97,7 @@
avb_assert(slot < 2);
- if (avb_ab_data_read(ab_ops, &ab_data) != AVB_IO_RESULT_OK) {
+ if (avb_ab_data_read(ops->ab_ops, &ab_data) != AVB_IO_RESULT_OK) {
return -EIO;
}
@@ -115,7 +115,7 @@
avb_assert(slot < 2);
- if (avb_ab_data_read(ab_ops, &ab_data) != AVB_IO_RESULT_OK) {
+ if (avb_ab_data_read(ops->ab_ops, &ab_data) != AVB_IO_RESULT_OK) {
return -EIO;
}
diff --git a/boot_control/avb_ops_device.c b/libavb_user/avb_ops_user.c
similarity index 84%
rename from boot_control/avb_ops_device.c
rename to libavb_user/avb_ops_user.c
index 6a744c5..a95ce4d 100644
--- a/boot_control/avb_ops_device.c
+++ b/libavb_user/avb_ops_user.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2016 The Android Open Source Project
+ * Copyright (C) 2017 The Android Open Source Project
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
@@ -22,6 +22,8 @@
* SOFTWARE.
*/
+#include "avb_ops_user.h"
+
#include <errno.h>
#include <fcntl.h>
#include <linux/fs.h>
@@ -35,7 +37,7 @@
#include <cutils/properties.h>
#include <fs_mgr.h>
-#include "avb_ops_device.h"
+#include <libavb_ab/libavb_ab.h>
/* Open the appropriate fstab file and fallback to /fstab.device if
* that's what's being used.
@@ -47,7 +49,7 @@
return fstab;
}
- fstab = fs_mgr_read_fstab_with_dt("/fstab.device");
+ fstab = fs_mgr_read_fstab("/fstab.device");
return fstab;
}
@@ -120,11 +122,21 @@
fd = open_partition(partition, O_RDONLY);
if (fd == -1) {
- avb_errorv("Error opening \"", partition, "\" partition.\n", NULL);
- ret = AVB_IO_RESULT_ERROR_IO;
+ ret = AVB_IO_RESULT_ERROR_NO_SUCH_PARTITION;
goto out;
}
+ if (offset < 0) {
+ uint64_t partition_size;
+ if (ioctl(fd, BLKGETSIZE64, &partition_size) != 0) {
+ avb_errorv(
+ "Error getting size of \"", partition, "\" partition.\n", NULL);
+ ret = AVB_IO_RESULT_ERROR_IO;
+ goto out;
+ }
+ offset = partition_size - (-offset);
+ }
+
where = lseek(fd, offset, SEEK_SET);
if (where == -1) {
avb_error("Error seeking to offset.\n");
@@ -209,35 +221,33 @@
return ret;
}
-AvbABOps* avb_ops_device_new(void) {
- AvbABOps* ab_ops;
+AvbOps* avb_ops_user_new(void) {
+ AvbOps* ops;
- ab_ops = calloc(1, sizeof(AvbABOps));
- if (ab_ops == NULL) {
- avb_error("Error allocating memory for AvbABOps.\n");
- goto out;
- }
-
- ab_ops->ops = calloc(1, sizeof(AvbOps));
- if (ab_ops->ops == NULL) {
+ ops = calloc(1, sizeof(AvbOps));
+ if (ops == NULL) {
avb_error("Error allocating memory for AvbOps.\n");
- free(ab_ops);
goto out;
}
- /* We only need these operations since that's all what is being used
- * by the A/B routines.
- */
- ab_ops->ops->read_from_partition = read_from_partition;
- ab_ops->ops->write_to_partition = write_to_partition;
- ab_ops->read_ab_metadata = avb_ab_data_read;
- ab_ops->write_ab_metadata = avb_ab_data_write;
+ ops->ab_ops = calloc(1, sizeof(AvbABOps));
+ if (ops->ab_ops == NULL) {
+ avb_error("Error allocating memory for AvbABOps.\n");
+ free(ops);
+ goto out;
+ }
+ ops->ab_ops->ops = ops;
+
+ ops->read_from_partition = read_from_partition;
+ ops->write_to_partition = write_to_partition;
+ ops->ab_ops->read_ab_metadata = avb_ab_data_read;
+ ops->ab_ops->write_ab_metadata = avb_ab_data_write;
out:
- return ab_ops;
+ return ops;
}
-void avb_ops_device_free(AvbABOps* ab_ops) {
- free(ab_ops->ops);
- free(ab_ops);
+void avb_ops_user_free(AvbOps* ops) {
+ free(ops->ab_ops);
+ free(ops);
}
diff --git a/boot_control/avb_ops_device.h b/libavb_user/avb_ops_user.h
similarity index 77%
rename from boot_control/avb_ops_device.h
rename to libavb_user/avb_ops_user.h
index ad7bddb..4e547b9 100644
--- a/boot_control/avb_ops_device.h
+++ b/libavb_user/avb_ops_user.h
@@ -22,19 +22,27 @@
* SOFTWARE.
*/
-#ifndef AVB_OPS_DEVICE_H_
-#define AVB_OPS_DEVICE_H_
+#ifndef AVB_OPS_USER_H_
+#define AVB_OPS_USER_H_
-#include <libavb_ab/libavb_ab.h>
+#include <libavb/libavb.h>
-/* Allocates an AvbOps instance suitable for use on the
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Allocates an AvbOps instance suitable for use in userspace on the
* device. Returns NULL on OOM.
*
- * Free with avb_ops_device_free().
+ * Free with avb_ops_user_free().
*/
-AvbABOps* avb_ops_device_new(void);
+AvbOps* avb_ops_user_new(void);
/* Frees an AvbOps instance previously allocated with avb_ops_device_new(). */
-void avb_ops_device_free(AvbABOps* ab_ops);
+void avb_ops_user_free(AvbOps* ops);
-#endif /* AVB_OPS_DEVICE_H_ */
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* AVB_OPS_USER_H_ */
diff --git a/boot_control/avb_ops_device.h b/libavb_user/libavb_user.h
similarity index 75%
copy from boot_control/avb_ops_device.h
copy to libavb_user/libavb_user.h
index ad7bddb..4b34bdd 100644
--- a/boot_control/avb_ops_device.h
+++ b/libavb_user/libavb_user.h
@@ -22,19 +22,18 @@
* SOFTWARE.
*/
-#ifndef AVB_OPS_DEVICE_H_
-#define AVB_OPS_DEVICE_H_
+#ifndef LIBAVB_USER_H_
+#define LIBAVB_USER_H_
#include <libavb_ab/libavb_ab.h>
-/* Allocates an AvbOps instance suitable for use on the
- * device. Returns NULL on OOM.
- *
- * Free with avb_ops_device_free().
+/* The AVB_INSIDE_LIBAVB_USER_H preprocessor symbol is used to enforce
+ * library users to include only this file. All public interfaces, and
+ * only public interfaces, must be included here.
*/
-AvbABOps* avb_ops_device_new(void);
-/* Frees an AvbOps instance previously allocated with avb_ops_device_new(). */
-void avb_ops_device_free(AvbABOps* ab_ops);
+#define AVB_INSIDE_LIBAVB_USER_H
+#include "avb_ops_user.h"
+#undef AVB_INSIDE_LIBAVB_USER_H
-#endif /* AVB_OPS_DEVICE_H_ */
+#endif /* LIBAVB_USER_H_ */
diff --git a/tools/avbctl/avbctl.cc b/tools/avbctl/avbctl.cc
new file mode 100644
index 0000000..0842c59
--- /dev/null
+++ b/tools/avbctl/avbctl.cc
@@ -0,0 +1,255 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <sysexits.h>
+
+#include <android/hardware/boot/1.0/IBootControl.h>
+
+#include <libavb_user/libavb_user.h>
+
+using android::sp;
+using android::hardware::hidl_string;
+using android::hardware::Return;
+using android::hardware::boot::V1_0::IBootControl;
+using android::hardware::boot::V1_0::Slot;
+
+namespace {
+
+/* Prints program usage to |where|. */
+void usage(FILE* where, int /* argc */, char* argv[]) {
+ fprintf(where,
+ "%s - command-line tool for AVB.\n"
+ "\n"
+ "Usage:\n"
+ " %s COMMAND\n"
+ "\n"
+ "Commands:\n"
+ " %s disable-verity - Disable verity in current slot.\n"
+ " %s enable-verity - Enable verity in current slot.\n",
+ argv[0],
+ argv[0],
+ argv[0],
+ argv[0]);
+}
+
+/* Returns the A/B suffix the device booted from or the empty string
+ * if A/B is not in use.
+ */
+std::string get_ab_suffix(sp<IBootControl> module) {
+ std::string suffix = "";
+
+ if (module != nullptr) {
+ uint32_t num_slots = module->getNumberSlots();
+ if (num_slots > 1) {
+ Slot cur_slot = module->getCurrentSlot();
+ Return<void> ret =
+ module->getSuffix(cur_slot, [&suffix](const hidl_string& value) {
+ suffix = std::string(value.c_str());
+ });
+ if (!ret.isOk()) {
+ fprintf(stderr, "Error getting suffix for slot %d.\n", cur_slot);
+ }
+ }
+ }
+
+ return suffix;
+}
+
+/* Loads the toplevel AvbVBMetaImageHeader from the slot denoted by
+ * |ab_suffix| into |vbmeta_image|. No validation, verification, or
+ * byteswapping is performed.
+ *
+ * If successful, |true| is returned and the partition it was loaded
+ * from is returned in |out_partition_name| and the offset on said
+ * partition is returned in |out_vbmeta_offset|.
+ */
+bool load_top_level_vbmeta_header(
+ AvbOps* ops,
+ const std::string& ab_suffix,
+ uint8_t vbmeta_image[AVB_VBMETA_IMAGE_HEADER_SIZE],
+ std::string* out_partition_name,
+ uint64_t* out_vbmeta_offset) {
+ std::string partition_name = std::string("vbmeta") + ab_suffix;
+ uint64_t vbmeta_offset = 0;
+
+ // Only read the header.
+ size_t num_read;
+ AvbIOResult io_res = ops->read_from_partition(ops,
+ partition_name.c_str(),
+ vbmeta_offset,
+ AVB_VBMETA_IMAGE_HEADER_SIZE,
+ vbmeta_image,
+ &num_read);
+ if (io_res == AVB_IO_RESULT_ERROR_NO_SUCH_PARTITION) {
+ AvbFooter footer;
+ // Try looking for the vbmeta struct in 'boot' via the footer.
+ partition_name = std::string("boot") + ab_suffix;
+ io_res = ops->read_from_partition(ops,
+ partition_name.c_str(),
+ -AVB_FOOTER_SIZE,
+ AVB_FOOTER_SIZE,
+ &footer,
+ &num_read);
+ if (io_res != AVB_IO_RESULT_OK) {
+ fprintf(stderr,
+ "Error loading footer from partition '%s' (%d).\n",
+ partition_name.c_str(),
+ io_res);
+ return false;
+ }
+
+ if (memcmp(footer.magic, AVB_FOOTER_MAGIC, AVB_FOOTER_MAGIC_LEN) != 0) {
+ fprintf(stderr,
+ "Data from '%s' does not look like a vbmeta footer.\n",
+ partition_name.c_str());
+ return false;
+ }
+
+ vbmeta_offset = avb_be64toh(footer.vbmeta_offset);
+ io_res = ops->read_from_partition(ops,
+ partition_name.c_str(),
+ vbmeta_offset,
+ AVB_VBMETA_IMAGE_HEADER_SIZE,
+ vbmeta_image,
+ &num_read);
+ }
+
+ if (io_res != AVB_IO_RESULT_OK) {
+ fprintf(stderr,
+ "Error loading from offset %" PRIu64 " of partition '%s' (%d).\n",
+ vbmeta_offset,
+ partition_name.c_str(),
+ io_res);
+ return false;
+ }
+
+ if (out_partition_name != nullptr) {
+ *out_partition_name = partition_name;
+ }
+ if (out_vbmeta_offset != nullptr) {
+ *out_vbmeta_offset = vbmeta_offset;
+ }
+ return true;
+}
+
+/* Function to enable and disable dm-verity. The |ops| parameter
+ * should be an |AvbOps| from libavb_user and |module| can either be
+ * |nullptr| or a valid boot_control module.
+ */
+int do_set_verity(AvbOps* ops, sp<IBootControl> module, bool enable_verity) {
+ uint8_t vbmeta_image[AVB_VBMETA_IMAGE_HEADER_SIZE]; // 256 bytes.
+ std::string ab_suffix;
+ std::string partition_name;
+ uint64_t vbmeta_offset;
+ AvbIOResult io_res;
+
+ ab_suffix = get_ab_suffix(module);
+
+ if (!load_top_level_vbmeta_header(
+ ops, ab_suffix, vbmeta_image, &partition_name, &vbmeta_offset)) {
+ return EX_SOFTWARE;
+ }
+
+ if (memcmp(vbmeta_image, AVB_MAGIC, AVB_MAGIC_LEN) != 0) {
+ fprintf(stderr,
+ "Data from '%s' does not look like a vbmeta header.\n",
+ partition_name.c_str());
+ return EX_SOFTWARE;
+ }
+
+ // Set/clear the HASHTREE_DISABLED bit, as requested.
+ AvbVBMetaImageHeader* header =
+ reinterpret_cast<AvbVBMetaImageHeader*>(vbmeta_image);
+ uint32_t flags = avb_be32toh(header->flags);
+ flags &= ~AVB_VBMETA_IMAGE_FLAGS_HASHTREE_DISABLED;
+ if (!enable_verity) {
+ flags |= AVB_VBMETA_IMAGE_FLAGS_HASHTREE_DISABLED;
+ }
+ header->flags = avb_htobe32(flags);
+
+ // Write the header.
+ io_res = ops->write_to_partition(ops,
+ partition_name.c_str(),
+ vbmeta_offset,
+ AVB_VBMETA_IMAGE_HEADER_SIZE,
+ vbmeta_image);
+ if (io_res != AVB_IO_RESULT_OK) {
+ fprintf(stderr,
+ "Error writing to offset %" PRIu64 " of partition '%s' (%d).\n",
+ vbmeta_offset,
+ partition_name.c_str(),
+ io_res);
+ return EX_SOFTWARE;
+ }
+
+ fprintf(stdout,
+ "Successfully %s verity on %s.\n",
+ enable_verity ? "enabled" : "disabled",
+ partition_name.c_str());
+
+ return EX_OK;
+}
+
+} // namespace
+
+int main(int argc, char* argv[]) {
+ int ret;
+ sp<IBootControl> module;
+ AvbOps* ops = nullptr;
+
+ if (argc < 2) {
+ usage(stderr, argc, argv);
+ ret = EX_USAGE;
+ goto out;
+ }
+
+ ops = avb_ops_user_new();
+ if (ops == nullptr) {
+ fprintf(stderr, "Error getting AVB ops.\n");
+ ret = EX_SOFTWARE;
+ goto out;
+ }
+
+ // Failing to get the boot_control HAL is not a fatal error - it can
+ // happen if A/B is not in use, in which case |nullptr| is returned.
+ module = IBootControl::getService();
+
+ if (strcmp(argv[1], "disable-verity") == 0) {
+ ret = do_set_verity(ops, module, false);
+ } else if (strcmp(argv[1], "enable-verity") == 0) {
+ ret = do_set_verity(ops, module, true);
+ } else {
+ usage(stderr, argc, argv);
+ ret = EX_USAGE;
+ }
+
+ ret = EX_OK;
+out:
+ if (ops != nullptr) {
+ avb_ops_user_free(ops);
+ }
+ return ret;
+}