Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit dded5259 authored by Bowgo Tsai's avatar Bowgo Tsai Committed by android-build-merger
Browse files

Merge "libfs_avb: support loading vbmeta structs from any partition" am: 8dd6e592 am: 035e2855

am: 05025d71

Change-Id: I5b26057c96d3e912754f02f377fe9142e6ca13f6
parents 35f0de93 05025d71
Loading
Loading
Loading
Loading
+6 −0
Original line number Original line Diff line number Diff line
@@ -34,6 +34,12 @@ static std::string other_suffix(const std::string& slot_suffix) {
    return "";
    return "";
}
}


// Returns "_b" or "_a", which is *the other* slot of androidboot.slot_suffix
// in kernel cmdline, or an empty string if that parameter does not exist.
std::string fs_mgr_get_other_slot_suffix() {
    return other_suffix(fs_mgr_get_slot_suffix());
}

// Returns "_a" or "_b" based on androidboot.slot_suffix in kernel cmdline, or an empty string
// Returns "_a" or "_b" based on androidboot.slot_suffix in kernel cmdline, or an empty string
// if that parameter does not exist.
// if that parameter does not exist.
std::string fs_mgr_get_slot_suffix() {
std::string fs_mgr_get_slot_suffix() {
+1 −0
Original line number Original line Diff line number Diff line
@@ -98,6 +98,7 @@ int fs_mgr_has_sysfs_path(const struct fstab_rec* fstab);
int fs_mgr_is_fs_verity(const struct fstab_rec* fstab);
int fs_mgr_is_fs_verity(const struct fstab_rec* fstab);


std::string fs_mgr_get_slot_suffix();
std::string fs_mgr_get_slot_suffix();
std::string fs_mgr_get_other_slot_suffix();
std::set<std::string> fs_mgr_get_boot_devices();
std::set<std::string> fs_mgr_get_boot_devices();


struct FstabEntry {
struct FstabEntry {
+11 −1
Original line number Original line Diff line number Diff line
@@ -56,6 +56,11 @@ cc_defaults {
        "tests/data/*",
        "tests/data/*",
    ],
    ],
    static_libs: [
    static_libs: [
        "libavb",
        "libavb_host_sysdeps",
        "libdm",
        "libfs_avb",
        "libfstab",
        "libgtest_host",
        "libgtest_host",
    ],
    ],
    shared_libs: [
    shared_libs: [
@@ -86,8 +91,12 @@ cc_test_host {
    static_libs: [
    static_libs: [
        "libfs_avb_test_util",
        "libfs_avb_test_util",
    ],
    ],
    shared_libs: [
        "libcrypto",
    ],
    srcs: [
    srcs: [
        "tests/basic_test.cpp",
        "tests/basic_test.cpp",
        "tests/fs_avb_test.cpp",
    ],
    ],
}
}


@@ -96,10 +105,11 @@ cc_test_host {
    defaults: ["libfs_avb_host_test_defaults"],
    defaults: ["libfs_avb_host_test_defaults"],
    static_libs: [
    static_libs: [
        "libfs_avb_test_util",
        "libfs_avb_test_util",
        "libfstab",
    ],
    ],
    srcs: [
    srcs: [
        "avb_util.cpp",
        "util.cpp",
        "util.cpp",
        "tests/avb_util_test.cpp",
        "tests/util_test.cpp",
        "tests/util_test.cpp",
    ],
    ],
}
}
+3 −1
Original line number Original line Diff line number Diff line
@@ -29,6 +29,7 @@
#include <stdlib.h>
#include <stdlib.h>
#include <string.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/stat.h>

#include <string>
#include <string>


#include <android-base/macros.h>
#include <android-base/macros.h>
@@ -190,7 +191,8 @@ AvbSlotVerifyResult FsManagerAvbOps::AvbSlotVerify(const std::string& ab_suffix,
    // Copies avb_slot_data->vbmeta_images[].
    // Copies avb_slot_data->vbmeta_images[].
    for (size_t i = 0; i < avb_slot_data->num_vbmeta_images; i++) {
    for (size_t i = 0; i < avb_slot_data->num_vbmeta_images; i++) {
        out_vbmeta_images->emplace_back(VBMetaData(avb_slot_data->vbmeta_images[i].vbmeta_data,
        out_vbmeta_images->emplace_back(VBMetaData(avb_slot_data->vbmeta_images[i].vbmeta_data,
                                                   avb_slot_data->vbmeta_images[i].vbmeta_size));
                                                   avb_slot_data->vbmeta_images[i].vbmeta_size,
                                                   avb_slot_data->vbmeta_images[i].partition_name));
    }
    }


    // Free the local resource.
    // Free the local resource.
+343 −0
Original line number Original line Diff line number Diff line
@@ -16,19 +16,67 @@


#include "avb_util.h"
#include "avb_util.h"


#include <unistd.h>

#include <array>
#include <array>
#include <sstream>
#include <sstream>


#include <android-base/file.h>
#include <android-base/file.h>
#include <android-base/strings.h>
#include <android-base/unique_fd.h>
#include <android-base/unique_fd.h>


#include "util.h"
#include "util.h"


using android::base::StartsWith;
using android::base::unique_fd;
using android::base::unique_fd;


namespace android {
namespace android {
namespace fs_mgr {
namespace fs_mgr {


// Helper functions to print enum class VBMetaVerifyResult.
const char* VBMetaVerifyResultToString(VBMetaVerifyResult result) {
    // clang-format off
    static const char* const name[] = {
        "ResultSuccess",
        "ResultError",
        "ResultErrorVerification",
        "ResultUnknown",
    };
    // clang-format on

    uint32_t index = static_cast<uint32_t>(result);
    uint32_t unknown_index = sizeof(name) / sizeof(char*) - 1;
    if (index >= unknown_index) {
        index = unknown_index;
    }

    return name[index];
}

std::ostream& operator<<(std::ostream& os, VBMetaVerifyResult result) {
    os << VBMetaVerifyResultToString(result);
    return os;
}

// class VBMetaData
// ----------------
std::unique_ptr<AvbVBMetaImageHeader> VBMetaData::GetVBMetaHeader(bool update_vbmeta_size) {
    auto vbmeta_header(std::make_unique<AvbVBMetaImageHeader>());

    if (!vbmeta_header) return nullptr;

    /* Byteswap the header. */
    avb_vbmeta_image_header_to_host_byte_order((AvbVBMetaImageHeader*)vbmeta_ptr_.get(),
                                               vbmeta_header.get());
    if (update_vbmeta_size) {
        vbmeta_size_ = sizeof(AvbVBMetaImageHeader) +
                       vbmeta_header->authentication_data_block_size +
                       vbmeta_header->auxiliary_data_block_size;
    }

    return vbmeta_header;
}

// Constructs dm-verity arguments for sending DM_TABLE_LOAD ioctl to kernel.
// Constructs dm-verity arguments for sending DM_TABLE_LOAD ioctl to kernel.
// See the following link for more details:
// See the following link for more details:
// https://gitlab.com/cryptsetup/cryptsetup/wikis/DMVerity
// https://gitlab.com/cryptsetup/cryptsetup/wikis/DMVerity
@@ -173,5 +221,300 @@ bool GetHashtreeDescriptor(const std::string& partition_name,
    return true;
    return true;
}
}


// Converts a AVB partition_name (without A/B suffix) to a device partition name.
// e.g.,       "system" => "system_a",
//       "system_other" => "system_b".
//
// If the device is non-A/B, converts it to a partition name without suffix.
// e.g.,       "system" => "system",
//       "system_other" => "system".
std::string AvbPartitionToDevicePatition(const std::string& avb_partition_name,
                                         const std::string& ab_suffix,
                                         const std::string& ab_other_suffix) {
    bool is_other_slot = false;
    std::string sanitized_partition_name(avb_partition_name);

    auto other_suffix = sanitized_partition_name.rfind("_other");
    if (other_suffix != std::string::npos) {
        sanitized_partition_name.erase(other_suffix);  // converts system_other => system
        is_other_slot = true;
    }

    auto append_suffix = is_other_slot ? ab_other_suffix : ab_suffix;
    return sanitized_partition_name + append_suffix;
}

off64_t GetTotalSize(int fd) {
    off64_t saved_current = lseek64(fd, 0, SEEK_CUR);
    if (saved_current == -1) {
        PERROR << "Failed to get current position";
        return -1;
    }

    // lseek64() returns the resulting offset location from the beginning of the file.
    off64_t total_size = lseek64(fd, 0, SEEK_END);
    if (total_size == -1) {
        PERROR << "Failed to lseek64 to end of the partition";
        return -1;
    }

    // Restores the original offset.
    if (lseek64(fd, saved_current, SEEK_SET) == -1) {
        PERROR << "Failed to lseek64 to the original offset: " << saved_current;
    }

    return total_size;
}

std::unique_ptr<AvbFooter> GetAvbFooter(int fd) {
    std::array<uint8_t, AVB_FOOTER_SIZE> footer_buf;
    auto footer(std::make_unique<AvbFooter>());

    off64_t footer_offset = GetTotalSize(fd) - AVB_FOOTER_SIZE;

    ssize_t num_read =
            TEMP_FAILURE_RETRY(pread64(fd, footer_buf.data(), AVB_FOOTER_SIZE, footer_offset));
    if (num_read < 0 || num_read != AVB_FOOTER_SIZE) {
        PERROR << "Failed to read AVB footer";
        return nullptr;
    }

    if (!avb_footer_validate_and_byteswap((const AvbFooter*)footer_buf.data(), footer.get())) {
        PERROR << "AVB footer verification failed.";
        return nullptr;
    }

    return footer;
}

bool VerifyPublicKeyBlob(const uint8_t* key, size_t length, const std::string& expected_key_blob) {
    if (expected_key_blob.empty()) {  // no expectation of the key, return true.
        return true;
    }
    if (expected_key_blob.size() != length) {
        return false;
    }
    if (0 == memcmp(key, expected_key_blob.data(), length)) {
        return true;
    }
    return false;
}

VBMetaVerifyResult VerifyVBMetaSignature(const VBMetaData& vbmeta,
                                         const std::string& expected_public_key_blob) {
    const uint8_t* pk_data;
    size_t pk_len;
    ::AvbVBMetaVerifyResult vbmeta_ret;

    vbmeta_ret = avb_vbmeta_image_verify(vbmeta.data(), vbmeta.size(), &pk_data, &pk_len);

    switch (vbmeta_ret) {
        case AVB_VBMETA_VERIFY_RESULT_OK:
            if (pk_data == nullptr || pk_len <= 0) {
                LERROR << vbmeta.partition()
                       << ": Error verifying vbmeta image: failed to get public key";
                return VBMetaVerifyResult::kError;
            }
            if (!VerifyPublicKeyBlob(pk_data, pk_len, expected_public_key_blob)) {
                LERROR << vbmeta.partition() << ": Error verifying vbmeta image: public key used to"
                       << " sign data does not match key in chain descriptor";
                return VBMetaVerifyResult::kErrorVerification;
            }
            return VBMetaVerifyResult::kSuccess;
        case AVB_VBMETA_VERIFY_RESULT_OK_NOT_SIGNED:
        case AVB_VBMETA_VERIFY_RESULT_HASH_MISMATCH:
        case AVB_VBMETA_VERIFY_RESULT_SIGNATURE_MISMATCH:
            LERROR << vbmeta.partition() << ": Error verifying vbmeta image: "
                   << avb_vbmeta_verify_result_to_string(vbmeta_ret);
            return VBMetaVerifyResult::kErrorVerification;
        case AVB_VBMETA_VERIFY_RESULT_INVALID_VBMETA_HEADER:
            // No way to continue this case.
            LERROR << vbmeta.partition() << ": Error verifying vbmeta image: invalid vbmeta header";
            break;
        case AVB_VBMETA_VERIFY_RESULT_UNSUPPORTED_VERSION:
            // No way to continue this case.
            LERROR << vbmeta.partition()
                   << ": Error verifying vbmeta image: unsupported AVB version";
            break;
        default:
            LERROR << "Unknown vbmeta image verify return value: " << vbmeta_ret;
            break;
    }

    return VBMetaVerifyResult::kError;
}

std::unique_ptr<VBMetaData> VerifyVBMetaData(int fd, const std::string& partition_name,
                                             const std::string& expected_public_key_blob,
                                             VBMetaVerifyResult* out_verify_result) {
    uint64_t vbmeta_offset = 0;
    uint64_t vbmeta_size = VBMetaData::kMaxVBMetaSize;
    bool is_vbmeta_partition = StartsWith(partition_name, "vbmeta");

    if (!is_vbmeta_partition) {
        std::unique_ptr<AvbFooter> footer = GetAvbFooter(fd);
        if (!footer) {
            return nullptr;
        }
        vbmeta_offset = footer->vbmeta_offset;
        vbmeta_size = footer->vbmeta_size;
    }

    if (vbmeta_size > VBMetaData::kMaxVBMetaSize) {
        LERROR << "VbMeta size in footer exceeds kMaxVBMetaSize";
        return nullptr;
    }

    auto vbmeta = std::make_unique<VBMetaData>(vbmeta_size, partition_name);
    ssize_t num_read = TEMP_FAILURE_RETRY(pread64(fd, vbmeta->data(), vbmeta_size, vbmeta_offset));
    // Allows partial read for vbmeta partition, because its vbmeta_size is kMaxVBMetaSize.
    if (num_read < 0 || (!is_vbmeta_partition && static_cast<uint64_t>(num_read) != vbmeta_size)) {
        PERROR << partition_name << ": Failed to read vbmeta at offset " << vbmeta_offset
               << " with size " << vbmeta_size;
        return nullptr;
    }

    auto verify_result = VerifyVBMetaSignature(*vbmeta, expected_public_key_blob);
    if (out_verify_result != nullptr) *out_verify_result = verify_result;

    if (verify_result == VBMetaVerifyResult::kSuccess ||
        verify_result == VBMetaVerifyResult::kErrorVerification) {
        return vbmeta;
    }

    return nullptr;
}

bool RollbackDetected(const std::string& partition_name ATTRIBUTE_UNUSED,
                      uint64_t rollback_index ATTRIBUTE_UNUSED) {
    // TODO(bowgotsai): Support rollback protection.
    return false;
}

std::vector<ChainInfo> GetChainPartitionInfo(const VBMetaData& vbmeta, bool* fatal_error) {
    CHECK(fatal_error != nullptr);
    std::vector<ChainInfo> chain_partitions;

    size_t num_descriptors;
    std::unique_ptr<const AvbDescriptor* [], decltype(&avb_free)> descriptors(
            avb_descriptor_get_all(vbmeta.data(), vbmeta.size(), &num_descriptors), avb_free);

    if (!descriptors || num_descriptors < 1) {
        return {};
    }

    for (size_t i = 0; i < num_descriptors; i++) {
        AvbDescriptor desc;
        if (!avb_descriptor_validate_and_byteswap(descriptors[i], &desc)) {
            LERROR << "Descriptor[" << i << "] is invalid in vbmeta: " << vbmeta.partition();
            *fatal_error = true;
            return {};
        }
        if (desc.tag == AVB_DESCRIPTOR_TAG_CHAIN_PARTITION) {
            AvbChainPartitionDescriptor chain_desc;
            if (!avb_chain_partition_descriptor_validate_and_byteswap(
                        (AvbChainPartitionDescriptor*)descriptors[i], &chain_desc)) {
                LERROR << "Chain descriptor[" << i
                       << "] is invalid in vbmeta: " << vbmeta.partition();
                *fatal_error = true;
                return {};
            }
            const char* chain_partition_name =
                    ((const char*)descriptors[i]) + sizeof(AvbChainPartitionDescriptor);
            const char* chain_public_key_blob =
                    chain_partition_name + chain_desc.partition_name_len;
            chain_partitions.emplace_back(
                    std::string(chain_partition_name, chain_desc.partition_name_len),
                    std::string(chain_public_key_blob, chain_desc.public_key_len));
        }
    }

    return chain_partitions;
}

VBMetaVerifyResult LoadAndVerifyVbmetaImpl(
        const std::string& partition_name, const std::string& ab_suffix,
        const std::string& ab_other_suffix, const std::string& expected_public_key_blob,
        bool allow_verification_error, bool load_chained_vbmeta, bool rollback_protection,
        std::function<std::string(const std::string&)> device_path_constructor,
        bool is_chained_vbmeta, std::vector<VBMetaData>* out_vbmeta_images) {
    // Ensures the device path (might be a symlink created by init) is ready to access.
    auto device_path = device_path_constructor(
            AvbPartitionToDevicePatition(partition_name, ab_suffix, ab_other_suffix));
    if (!WaitForFile(device_path, 1s)) {
        PERROR << "No such partition: " << device_path;
        return VBMetaVerifyResult::kError;
    }

    unique_fd fd(TEMP_FAILURE_RETRY(open(device_path.c_str(), O_RDONLY | O_CLOEXEC)));
    if (fd < 0) {
        PERROR << "Failed to open: " << device_path;
        return VBMetaVerifyResult::kError;
    }

    VBMetaVerifyResult verify_result;
    std::unique_ptr<VBMetaData> vbmeta =
            VerifyVBMetaData(fd, partition_name, expected_public_key_blob, &verify_result);
    if (!vbmeta) {
        LERROR << partition_name << ": Failed to load vbmeta, result: " << verify_result;
        return VBMetaVerifyResult::kError;
    }

    if (!allow_verification_error && verify_result == VBMetaVerifyResult::kErrorVerification) {
        LERROR << partition_name << ": allow verification error is not allowed";
        return VBMetaVerifyResult::kError;
    }

    std::unique_ptr<AvbVBMetaImageHeader> vbmeta_header =
            vbmeta->GetVBMetaHeader(true /* update_vbmeta_size */);
    if (!vbmeta_header) {
        LERROR << partition_name << ": Failed to get vbmeta header";
        return VBMetaVerifyResult::kError;
    }

    if (rollback_protection && RollbackDetected(partition_name, vbmeta_header->rollback_index)) {
        return VBMetaVerifyResult::kError;
    }

    // vbmeta flags can only be set by the top-level vbmeta image.
    if (is_chained_vbmeta && vbmeta_header->flags != 0) {
        LERROR << partition_name << ": chained vbmeta image has non-zero flags";
        return VBMetaVerifyResult::kError;
    }

    if (out_vbmeta_images) {
        out_vbmeta_images->emplace_back(std::move(*vbmeta));
    }

    // If verification has been disabled by setting a bit in the image, we're done.
    if (vbmeta_header->flags & AVB_VBMETA_IMAGE_FLAGS_VERIFICATION_DISABLED) {
        LWARNING << "VERIFICATION_DISABLED bit is set for partition: " << partition_name;
        return verify_result;
    }

    if (load_chained_vbmeta) {
        bool fatal_error = false;
        auto chain_partitions = GetChainPartitionInfo(*out_vbmeta_images->rbegin(), &fatal_error);
        if (fatal_error) {
            return VBMetaVerifyResult::kError;
        }
        for (auto& chain : chain_partitions) {
            auto sub_ret = LoadAndVerifyVbmetaImpl(
                    chain.partition_name, ab_suffix, ab_other_suffix, chain.public_key_blob,
                    allow_verification_error, load_chained_vbmeta, rollback_protection,
                    device_path_constructor, true, /* is_chained_vbmeta */
                    out_vbmeta_images);
            if (sub_ret != VBMetaVerifyResult::kSuccess) {
                verify_result = sub_ret;  // might be 'ERROR' or 'ERROR VERIFICATION'.
                if (verify_result == VBMetaVerifyResult::kError) {
                    return verify_result;  // stop here if we got an 'ERROR'.
                }
            }
        }
    }

    return verify_result;
}

}  // namespace fs_mgr
}  // namespace fs_mgr
}  // namespace android
}  // namespace android
Loading