| /* |
| * 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; |
| } |