fs_mgr_avb: refactors how vbmeta is loaded

Adds two classes FsManagerAvbhandle and FsManagerAvbVerifier to replace the
following functions or struct:

    - fs_mgr_load_vbmeta_images() -> FsManagerAvbhandle::Open()
    - fs_mgr_unload_vbmeta_images() -> deleted
    - fs_mgr_setup_avb() -> FsManagerAvbhandle::SetUpAvb()

    - androidboot_vbmeta -> FsManagerAvbVerifier
    - load_vbmeta_prop() -> FsManagerAvbVerifier::Create()
    - verify_vbmeta_images() -> FsManagerAvbVerifier::VerifyVbmetaImages()

And only invokes FsManagerAvbhandle::Open() when there is a fstab entry having
'avb' flag (need HASHTREE descriptor). fs_mgr_is_avb_used() can be
removed as it only checks system property "ro.boot.vbmeta.hash_alg" to
decide whether vbmeta needs to be loaded, which might not be accurate.

For example, there are only HASH descriptors in the verified chain but
no HASHTREE descriptors. In this case, the fs_mgr doesn't have to do
anything because it only takes care of HASHTREE descriptors.

Also adds a new class FsManagerAvbOps to provide the C++ binding
FsManagerAvbOps::AvbSlotVerify() for libavb->avb_slot_verify().

Bug: 33254008
Test: test AVB on bullhead
Change-Id: I8fe15ba01c277152630a2a5c1c5c7f25fbf34030
This commit is contained in:
Bowgo Tsai 2017-03-30 18:42:54 +08:00
parent 87d0836cda
commit 95c966a859
6 changed files with 335 additions and 341 deletions

View file

