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