From 53bed1c153304aa7af235dd69877231a5882e0ed Mon Sep 17 00:00:00 2001 From: Yo Chiang Date: Wed, 1 Jan 2020 16:25:19 +0800 Subject: Add getAvbPublicKey to IGsiService and IImageService Retrieves AVB public key from an image's VBMeta block. Bug: 146418878 Test: adb shell am start-activity \ -n com.android.dynsystem/com.android.dynsystem.VerificationActivity \ -a android.os.image.action.START_INSTALL \ --el KEY_USERDATA_SIZE 8589934592 \ -d file:///storage/emulated/0/Download/aosp_arm64-dsu_test.zip Test: adb shell gsi_tool status \ // command should show sha1 of public key for each partition Change-Id: Ic529275a2678e6c1ce341910e4dbbe9af66742a7 --- Android.bp | 4 +- aidl/android/gsi/AvbPublicKey.aidl | 25 +++++++++ aidl/android/gsi/IGsiService.aidl | 17 ++++++ aidl/android/gsi/IImageService.aidl | 18 ++++++ gsi_service.cpp | 108 ++++++++++++++++++++++++++++++++++++ gsi_service.h | 1 + gsi_tool.cpp | 14 +++++ partition_installer.cpp | 4 ++ partition_installer.h | 1 + 9 files changed, 191 insertions(+), 1 deletion(-) create mode 100644 aidl/android/gsi/AvbPublicKey.aidl diff --git a/Android.bp b/Android.bp index e46c16a..1667ffb 100644 --- a/Android.bp +++ b/Android.bp @@ -95,6 +95,7 @@ cc_binary { ], static_libs: [ "gsi_aidl_interface-cpp", + "libavb", "libcutils", "libdm", "libext4_utils", @@ -122,10 +123,11 @@ aidl_interface { filegroup { name: "gsiservice_aidl", srcs: [ + "aidl/android/gsi/AvbPublicKey.aidl", "aidl/android/gsi/GsiProgress.aidl", - "aidl/android/gsi/IImageService.aidl", "aidl/android/gsi/IGsid.aidl", "aidl/android/gsi/IGsiService.aidl", + "aidl/android/gsi/IImageService.aidl", "aidl/android/gsi/IProgressCallback.aidl", "aidl/android/gsi/MappedImage.aidl", ], diff --git a/aidl/android/gsi/AvbPublicKey.aidl b/aidl/android/gsi/AvbPublicKey.aidl new file mode 100644 index 0000000..519ec80 --- /dev/null +++ b/aidl/android/gsi/AvbPublicKey.aidl @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.gsi; + +/** {@hide} */ +parcelable AvbPublicKey { + /* Raw data bytes. */ + byte[] bytes; + /* SHA-1 digest of the key. */ + byte[] sha1; +} diff --git a/aidl/android/gsi/IGsiService.aidl b/aidl/android/gsi/IGsiService.aidl index cd48c63..65050fe 100644 --- a/aidl/android/gsi/IGsiService.aidl +++ b/aidl/android/gsi/IGsiService.aidl @@ -16,6 +16,7 @@ package android.gsi; +import android.gsi.AvbPublicKey; import android.gsi.GsiProgress; import android.gsi.IImageService; import android.os.ParcelFileDescriptor; @@ -188,4 +189,20 @@ interface IGsiService { * for dumpstate. */ @utf8InCpp String dumpDeviceMapperDevices(); + + /** + * Retrieve AVB public key from the current mapped partition. + * This works only while partition device is mapped and the end-of-partition + * AVB footer has been written. + * A call to createPartition() does the following things: + * 1. Close the previous partition installer, thus unmap the partition. + * 2. Open a new partition installer. + * 3. Create and map the new partition. + * + * In other words, getAvbPublicKey() works between two createPartition() calls. + * + * @param dst Output the AVB public key. + * @return 0 on success, an error code on failure. + */ + int getAvbPublicKey(out AvbPublicKey dst); } diff --git a/aidl/android/gsi/IImageService.aidl b/aidl/android/gsi/IImageService.aidl index 5d9002f..c8c5a9d 100644 --- a/aidl/android/gsi/IImageService.aidl +++ b/aidl/android/gsi/IImageService.aidl @@ -16,6 +16,7 @@ package android.gsi; +import android.gsi.AvbPublicKey; import android.gsi.MappedImage; import android.gsi.IProgressCallback; @@ -26,6 +27,11 @@ interface IImageService { const int CREATE_IMAGE_READONLY = 0x1; const int CREATE_IMAGE_ZERO_FILL = 0x2; + /* Successfully returned */ + const int IMAGE_OK = 0; + /* Generic error code */ + const int IMAGE_ERROR = 1; + /** * Create an image that can be mapped as a block device. * @@ -86,6 +92,18 @@ interface IImageService { */ boolean isImageMapped(@utf8InCpp String name); + /** + * Retrieve AVB public key from an image. + * If the image is already mapped then it works the same as + * IGsiService::getAvbPublicKey(). Otherwise this will attempt to + * map / unmap the partition image upon enter / return. + * + * @param name Image name as passed to createBackingImage(). + * @param dst Output of the AVB public key. + * @return 0 on success, an error code on failure. + */ + int getAvbPublicKey(@utf8InCpp String name, out AvbPublicKey dst); + /** * Get all installed backing image names * diff --git a/gsi_service.cpp b/gsi_service.cpp index 9b74502..d38078e 100644 --- a/gsi_service.cpp +++ b/gsi_service.cpp @@ -25,6 +25,7 @@ #include #include +#include #include #include #include @@ -38,8 +39,10 @@ #include #include #include +#include #include #include +#include #include #include "file_paths.h" @@ -52,6 +55,7 @@ using namespace std::literals; using namespace android::fs_mgr; using namespace android::fiemap; using android::base::ReadFileToString; +using android::base::ReadFullyAtOffset; using android::base::RemoveFileIfExists; using android::base::StringPrintf; using android::base::unique_fd; @@ -66,6 +70,8 @@ android::wp GsiService::sInstance; // Default userdata image size. static constexpr int64_t kDefaultUserdataSize = int64_t(2) * 1024 * 1024 * 1024; +static bool GetAvbPublicKeyFromFd(int fd, AvbPublicKey* dst); + void Gsid::Register() { auto ret = android::BinderService::publish(); if (ret != android::OK) { @@ -440,6 +446,24 @@ binder::Status GsiService::dumpDeviceMapperDevices(std::string* _aidl_return) { return binder::Status::ok(); } +binder::Status GsiService::getAvbPublicKey(AvbPublicKey* dst, int32_t* _aidl_return) { + ENFORCE_SYSTEM; + std::lock_guard guard(parent_->lock()); + + if (!installer_) { + *_aidl_return = INSTALL_ERROR_GENERIC; + return binder::Status::ok(); + } + int fd = installer_->GetPartitionFd(); + if (!GetAvbPublicKeyFromFd(fd, dst)) { + LOG(ERROR) << "Failed to extract AVB public key"; + *_aidl_return = INSTALL_ERROR_GENERIC; + return binder::Status::ok(); + } + *_aidl_return = INSTALL_OK; + return binder::Status::ok(); +} + bool GsiService::CreateInstallStatusFile() { if (!android::base::WriteStringToFile("0", kDsuInstallStatusFile)) { PLOG(ERROR) << "write " << kDsuInstallStatusFile; @@ -482,6 +506,8 @@ class ImageService : public BinderService, public BnImageService { binder::Status unmapImageDevice(const std::string& name) override; binder::Status backingImageExists(const std::string& name, bool* _aidl_return) override; binder::Status isImageMapped(const std::string& name, bool* _aidl_return) override; + binder::Status getAvbPublicKey(const std::string& name, AvbPublicKey* dst, + int32_t* _aidl_return) override; binder::Status zeroFillNewImage(const std::string& name, int64_t bytes) override; binder::Status removeAllImages() override; binder::Status removeDisabledImages() override; @@ -582,6 +608,46 @@ binder::Status ImageService::isImageMapped(const std::string& name, bool* _aidl_ return binder::Status::ok(); } +binder::Status ImageService::getAvbPublicKey(const std::string& name, AvbPublicKey* dst, + int32_t* _aidl_return) { + if (!CheckUid()) return UidSecurityError(); + + std::lock_guard guard(parent_->lock()); + + std::string device_path; + std::unique_ptr mapped_device; + if (!impl_->IsImageMapped(name)) { + mapped_device = MappedDevice::Open(impl_.get(), 10s, name); + if (!mapped_device) { + PLOG(ERROR) << "Fail to map image: " << name; + *_aidl_return = IMAGE_ERROR; + return binder::Status::ok(); + } + device_path = mapped_device->path(); + } else { + if (!impl_->GetMappedImageDevice(name, &device_path)) { + PLOG(ERROR) << "GetMappedImageDevice() failed"; + *_aidl_return = IMAGE_ERROR; + return binder::Status::ok(); + } + } + android::base::unique_fd fd(open(device_path.c_str(), O_RDONLY | O_CLOEXEC)); + if (!fd.ok()) { + PLOG(ERROR) << "Fail to open mapped device: " << device_path; + *_aidl_return = IMAGE_ERROR; + return binder::Status::ok(); + } + bool ok = GetAvbPublicKeyFromFd(fd.get(), dst); + fd = {}; + if (!ok) { + LOG(ERROR) << "Failed to extract AVB public key"; + *_aidl_return = IMAGE_ERROR; + return binder::Status::ok(); + } + *_aidl_return = IMAGE_OK; + return binder::Status::ok(); +} + binder::Status ImageService::zeroFillNewImage(const std::string& name, int64_t bytes) { if (!CheckUid()) return UidSecurityError(); @@ -934,5 +1000,47 @@ void GsiService::RunStartupTasks() { } } +static bool GetAvbPublicKeyFromFd(int fd, AvbPublicKey* dst) { + // Read the AVB footer from EOF. + int64_t total_size = get_block_device_size(fd); + int64_t footer_offset = total_size - AVB_FOOTER_SIZE; + std::array footer_bytes; + if (!ReadFullyAtOffset(fd, footer_bytes.data(), AVB_FOOTER_SIZE, footer_offset)) { + PLOG(ERROR) << "cannot read AVB footer"; + return false; + } + // Validate the AVB footer data and byte swap to native byte order. + AvbFooter footer; + if (!avb_footer_validate_and_byteswap((const AvbFooter*)footer_bytes.data(), &footer)) { + LOG(ERROR) << "invalid AVB footer"; + return false; + } + // Read the VBMeta image. + std::vector vbmeta_bytes(footer.vbmeta_size); + if (!ReadFullyAtOffset(fd, vbmeta_bytes.data(), vbmeta_bytes.size(), footer.vbmeta_offset)) { + PLOG(ERROR) << "cannot read VBMeta image"; + return false; + } + // Validate the VBMeta image and retrieve AVB public key. + // After a successful call to avb_vbmeta_image_verify(), public_key_data + // will point to the serialized AVB public key, in the same format generated + // by the `avbtool extract_public_key` command. + const uint8_t* public_key_data; + size_t public_key_size; + AvbVBMetaVerifyResult result = avb_vbmeta_image_verify(vbmeta_bytes.data(), vbmeta_bytes.size(), + &public_key_data, &public_key_size); + if (result != AVB_VBMETA_VERIFY_RESULT_OK) { + LOG(ERROR) << "invalid VBMeta image: " << avb_vbmeta_verify_result_to_string(result); + return false; + } + if (public_key_data != nullptr) { + dst->bytes.resize(public_key_size); + memcpy(dst->bytes.data(), public_key_data, public_key_size); + dst->sha1.resize(SHA_DIGEST_LENGTH); + SHA1(public_key_data, public_key_size, dst->sha1.data()); + } + return true; +} + } // namespace gsi } // namespace android diff --git a/gsi_service.h b/gsi_service.h index b828899..a853e2b 100644 --- a/gsi_service.h +++ b/gsi_service.h @@ -82,6 +82,7 @@ class GsiService : public BinderService, public BnGsiService { binder::Status openImageService(const std::string& prefix, android::sp* _aidl_return) override; binder::Status dumpDeviceMapperDevices(std::string* _aidl_return) override; + binder::Status getAvbPublicKey(AvbPublicKey* dst, int32_t* _aidl_return) override; // This is in GsiService, rather than GsiInstaller, since we need to access // it outside of the main lock which protects the unique_ptr. diff --git a/gsi_tool.cpp b/gsi_tool.cpp index 9abb1ff..a38bf20 100644 --- a/gsi_tool.cpp +++ b/gsi_tool.cpp @@ -31,6 +31,7 @@ #include #include #include +#include #include #include #include @@ -45,6 +46,7 @@ using namespace std::chrono_literals; using android::sp; using android::base::Split; +using android::base::StringPrintf; using CommandCallback = std::function, int, char**)>; static int Disable(sp gsid, int argc, char** argv); @@ -438,6 +440,18 @@ static int Status(sp gsid, int argc, char** /* argv */) { } for (auto&& image : images) { std::cout << "installed: " << image << std::endl; + AvbPublicKey public_key; + int err = 0; + status = image_service->getAvbPublicKey(image, &public_key, &err); + std::cout << "AVB public key (sha1): "; + if (!public_key.bytes.empty()) { + for (auto b : public_key.sha1) { + std::cout << StringPrintf("%02x", b & 255); + } + std::cout << std::endl; + } else { + std::cout << "[NONE]" << std::endl; + } } } return 0; diff --git a/partition_installer.cpp b/partition_installer.cpp index ff2cc59..2ac313f 100644 --- a/partition_installer.cpp +++ b/partition_installer.cpp @@ -257,6 +257,10 @@ bool PartitionInstaller::CommitGsiChunk(const void* data, size_t bytes) { return true; } +int PartitionInstaller::GetPartitionFd() { + return system_device_->fd(); +} + bool PartitionInstaller::MapAshmem(int fd, size_t size) { ashmem_size_ = size; ashmem_data_ = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); diff --git a/partition_installer.h b/partition_installer.h index 830c034..1503648 100644 --- a/partition_installer.h +++ b/partition_installer.h @@ -48,6 +48,7 @@ class PartitionInstaller final { bool CommitGsiChunk(const void* data, size_t bytes); bool MapAshmem(int fd, size_t size); bool CommitGsiChunk(size_t bytes); + int GetPartitionFd(); static int WipeWritable(const std::string& active_dsu, const std::string& install_dir, const std::string& name); -- cgit v1.2.3