@ -707,6 +707,23 @@ static int handle_encryptable(const struct fstab_rec* rec)
}
}
static std::string extract_by_name_prefix(struct fstab* fstab) {
// We assume that there's an entry for the /misc mount point in the
// fstab file and use that to get the device file by-name prefix.
// The device needs not to have an actual /misc partition.
// e.g.,
// - /dev/block/platform/soc.0/7824900.sdhci/by-name/misc ->
// - /dev/block/platform/soc.0/7824900.sdhci/by-name/
struct fstab_rec* fstab_entry = fs_mgr_get_entry_for_mount_point(fstab, "/misc");
if (fstab_entry == nullptr) {
LERROR << "/misc mount point not found in fstab";
return "";
}
std::string full_path(fstab_entry->blk_device);
size_t end_slash = full_path.find_last_of("/");
return full_path.substr(0, end_slash + 1);
}
// TODO: add ueventd notifiers if they don't exist.
// This is just doing a wait_for_device for maximum of 1s
int fs_mgr_test_access(const char *device) {
@ -750,17 +767,12 @@ int fs_mgr_mount_all(struct fstab *fstab, int mount_mode)
int mret = -1;
int mount_errno = 0;
int attempted_idx = -1;
int avb_ret = FS_MGR_SETUP_AVB_FAIL;
FsManagerAvbUniquePtr avb_handle(nullptr);
if (!fstab) {
return -1;
}
if (fs_mgr_is_avb_used() &&
(avb_ret = fs_mgr_load_vbmeta_images(fstab)) == FS_MGR_SETUP_AVB_FAIL) {
return -1;
}
for (i = 0; i < fstab->num_entries; i++) {
/* Don't mount entries that are managed by vold or not for the mount mode*/
if ((fstab->recs[i].fs_mgr_flags & (MF_VOLDMANAGED | MF_RECOVERYONLY)) ||
@ -799,16 +811,15 @@ int fs_mgr_mount_all(struct fstab *fstab, int mount_mode)
wait_for_file(fstab->recs[i].blk_device, WAIT_TIMEOUT);
}
if (fs_mgr_is_avb_used() && (fstab->recs[i].fs_mgr_flags & MF_AVB)) {
/* If HASHTREE_DISABLED is set (cf. 'adb disable-verity'), we
* should set up the device without using dm-verity.
* The actual mounting still take place in the following
* mount_with_alternatives().
*/
if (avb_ret == FS_MGR_SETUP_AVB_HASHTREE_DISABLED) {
LINFO << "AVB HASHTREE disabled";
} else if (fs_mgr_setup_avb(&fstab->recs[i]) !=
FS_MGR_SETUP_AVB_SUCCESS) {
if (fstab->recs[i].fs_mgr_flags & MF_AVB) {
if (!avb_handle) {
avb_handle = FsManagerAvbHandle::Open(extract_by_name_prefix(fstab));
if (!avb_handle) {
LERROR << "Failed to open FsManagerAvbHandle";
return -1;
}
}
if (!avb_handle->SetUpAvb(&fstab->recs[i])) {
LERROR << "Failed to set up AVB on partition: "
<< fstab->recs[i].mount_point << ", skipping!";
/* Skips mounting the device. */
@ -934,10 +945,6 @@ int fs_mgr_mount_all(struct fstab *fstab, int mount_mode)
}
}
if (fs_mgr_is_avb_used()) {
fs_mgr_unload_vbmeta_images();
}
if (error_count) {
return -1;
} else {
@ -976,17 +983,12 @@ int fs_mgr_do_mount(struct fstab *fstab, const char *n_name, char *n_blk_device,
int mount_errors = 0;
int first_mount_errno = 0;
char *m;
int avb_ret = FS_MGR_SETUP_AVB_FAIL;
FsManagerAvbUniquePtr avb_handle(nullptr);
if (!fstab) {
return ret;
}
if (fs_mgr_is_avb_used() &&
(avb_ret = fs_mgr_load_vbmeta_images(fstab)) == FS_MGR_SETUP_AVB_FAIL) {
return ret;
}
for (i = 0; i < fstab->num_entries; i++) {
if (!fs_match(fstab->recs[i].mount_point, n_name)) {
continue;
@ -1021,16 +1023,15 @@ int fs_mgr_do_mount(struct fstab *fstab, const char *n_name, char *n_blk_device,
do_reserved_size(n_blk_device, fstab->recs[i].fs_type, &fstab->recs[i], &fs_stat);
}
if (fs_mgr_is_avb_used() && (fstab->recs[i].fs_mgr_flags & MF_AVB)) {
/* If HASHTREE_DISABLED is set (cf. 'adb disable-verity'), we
* should set up the device without using dm-verity.
* The actual mounting still take place in the following
* mount_with_alternatives().
*/
if (avb_ret == FS_MGR_SETUP_AVB_HASHTREE_DISABLED) {
LINFO << "AVB HASHTREE disabled";
} else if (fs_mgr_setup_avb(&fstab->recs[i]) !=
FS_MGR_SETUP_AVB_SUCCESS) {
if (fstab->recs[i].fs_mgr_flags & MF_AVB) {
if (!avb_handle) {
avb_handle = FsManagerAvbHandle::Open(extract_by_name_prefix(fstab));
if (!avb_handle) {
LERROR << "Failed to open FsManagerAvbHandle";
return -1;
}
}
if (!avb_handle->SetUpAvb(&fstab->recs[i])) {
LERROR << "Failed to set up AVB on partition: "
<< fstab->recs[i].mount_point << ", skipping!";
/* Skips mounting the device. */
@ -1079,9 +1080,6 @@ int fs_mgr_do_mount(struct fstab *fstab, const char *n_name, char *n_blk_device,
}
out:
if (fs_mgr_is_avb_used()) {
fs_mgr_unload_vbmeta_images();
}
return ret;
}

View file

@ -28,6 +28,7 @@
#include <android-base/file.h>
#include <android-base/parseint.h>
#include <android-base/properties.h>
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
#include <android-base/unique_fd.h>
#include <cutils/properties.h>
@ -85,24 +86,6 @@
hashtree_desc.fec_offset / hashtree_desc.data_block_size, /* fec_start */ \
VERITY_TABLE_OPT_IGNZERO, VERITY_TABLE_OPT_RESTART
AvbSlotVerifyData* fs_mgr_avb_verify_data = nullptr;
AvbOps* fs_mgr_avb_ops = nullptr;
enum HashAlgorithm {
kInvalid = 0,
kSHA256 = 1,
kSHA512 = 2,
};
struct androidboot_vbmeta {
HashAlgorithm hash_alg;
uint8_t digest[SHA512_DIGEST_LENGTH];
size_t vbmeta_size;
bool allow_verification_error;
};
androidboot_vbmeta fs_mgr_vbmeta_prop;
static inline bool nibble_value(const char& c, uint8_t* value) {
FS_MGR_CHECK(value != nullptr);
@ -159,27 +142,78 @@ static std::string bytes_to_hex(const uint8_t* bytes, size_t bytes_len) {
return hex;
}
static bool load_vbmeta_prop(androidboot_vbmeta* vbmeta_prop) {
FS_MGR_CHECK(vbmeta_prop != nullptr);
template <typename Hasher>
static std::pair<size_t, bool> verify_vbmeta_digest(const AvbSlotVerifyData& verify_data,
const uint8_t* expected_digest) {
size_t total_size = 0;
Hasher hasher;
for (size_t n = 0; n < verify_data.num_vbmeta_images; n++) {
hasher.update(verify_data.vbmeta_images[n].vbmeta_data,
verify_data.vbmeta_images[n].vbmeta_size);
total_size += verify_data.vbmeta_images[n].vbmeta_size;
}
bool matched = (memcmp(hasher.finalize(), expected_digest, Hasher::DIGEST_SIZE) == 0);
return std::make_pair(total_size, matched);
}
// Reads the following values from kernel cmdline and provides the
// VerifyVbmetaImages() to verify AvbSlotVerifyData.
// - androidboot.vbmeta.device_state
// - androidboot.vbmeta.hash_alg
// - androidboot.vbmeta.size
// - androidboot.vbmeta.digest
class FsManagerAvbVerifier {
public:
// The factory method to return a unique_ptr<FsManagerAvbVerifier>
static std::unique_ptr<FsManagerAvbVerifier> Create();
bool VerifyVbmetaImages(const AvbSlotVerifyData& verify_data);
bool IsDeviceUnlocked() { return is_device_unlocked_; }
protected:
FsManagerAvbVerifier() = default;
private:
enum HashAlgorithm {
kInvalid = 0,
kSHA256 = 1,
kSHA512 = 2,
};
HashAlgorithm hash_alg_;
uint8_t digest_[SHA512_DIGEST_LENGTH];
size_t vbmeta_size_;
bool is_device_unlocked_;
};
std::unique_ptr<FsManagerAvbVerifier> FsManagerAvbVerifier::Create() {
std::string cmdline;
android::base::ReadFileToString("/proc/cmdline", &cmdline);
if (!android::base::ReadFileToString("/proc/cmdline", &cmdline)) {
LERROR << "Failed to read /proc/cmdline";
return nullptr;
}
std::unique_ptr<FsManagerAvbVerifier> avb_verifier(new FsManagerAvbVerifier());
if (!avb_verifier) {
LERROR << "Failed to create unique_ptr<FsManagerAvbVerifier>";
return nullptr;
}
std::string hash_alg;
std::string digest;
std::string hash_alg;
for (const auto& entry : android::base::Split(android::base::Trim(cmdline), " ")) {
std::vector<std::string> pieces = android::base::Split(entry, "=");
const std::string& key = pieces[0];
const std::string& value = pieces[1];
if (key == "androidboot.vbmeta.device_state") {
vbmeta_prop->allow_verification_error = (value == "unlocked");
avb_verifier->is_device_unlocked_ = (value == "unlocked");
} else if (key == "androidboot.vbmeta.hash_alg") {
hash_alg = value;
} else if (key == "androidboot.vbmeta.size") {
if (!android::base::ParseUint(value.c_str(), &vbmeta_prop->vbmeta_size)) {
return false;
if (!android::base::ParseUint(value.c_str(), &avb_verifier->vbmeta_size_)) {
return nullptr;
}
} else if (key == "androidboot.vbmeta.digest") {
digest = value;
@ -190,48 +224,31 @@ static bool load_vbmeta_prop(androidboot_vbmeta* vbmeta_prop) {
size_t expected_digest_size = 0;
if (hash_alg == "sha256") {
expected_digest_size = SHA256_DIGEST_LENGTH * 2;
vbmeta_prop->hash_alg = kSHA256;
avb_verifier->hash_alg_ = kSHA256;
} else if (hash_alg == "sha512") {
expected_digest_size = SHA512_DIGEST_LENGTH * 2;
vbmeta_prop->hash_alg = kSHA512;
avb_verifier->hash_alg_ = kSHA512;
} else {
LERROR << "Unknown hash algorithm: " << hash_alg.c_str();
return false;
return nullptr;
}
// Reads digest.
if (digest.size() != expected_digest_size) {
LERROR << "Unexpected digest size: " << digest.size()
<< " (expected: " << expected_digest_size << ")";
return false;
return nullptr;
}
if (!hex_to_bytes(vbmeta_prop->digest, sizeof(vbmeta_prop->digest), digest)) {
if (!hex_to_bytes(avb_verifier->digest_, sizeof(avb_verifier->digest_), digest)) {
LERROR << "Hash digest contains non-hexidecimal character: " << digest.c_str();
return false;
return nullptr;
}
return true;
return avb_verifier;
}
template <typename Hasher>
static std::pair<size_t, bool> verify_vbmeta_digest(const AvbSlotVerifyData& verify_data,
const androidboot_vbmeta& vbmeta_prop) {
size_t total_size = 0;
Hasher hasher;
for (size_t n = 0; n < verify_data.num_vbmeta_images; n++) {
hasher.update(verify_data.vbmeta_images[n].vbmeta_data,
verify_data.vbmeta_images[n].vbmeta_size);
total_size += verify_data.vbmeta_images[n].vbmeta_size;
}
bool matched = (memcmp(hasher.finalize(), vbmeta_prop.digest, Hasher::DIGEST_SIZE) == 0);
return std::make_pair(total_size, matched);
}
static bool verify_vbmeta_images(const AvbSlotVerifyData& verify_data,
const androidboot_vbmeta& vbmeta_prop) {
bool FsManagerAvbVerifier::VerifyVbmetaImages(const AvbSlotVerifyData& verify_data) {
if (verify_data.num_vbmeta_images == 0) {
LERROR << "No vbmeta images";
return false;
@ -240,17 +257,17 @@ static bool verify_vbmeta_images(const AvbSlotVerifyData& verify_data,
size_t total_size = 0;
bool digest_matched = false;
if (vbmeta_prop.hash_alg == kSHA256) {
if (hash_alg_ == kSHA256) {
std::tie(total_size, digest_matched) =
verify_vbmeta_digest<SHA256Hasher>(verify_data, vbmeta_prop);
} else if (vbmeta_prop.hash_alg == kSHA512) {
verify_vbmeta_digest<SHA256Hasher>(verify_data, digest_);
} else if (hash_alg_ == kSHA512) {
std::tie(total_size, digest_matched) =
verify_vbmeta_digest<SHA512Hasher>(verify_data, vbmeta_prop);
verify_vbmeta_digest<SHA512Hasher>(verify_data, digest_);
}
if (total_size != vbmeta_prop.vbmeta_size) {
LERROR << "total vbmeta size mismatch: " << total_size
<< " (expected: " << vbmeta_prop.vbmeta_size << ")";
if (total_size != vbmeta_size_) {
LERROR << "total vbmeta size mismatch: " << total_size << " (expected: " << vbmeta_size_
<< ")";
return false;
}
@ -408,8 +425,7 @@ static bool get_hashtree_descriptor(const std::string& partition_name,
continue;
}
if (desc.tag == AVB_DESCRIPTOR_TAG_HASHTREE) {
desc_partition_name =
(const uint8_t*)descriptors[j] + sizeof(AvbHashtreeDescriptor);
desc_partition_name = (const uint8_t*)descriptors[j] + sizeof(AvbHashtreeDescriptor);
if (!avb_hashtree_descriptor_validate_and_byteswap(
(AvbHashtreeDescriptor*)descriptors[j], out_hashtree_desc)) {
continue;
@ -441,134 +457,96 @@ static bool get_hashtree_descriptor(const std::string& partition_name,
return true;
}
static bool init_is_avb_used() {
// When AVB is used, boot loader should set androidboot.vbmeta.{hash_alg,
// size, digest} in kernel cmdline or in device tree. They will then be
// imported by init process to system properties: ro.boot.vbmeta.{hash_alg, size, digest}.
//
// In case of early mount, init properties are not initialized, so we also
// ensure we look into kernel command line and device tree if the property is
// not found
//
// Checks hash_alg as an indicator for whether AVB is used.
// We don't have to parse and check all of them here. The check will
// be done in fs_mgr_load_vbmeta_images() and FS_MGR_SETUP_AVB_FAIL will
// be returned when there is an error.
std::string hash_alg;
if (!fs_mgr_get_boot_config("vbmeta.hash_alg", &hash_alg)) {
return false;
}
if (hash_alg == "sha256" || hash_alg == "sha512") {
return true;
}
return false;
}
bool fs_mgr_is_avb_used() {
static bool result = init_is_avb_used();
return result;
}
int fs_mgr_load_vbmeta_images(struct fstab* fstab) {
FS_MGR_CHECK(fstab != nullptr);
// Gets the expected hash value of vbmeta images from
// kernel cmdline.
if (!load_vbmeta_prop(&fs_mgr_vbmeta_prop)) {
return FS_MGR_SETUP_AVB_FAIL;
FsManagerAvbUniquePtr FsManagerAvbHandle::Open(const std::string& device_file_by_name_prefix) {
if (device_file_by_name_prefix.empty()) {
LERROR << "Missing device file by-name prefix";
return nullptr;
}
fs_mgr_avb_ops = fs_mgr_dummy_avb_ops_new(fstab);
if (fs_mgr_avb_ops == nullptr) {
LERROR << "Failed to allocate dummy avb_ops";
return FS_MGR_SETUP_AVB_FAIL;
// Gets the expected hash value of vbmeta images from kernel cmdline.
std::unique_ptr<FsManagerAvbVerifier> avb_verifier = FsManagerAvbVerifier::Create();
if (!avb_verifier) {
LERROR << "Failed to create FsManagerAvbVerifier";
return nullptr;
}
// Invokes avb_slot_verify() to load and verify all vbmeta images.
// Sets requested_partitions to nullptr as it's to copy the contents
// of HASH partitions into fs_mgr_avb_verify_data, which is not required as
// fs_mgr only deals with HASHTREE partitions.
const char* requested_partitions[] = {nullptr};
std::string ab_suffix = fs_mgr_get_slot_suffix();
FsManagerAvbUniquePtr avb_handle(new FsManagerAvbHandle());
if (!avb_handle) {
LERROR << "Failed to allocate FsManagerAvbHandle";
return nullptr;
}
AvbSlotVerifyResult verify_result =
avb_slot_verify(fs_mgr_avb_ops, requested_partitions, ab_suffix.c_str(),
fs_mgr_vbmeta_prop.allow_verification_error, &fs_mgr_avb_verify_data);
FsManagerAvbOps avb_ops(device_file_by_name_prefix);
AvbSlotVerifyResult verify_result = avb_ops.AvbSlotVerify(
fs_mgr_get_slot_suffix(), avb_verifier->IsDeviceUnlocked(), &avb_handle->avb_slot_data_);
// Only allow two verify results:
// - AVB_SLOT_VERIFY_RESULT_OK.
// - AVB_SLOT_VERIFY_RESULT_ERROR_VERIFICATION (for UNLOCKED state).
if (verify_result == AVB_SLOT_VERIFY_RESULT_ERROR_VERIFICATION) {
if (!fs_mgr_vbmeta_prop.allow_verification_error) {
if (!avb_verifier->IsDeviceUnlocked()) {
LERROR << "ERROR_VERIFICATION isn't allowed";
goto fail;
return nullptr;
}
} else if (verify_result != AVB_SLOT_VERIFY_RESULT_OK) {
LERROR << "avb_slot_verify failed, result: " << verify_result;
goto fail;
return nullptr;
}
// Verifies vbmeta images against the digest passed from bootloader.
if (!verify_vbmeta_images(*fs_mgr_avb_verify_data, fs_mgr_vbmeta_prop)) {
LERROR << "verify_vbmeta_images failed";
goto fail;
if (!avb_verifier->VerifyVbmetaImages(*avb_handle->avb_slot_data_)) {
LERROR << "VerifyVbmetaImages failed";
return nullptr;
} else {
// Checks whether FLAGS_HASHTREE_DISABLED is set.
AvbVBMetaImageHeader vbmeta_header;
avb_vbmeta_image_header_to_host_byte_order(
(AvbVBMetaImageHeader*)fs_mgr_avb_verify_data->vbmeta_images[0].vbmeta_data,
(AvbVBMetaImageHeader*)avb_handle->avb_slot_data_->vbmeta_images[0].vbmeta_data,
&vbmeta_header);
bool hashtree_disabled =
((AvbVBMetaImageFlags)vbmeta_header.flags & AVB_VBMETA_IMAGE_FLAGS_HASHTREE_DISABLED);
if (hashtree_disabled) {
return FS_MGR_SETUP_AVB_HASHTREE_DISABLED;
avb_handle->status_ = kFsManagerAvbHandleHashtreeDisabled;
return avb_handle;
}
}
if (verify_result == AVB_SLOT_VERIFY_RESULT_OK) {
return FS_MGR_SETUP_AVB_SUCCESS;
avb_handle->status_ = kFsManagerAvbHandleSuccess;
return avb_handle;
}
fail:
fs_mgr_unload_vbmeta_images();
return FS_MGR_SETUP_AVB_FAIL;
return nullptr;
}
void fs_mgr_unload_vbmeta_images() {
if (fs_mgr_avb_verify_data != nullptr) {
avb_slot_verify_data_free(fs_mgr_avb_verify_data);
bool FsManagerAvbHandle::SetUpAvb(struct fstab_rec* fstab_entry) {
if (!fstab_entry) return false;
if (!avb_slot_data_ || avb_slot_data_->num_vbmeta_images < 1) {
return false;
}
if (fs_mgr_avb_ops != nullptr) {
fs_mgr_dummy_avb_ops_free(fs_mgr_avb_ops);
}
}
int fs_mgr_setup_avb(struct fstab_rec* fstab_entry) {
if (!fstab_entry || !fs_mgr_avb_verify_data || fs_mgr_avb_verify_data->num_vbmeta_images < 1) {
return FS_MGR_SETUP_AVB_FAIL;
if (status_ == kFsManagerAvbHandleHashtreeDisabled) {
LINFO << "AVB HASHTREE disabled on:" << fstab_entry->mount_point;
return true;
}
if (status_ != kFsManagerAvbHandleSuccess) return false;
std::string partition_name(basename(fstab_entry->mount_point));
if (!avb_validate_utf8((const uint8_t*)partition_name.c_str(), partition_name.length())) {
LERROR << "Partition name: " << partition_name.c_str() << " is not valid UTF-8.";
return FS_MGR_SETUP_AVB_FAIL;
return false;
}
AvbHashtreeDescriptor hashtree_descriptor;
std::string salt;
std::string root_digest;
if (!get_hashtree_descriptor(partition_name, *fs_mgr_avb_verify_data, &hashtree_descriptor,
&salt, &root_digest)) {
return FS_MGR_SETUP_AVB_FAIL;
if (!get_hashtree_descriptor(partition_name, *avb_slot_data_, &hashtree_descriptor, &salt,
&root_digest)) {
return false;
}
// Converts HASHTREE descriptor to verity_table_params.
if (!hashtree_dm_verity_setup(fstab_entry, hashtree_descriptor, salt, root_digest)) {
return FS_MGR_SETUP_AVB_FAIL;
return false;
}
return FS_MGR_SETUP_AVB_SUCCESS;
return true;
}

View file

@ -39,91 +39,10 @@
#include "fs_mgr_avb_ops.h"
#include "fs_mgr_priv.h"
static std::string fstab_by_name_prefix;
static std::string extract_by_name_prefix(struct fstab* fstab) {
// In AVB, we can assume that there's an entry for the /misc mount
// point in the fstab file and use that to get the device file for
// the misc partition. The device needs not to have an actual /misc
// partition. Then returns the prefix by removing the trailing "misc":
//
// - /dev/block/platform/soc.0/7824900.sdhci/by-name/misc ->
// - /dev/block/platform/soc.0/7824900.sdhci/by-name/
struct fstab_rec* fstab_entry = fs_mgr_get_entry_for_mount_point(fstab, "/misc");
if (fstab_entry == nullptr) {
LERROR << "/misc mount point not found in fstab";
return "";
}
std::string full_path(fstab_entry->blk_device);
size_t end_slash = full_path.find_last_of("/");
return full_path.substr(0, end_slash + 1);
}
static AvbIOResult read_from_partition(AvbOps* ops ATTRIBUTE_UNUSED, const char* partition,
int64_t offset, size_t num_bytes, void* buffer,
size_t* out_num_read) {
// The input |partition| name is with ab_suffix, e.g. system_a.
// Slot suffix (e.g. _a) will be appended to the device file path
// for partitions having 'slotselect' optin in fstab file, but it
// won't be appended to the mount point.
//
// Appends |partition| to the fstab_by_name_prefix, which is obtained
// by removing the trailing "misc" from the device file of /misc mount
// point. e.g.,
//
// - /dev/block/platform/soc.0/7824900.sdhci/by-name/ ->
// - /dev/block/platform/soc.0/7824900.sdhci/by-name/system_a
std::string path = fstab_by_name_prefix + partition;
// Ensures the device path (a symlink created by init) is ready to
// access. fs_mgr_test_access() will test a few iterations if the
// path doesn't exist yet.
if (fs_mgr_test_access(path.c_str()) < 0) {
return AVB_IO_RESULT_ERROR_NO_SUCH_PARTITION;
}
android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(path.c_str(), O_RDONLY | O_CLOEXEC)));
if (fd < 0) {
PERROR << "Failed to open " << path.c_str();
return AVB_IO_RESULT_ERROR_IO;
}
// If offset is negative, interprets its absolute value as the
// number of bytes from the end of the partition.
if (offset < 0) {
off64_t total_size = lseek64(fd, 0, SEEK_END);
if (total_size == -1) {
LERROR << "Failed to lseek64 to end of the partition";
return AVB_IO_RESULT_ERROR_IO;
}
offset = total_size + offset;
// Repositions the offset to the beginning.
if (lseek64(fd, 0, SEEK_SET) == -1) {
LERROR << "Failed to lseek64 to the beginning of the partition";
return AVB_IO_RESULT_ERROR_IO;
}
}
// On Linux, we never get partial reads from block devices (except
// for EOF).
ssize_t num_read = TEMP_FAILURE_RETRY(pread64(fd, buffer, num_bytes, offset));
if (num_read < 0 || (size_t)num_read != num_bytes) {
PERROR << "Failed to read " << num_bytes << " bytes from " << path.c_str() << " offset "
<< offset;
return AVB_IO_RESULT_ERROR_IO;
}
if (out_num_read != nullptr) {
*out_num_read = num_read;
}
return AVB_IO_RESULT_OK;
static AvbIOResult read_from_partition(AvbOps* ops, const char* partition, int64_t offset,
size_t num_bytes, void* buffer, size_t* out_num_read) {
return FsManagerAvbOps::GetInstanceFromAvbOps(ops)->ReadFromPartition(
partition, offset, num_bytes, buffer, out_num_read);
}
static AvbIOResult dummy_read_rollback_index(AvbOps* ops ATTRIBUTE_UNUSED,
@ -145,7 +64,6 @@ static AvbIOResult dummy_validate_vbmeta_public_key(
// Addtionally, user-space should check
// androidboot.vbmeta.{hash_alg, size, digest} against the digest
// of all vbmeta images after invoking avb_slot_verify().
*out_is_trusted = true;
return AVB_IO_RESULT_OK;
}
@ -170,28 +88,86 @@ static AvbIOResult dummy_get_unique_guid_for_partition(AvbOps* ops ATTRIBUTE_UNU
return AVB_IO_RESULT_OK;
}
AvbOps* fs_mgr_dummy_avb_ops_new(struct fstab* fstab) {
AvbOps* ops;
fstab_by_name_prefix = extract_by_name_prefix(fstab);
if (fstab_by_name_prefix.empty()) return nullptr;
ops = (AvbOps*)calloc(1, sizeof(AvbOps));
if (ops == nullptr) {
LERROR << "Error allocating memory for AvbOps";
return nullptr;
FsManagerAvbOps::FsManagerAvbOps(const std::string& device_file_by_name_prefix)
: device_file_by_name_prefix_(device_file_by_name_prefix) {
if (device_file_by_name_prefix_.back() != '/') {
device_file_by_name_prefix_ += '/';
}
// We only need to provide the implementation of read_from_partition()
// operation since that's all what is being used by the avb_slot_verify().
// Other I/O operations are only required in bootloader but not in
// user-space so we set them as dummy operations.
avb_ops_.read_from_partition = read_from_partition;
avb_ops_.read_rollback_index = dummy_read_rollback_index;
avb_ops_.validate_vbmeta_public_key = dummy_validate_vbmeta_public_key;
avb_ops_.read_is_device_unlocked = dummy_read_is_device_unlocked;
avb_ops_.get_unique_guid_for_partition = dummy_get_unique_guid_for_partition;
// We only need these operations since that's all what is being used
// by the avb_slot_verify(); Most of them are dummy operations because
// they're only required in bootloader but not required in user-space.
ops->read_from_partition = read_from_partition;
ops->read_rollback_index = dummy_read_rollback_index;
ops->validate_vbmeta_public_key = dummy_validate_vbmeta_public_key;
ops->read_is_device_unlocked = dummy_read_is_device_unlocked;
ops->get_unique_guid_for_partition = dummy_get_unique_guid_for_partition;
return ops;
// Sets user_data for GetInstanceFromAvbOps() to convert it back to FsManagerAvbOps.
avb_ops_.user_data = this;
}
void fs_mgr_dummy_avb_ops_free(AvbOps* ops) { free(ops); }
AvbIOResult FsManagerAvbOps::ReadFromPartition(const char* partition, int64_t offset,
size_t num_bytes, void* buffer,
size_t* out_num_read) {
// Appends |partition| to the device_file_by_name_prefix_, e.g.,
// - /dev/block/platform/soc.0/7824900.sdhci/by-name/ ->
// - /dev/block/platform/soc.0/7824900.sdhci/by-name/system_a
std::string path = device_file_by_name_prefix_ + partition;
// Ensures the device path (a symlink created by init) is ready to
// access. fs_mgr_test_access() will test a few iterations if the
// path doesn't exist yet.
if (fs_mgr_test_access(path.c_str()) < 0) {
return AVB_IO_RESULT_ERROR_NO_SUCH_PARTITION;
}
android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(path.c_str(), O_RDONLY | O_CLOEXEC)));
if (fd < 0) {
PERROR << "Failed to open " << path.c_str();
return AVB_IO_RESULT_ERROR_IO;
}
// If offset is negative, interprets its absolute value as the
// number of bytes from the end of the partition.
if (offset < 0) {
off64_t total_size = lseek64(fd, 0, SEEK_END);
if (total_size == -1) {
LERROR << "Failed to lseek64 to end of the partition";
return AVB_IO_RESULT_ERROR_IO;
}
offset = total_size + offset;
// Repositions the offset to the beginning.
if (lseek64(fd, 0, SEEK_SET) == -1) {
LERROR << "Failed to lseek64 to the beginning of the partition";
return AVB_IO_RESULT_ERROR_IO;
}
}
// On Linux, we never get partial reads from block devices (except
// for EOF).
ssize_t num_read = TEMP_FAILURE_RETRY(pread64(fd, buffer, num_bytes, offset));
if (num_read < 0 || (size_t)num_read != num_bytes) {
PERROR << "Failed to read " << num_bytes << " bytes from " << path.c_str() << " offset "
<< offset;
return AVB_IO_RESULT_ERROR_IO;
}
if (out_num_read != nullptr) {
*out_num_read = num_read;
}
return AVB_IO_RESULT_OK;
}
AvbSlotVerifyResult FsManagerAvbOps::AvbSlotVerify(const std::string& ab_suffix,
bool allow_verification_error,
AvbSlotVerifyData** out_data) {
// Invokes avb_slot_verify() to load and verify all vbmeta images.
// Sets requested_partitions to nullptr as it's to copy the contents
// of HASH partitions into handle>avb_slot_data_, which is not required as
// fs_mgr only deals with HASHTREE partitions.
const char* requested_partitions[] = {nullptr};
return avb_slot_verify(&avb_ops_, requested_partitions, ab_suffix.c_str(),
allow_verification_error, out_data);
}

View file

@ -29,31 +29,34 @@
#include "fs_mgr.h"
__BEGIN_DECLS
// This class provides C++ bindings to interact with libavb, a small
// self-contained piece of code that's intended to be used in bootloaders.
// It mainly contains two functions:
// - ReadFromPartition(): to read AVB metadata from a given partition.
// It provides the implementation of AvbOps.read_from_partition() when
// reading metadata through libavb.
// - AvbSlotVerify(): the C++ binding of libavb->avb_slot_verify() to
// read and verify the metadata and store it into the out_data parameter.
// The caller MUST check the integrity of metadata against the
// androidboot.vbmeta.{hash_alg, size, digest} values from /proc/cmdline.
// e.g., see class FsManagerAvbVerifier for more details.
//
class FsManagerAvbOps {
public:
FsManagerAvbOps(const std::string& device_file_by_name_prefix);
/* Allocates a "dummy" AvbOps instance solely for use in user-space.
* Returns nullptr on OOM.
*
* It mainly provides read_from_partitions() for user-space to get
* AvbSlotVerifyData.vbmeta_images[] and the caller MUST check their
* integrity against the androidboot.vbmeta.{hash_alg, size, digest}
* values from /proc/cmdline, e.g. verify_vbmeta_images()
* in fs_mgr_avb.cpp.
*
* Other I/O operations are only required in boot loader so we set
* them as dummy operations here.
* - Will allow any public key for signing.
* - returns 0 for any rollback index location.
* - returns device is unlocked regardless of the actual state.
* - returns a dummy guid for any partition.
*
* Frees with fs_mgr_dummy_avb_ops_free().
*/
AvbOps* fs_mgr_dummy_avb_ops_new(struct fstab* fstab);
static FsManagerAvbOps* GetInstanceFromAvbOps(AvbOps* ops) {
return reinterpret_cast<FsManagerAvbOps*>(ops->user_data);
}
/* Frees an AvbOps instance previously allocated with fs_mgr_avb_ops_new(). */
void fs_mgr_dummy_avb_ops_free(AvbOps* ops);
AvbIOResult ReadFromPartition(const char* partition, int64_t offset, size_t num_bytes,
void* buffer, size_t* out_num_read);
__END_DECLS
AvbSlotVerifyResult AvbSlotVerify(const std::string& ab_suffix, bool allow_verification_error,
AvbSlotVerifyData** out_data);
private:
AvbOps avb_ops_;
std::string device_file_by_name_prefix_;
};
#endif /* __CORE_FS_MGR_AVB_OPS_H */

View file

@ -17,40 +17,77 @@
#ifndef __CORE_FS_MGR_PRIV_AVB_H
#define __CORE_FS_MGR_PRIV_AVB_H
#ifndef __cplusplus
#include <stdbool.h>
#endif
#include <memory>
#include <string>
#include <libavb/libavb.h>
#include "fs_mgr.h"
__BEGIN_DECLS
enum FsManagerAvbHandleStatus {
kFsManagerAvbHandleSuccess = 0,
kFsManagerAvbHandleHashtreeDisabled = 1,
kFsManagerAvbHandleFail = 2,
};
#define FS_MGR_SETUP_AVB_HASHTREE_DISABLED (-2)
#define FS_MGR_SETUP_AVB_FAIL (-1)
#define FS_MGR_SETUP_AVB_SUCCESS 0
class FsManagerAvbHandle;
using FsManagerAvbUniquePtr = std::unique_ptr<FsManagerAvbHandle>;
bool fs_mgr_is_avb_used();
// Provides a factory method to return a unique_ptr pointing to itself and the
// SetUpAvb() function to extract dm-verity parameters from AVB metadata to
// load verity table into kernel through ioctl.
class FsManagerAvbHandle {
public:
// The factory method to return a FsManagerAvbUniquePtr that holds
// the verified AVB (external/avb) metadata of all verified partitions
// in avb_slot_data_.vbmeta_images[].
//
// The metadata is checked against the following values from /proc/cmdline.
// - androidboot.vbmeta.{hash_alg, size, digest}.
//
// A typical usage will be:
// - FsManagerAvbUniquePtr handle = FsManagerAvbHandle::Open();
//
// Possible return values:
// - nullptr: any error when reading and verifying the metadata,
// e.g., I/O error, digest value mismatch, size mismatch, etc.
//
// - a valid unique_ptr with status kFsMgrAvbHandleHashtreeDisabled:
// to support the existing 'adb disable-verity' feature in Android.
// It's very helpful for developers to make the filesystem writable to
// allow replacing binaries on the device.
//
// - a valid unique_ptr with status kFsMgrAvbHandleSuccess: the metadata
// is verified and can be trusted.
//
static FsManagerAvbUniquePtr Open(const std::string& device_file_by_name_prefix);
/* Gets AVB metadata through external/avb/libavb for all partitions:
* AvbSlotVerifyData.vbmeta_images[] and checks their integrity
* against the androidboot.vbmeta.{hash_alg, size, digest} values
* from /proc/cmdline.
*
* Return values:
* - FS_MGR_SETUP_AVB_SUCCESS: the metadata cab be trusted.
* - FS_MGR_SETUP_AVB_FAIL: any error when reading and verifying the
* metadata, e.g. I/O error, digest value mismatch, size mismatch.
* - FS_MGR_SETUP_AVB_HASHTREE_DISABLED: to support the existing
* 'adb disable-verity' feature in Android. It's very helpful for
* developers to make the filesystem writable to allow replacing
* binaries on the device.
*/
int fs_mgr_load_vbmeta_images(struct fstab* fstab);
// Sets up dm-verity on the given fstab entry.
// Returns true if the mount point is eligible to mount, it includes:
// - status_ is kFsMgrAvbHandleHashtreeDisabled or
// - status_ is kFsMgrAvbHandleSuccess and sending ioctl DM_TABLE_LOAD
// to load verity table is success.
// Otherwise, returns false.
bool SetUpAvb(fstab_rec* fstab_entry);
void fs_mgr_unload_vbmeta_images();
FsManagerAvbHandle(const FsManagerAvbHandle&) = delete; // no copy
FsManagerAvbHandle& operator=(const FsManagerAvbHandle&) = delete; // no assignment
int fs_mgr_setup_avb(struct fstab_rec* fstab_entry);
FsManagerAvbHandle(FsManagerAvbHandle&&) noexcept = delete; // no move
FsManagerAvbHandle& operator=(FsManagerAvbHandle&&) noexcept = delete; // no move assignment
__END_DECLS
~FsManagerAvbHandle() {
if (avb_slot_data_) {
avb_slot_verify_data_free(avb_slot_data_);
}
};
protected:
FsManagerAvbHandle() : avb_slot_data_(nullptr), status_(kFsManagerAvbHandleFail) {}
private:
AvbSlotVerifyData* avb_slot_data_;
FsManagerAvbHandleStatus status_;
};
#endif /* __CORE_FS_MGR_PRIV_AVB_H */

View file

@ -20,16 +20,18 @@
#include <openssl/sha.h>
class SHA256Hasher {
private:
private:
SHA256_CTX sha256_ctx;
uint8_t hash[SHA256_DIGEST_LENGTH];
public:
public:
enum { DIGEST_SIZE = SHA256_DIGEST_LENGTH };
SHA256Hasher() { SHA256_Init(&sha256_ctx); }
void update(const void* data, size_t data_size) { SHA256_Update(&sha256_ctx, data, data_size); }
void update(const uint8_t* data, size_t data_size) {
SHA256_Update(&sha256_ctx, data, data_size);
}
const uint8_t* finalize() {
SHA256_Final(hash, &sha256_ctx);
@ -38,11 +40,11 @@ class SHA256Hasher {
};
class SHA512Hasher {
private:
private:
SHA512_CTX sha512_ctx;
uint8_t hash[SHA512_DIGEST_LENGTH];
public:
public:
enum { DIGEST_SIZE = SHA512_DIGEST_LENGTH };
SHA512Hasher() { SHA512_Init(&sha512_ctx); }