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

Commit e0f5c106 authored by Bowgo Tsai's avatar Bowgo Tsai
Browse files

first_stage_mount: reading all avb_keys before chroot

Avb keys used to verify a partition are stored in the first-stage
ramdisk. However, after /system is mounted, init will chroot into it.
This makes those keys inaccessible for later mounts, e.g., /vendor or
/product.

This change retains avb keys by reading all of them before chroot
into /system.

Note that it is intentional to perform public matching for both
preload_avb_key_blobs and fstab_entry.avb_keys in libfs_avb.
As some keys might only be availble before init chroots into /system,
e.g., /avb/key1 in the first-stage ramdisk, while other keys might
only be available after the chroot, e.g., /system/etc/avb/key2.

Bug: 147585411
Test: specify avb_keys for a partition and checks the keys are preloaded
Test: atest libfs_avb_test
Test: atest libfs_avb_internal_test
Change-Id: I6bd490c4215480db2937cdfc3fea0d616e224a91
parent ab65ef22
Loading
Loading
Loading
Loading
+32 −12
Original line number Diff line number Diff line
@@ -266,8 +266,10 @@ AvbUniquePtr AvbHandle::LoadAndVerifyVbmeta(
    return avb_handle;
}

AvbUniquePtr AvbHandle::LoadAndVerifyVbmeta(const FstabEntry& fstab_entry) {
    if (fstab_entry.avb_keys.empty()) {
AvbUniquePtr AvbHandle::LoadAndVerifyVbmeta(const FstabEntry& fstab_entry,
                                            const std::vector<std::string>& preload_avb_key_blobs) {
    // At least one of the following should be provided for public key matching.
    if (preload_avb_key_blobs.empty() && fstab_entry.avb_keys.empty()) {
        LERROR << "avb_keys=/path/to/key(s) is missing for " << fstab_entry.mount_point;
        return nullptr;
    }
@@ -309,6 +311,20 @@ AvbUniquePtr AvbHandle::LoadAndVerifyVbmeta(const FstabEntry& fstab_entry) {
            return nullptr;
    }

    bool public_key_match = false;
    // Performs key matching for preload_avb_key_blobs first, if it is present.
    if (!public_key_data.empty() && !preload_avb_key_blobs.empty()) {
        if (std::find(preload_avb_key_blobs.begin(), preload_avb_key_blobs.end(),
                      public_key_data) != preload_avb_key_blobs.end()) {
            public_key_match = true;
        }
    }
    // Performs key matching for fstab_entry.avb_keys if necessary.
    // Note that it is intentional to match both preload_avb_key_blobs and fstab_entry.avb_keys.
    // Some keys might only be availble before init chroots into /system, e.g., /avb/key1
    // in the first-stage ramdisk, while other keys might only be available after the chroot,
    // e.g., /system/etc/avb/key2.
    if (!public_key_data.empty() && !public_key_match) {
        // fstab_entry.avb_keys might be either a directory containing multiple keys,
        // or a string indicating multiple keys separated by ':'.
        std::vector<std::string> allowed_avb_keys;
@@ -319,8 +335,12 @@ AvbUniquePtr AvbHandle::LoadAndVerifyVbmeta(const FstabEntry& fstab_entry) {
        } else {
            allowed_avb_keys = Split(fstab_entry.avb_keys, ":");
        }
        if (ValidatePublicKeyBlob(public_key_data, allowed_avb_keys)) {
            public_key_match = true;
        }
    }

    if (!ValidatePublicKeyBlob(public_key_data, allowed_avb_keys)) {
    if (!public_key_match) {
        avb_handle->status_ = AvbHandleStatus::kVerificationError;
        LWARNING << "Found unknown public key used to sign " << fstab_entry.mount_point;
        if (!allow_verification_error) {
+9 −2
Original line number Diff line number Diff line
@@ -85,8 +85,15 @@ class AvbHandle {
    // TODO(bowgotsai): remove Open() and switch to LoadAndVerifyVbmeta().
    static AvbUniquePtr Open();                 // loads inline vbmeta, via libavb.
    static AvbUniquePtr LoadAndVerifyVbmeta();  // loads inline vbmeta.
    static AvbUniquePtr LoadAndVerifyVbmeta(
            const FstabEntry& fstab_entry);     // loads offline vbmeta.

    // The caller can specify optional preload_avb_key_blobs for public key matching.
    // This is mostly for init to preload AVB keys before chroot into /system.
    // Both preload_avb_key_blobs and fstab_entry.avb_keys (file paths) will be used
    // for public key matching.
    static AvbUniquePtr LoadAndVerifyVbmeta(  // loads offline vbmeta.
            const FstabEntry& fstab_entry,
            const std::vector<std::string>& preload_avb_key_blobs = {});

    static AvbUniquePtr LoadAndVerifyVbmeta(    // loads offline vbmeta.
            const std::string& partition_name, const std::string& ab_suffix,
            const std::string& ab_other_suffix, const std::string& expected_public_key,
+56 −1
Original line number Diff line number Diff line
@@ -21,6 +21,7 @@
#include <unistd.h>

#include <chrono>
#include <map>
#include <memory>
#include <set>
#include <string>
@@ -29,6 +30,7 @@
#include <android-base/chrono_utils.h>
#include <android-base/file.h>
#include <android-base/logging.h>
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
#include <fs_avb/fs_avb.h>
#include <fs_mgr.h>
@@ -45,7 +47,9 @@
#include "uevent_listener.h"
#include "util.h"

using android::base::ReadFileToString;
using android::base::Split;
using android::base::StringPrintf;
using android::base::Timer;
using android::fiemap::IImageManager;
using android::fs_mgr::AvbHandle;
@@ -95,6 +99,7 @@ class FirstStageMount {
    void GetDmLinearMetadataDevice(std::set<std::string>* devices);
    bool InitDmLinearBackingDevices(const android::fs_mgr::LpMetadata& metadata);
    void UseDsuIfPresent();
    void PreloadAvbKeys();

    ListenerAction UeventCallback(const Uevent& uevent, std::set<std::string>* required_devices);

@@ -110,6 +115,9 @@ class FirstStageMount {
    std::string super_partition_name_;
    std::unique_ptr<DeviceHandler> device_handler_;
    UeventListener uevent_listener_;
    // Reads all AVB keys before chroot into /system, as they might be used
    // later when mounting other partitions, e.g., /vendor and /product.
    std::map<std::string, std::vector<std::string>> preload_avb_key_blobs_;
};

class FirstStageMountVBootV1 : public FirstStageMount {
@@ -508,11 +516,57 @@ bool FirstStageMount::MountPartition(const Fstab::iterator& begin, bool erase_sa
    return mounted;
}

void FirstStageMount::PreloadAvbKeys() {
    for (const auto& entry : fstab_) {
        // No need to cache the key content if it's empty, or is already cached.
        if (entry.avb_keys.empty() || preload_avb_key_blobs_.count(entry.avb_keys)) {
            continue;
        }

        // Determines all key paths first.
        std::vector<std::string> key_paths;
        if (is_dir(entry.avb_keys.c_str())) {  // fstab_keys might be a dir, e.g., /avb.
            const char* avb_key_dir = entry.avb_keys.c_str();
            std::unique_ptr<DIR, int (*)(DIR*)> dir(opendir(avb_key_dir), closedir);
            if (!dir) {
                LOG(ERROR) << "Failed to opendir: " << dir;
                continue;
            }
            // Gets all key pathes under the dir.
            struct dirent* de;
            while ((de = readdir(dir.get()))) {
                if (de->d_type != DT_REG) continue;
                std::string full_path = StringPrintf("%s/%s", avb_key_dir, de->d_name);
                key_paths.emplace_back(std::move(full_path));
            }
            std::sort(key_paths.begin(), key_paths.end());
        } else {
            // avb_keys are key paths separated by ":", if it's not a dir.
            key_paths = Split(entry.avb_keys, ":");
        }

        // Reads the key content then cache it.
        std::vector<std::string> key_blobs;
        for (const auto& path : key_paths) {
            std::string key_value;
            if (!ReadFileToString(path, &key_value)) {
                continue;
            }
            key_blobs.emplace_back(std::move(key_value));
        }

        // Maps entry.avb_keys to actual key blobs.
        preload_avb_key_blobs_[entry.avb_keys] = std::move(key_blobs);
    }
}

// If system is in the fstab then we're not a system-as-root device, and in
// this case, we mount system first then pivot to it.  From that point on,
// we are effectively identical to a system-as-root device.
bool FirstStageMount::TrySwitchSystemAsRoot() {
    UseDsuIfPresent();
    // Preloading all AVB keys from the ramdisk before switching root to /system.
    PreloadAvbKeys();

    auto system_partition = std::find_if(fstab_.begin(), fstab_.end(), [](const auto& entry) {
        return entry.mount_point == "/system";
@@ -776,7 +830,8 @@ bool FirstStageMountVBootV2::SetUpDmVerity(FstabEntry* fstab_entry) {
                       << fstab_entry->mount_point;
            return true;  // Returns true to mount the partition directly.
        } else {
            auto avb_standalone_handle = AvbHandle::LoadAndVerifyVbmeta(*fstab_entry);
            auto avb_standalone_handle = AvbHandle::LoadAndVerifyVbmeta(
                    *fstab_entry, preload_avb_key_blobs_[fstab_entry->avb_keys]);
            if (!avb_standalone_handle) {
                LOG(ERROR) << "Failed to load offline vbmeta for " << fstab_entry->mount_point;
                // Fallbacks to built-in hashtree if fs_mgr_flags.avb is set.