From d10d06d0b01151d6fcdf7ac70b5382e08858e233 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A5rten=20Kongstad?= Date: Mon, 7 Jan 2019 17:26:25 -0800 Subject: [PATCH] Add enforcement of idmap policies Teaches idmap2 to recognize policy restrictions put on overlayable resources. If overlayable enforcement is turned on for an overlay, then any resources defined within the overlayable api of the target will have policy restrictions imposed on them. All resources without overlayable definitions will continue to be overlayable without policy restrictions. Bug: 119390857 Test: atest idmap2 and booting Co-authored-by: Ryan Mitchell Change-Id: I7e435648eb6e4a87b0b90a7b2a0c3f33c1516ea6 --- cmds/idmap2/Android.bp | 2 + cmds/idmap2/idmap2/Create.cpp | 31 ++++- cmds/idmap2/idmap2/Scan.cpp | 107 ++++++++++++--- cmds/idmap2/idmap2d/Idmap2Service.cpp | 22 ++- cmds/idmap2/idmap2d/Idmap2Service.h | 7 +- .../idmap2d/aidl/android/os/IIdmap2.aidl | 13 +- .../include/idmap2/CommandLineOptions.h | 3 + cmds/idmap2/include/idmap2/Idmap.h | 11 +- cmds/idmap2/include/idmap2/Policies.h | 41 ++++++ cmds/idmap2/libidmap2/CommandLineOptions.cpp | 23 +++- cmds/idmap2/libidmap2/Idmap.cpp | 31 ++++- cmds/idmap2/libidmap2/Policies.cpp | 57 ++++++++ .../idmap2/tests/BinaryStreamVisitorTests.cpp | 42 +++++- cmds/idmap2/tests/CommandLineOptionsTests.cpp | 56 +++++++- cmds/idmap2/tests/FileUtilsTests.cpp | 14 +- cmds/idmap2/tests/Idmap2BinaryTests.cpp | 32 ++++- cmds/idmap2/tests/IdmapTests.cpp | 129 +++++++++++++++++- cmds/idmap2/tests/PoliciesTests.cpp | 70 ++++++++++ cmds/idmap2/tests/PrettyPrintVisitorTests.cpp | 5 +- cmds/idmap2/tests/RawPrintVisitorTests.cpp | 5 +- .../AndroidManifest.xml | 21 +++ .../tests/data/system-overlay-invalid/build | 26 ++++ .../res/values/values.xml | 29 ++++ .../system-overlay-invalid.apk | Bin 0 -> 1427 bytes .../data/system-overlay/AndroidManifest.xml | 21 +++ cmds/idmap2/tests/data/system-overlay/build | 26 ++++ .../data/system-overlay/res/values/values.xml | 22 +++ .../data/system-overlay/system-overlay.apk | Bin 0 -> 1283 bytes .../data/target/res/values/overlayable.xml | 48 +++++++ .../tests/data/target/res/values/values.xml | 8 ++ cmds/idmap2/tests/data/target/target.apk | Bin 2173 -> 3669 bytes .../tests/data/overlayable/overlayable.apk | Bin 5523 -> 3443 bytes .../com/android/server/om/IdmapManager.java | 84 +++++++++++- 33 files changed, 912 insertions(+), 74 deletions(-) create mode 100644 cmds/idmap2/include/idmap2/Policies.h create mode 100644 cmds/idmap2/libidmap2/Policies.cpp create mode 100644 cmds/idmap2/tests/PoliciesTests.cpp create mode 100644 cmds/idmap2/tests/data/system-overlay-invalid/AndroidManifest.xml create mode 100644 cmds/idmap2/tests/data/system-overlay-invalid/build create mode 100644 cmds/idmap2/tests/data/system-overlay-invalid/res/values/values.xml create mode 100644 cmds/idmap2/tests/data/system-overlay-invalid/system-overlay-invalid.apk create mode 100644 cmds/idmap2/tests/data/system-overlay/AndroidManifest.xml create mode 100644 cmds/idmap2/tests/data/system-overlay/build create mode 100644 cmds/idmap2/tests/data/system-overlay/res/values/values.xml create mode 100644 cmds/idmap2/tests/data/system-overlay/system-overlay.apk create mode 100644 cmds/idmap2/tests/data/target/res/values/overlayable.xml diff --git a/cmds/idmap2/Android.bp b/cmds/idmap2/Android.bp index abe18ba8a4153..e9cfe63e0d03a 100644 --- a/cmds/idmap2/Android.bp +++ b/cmds/idmap2/Android.bp @@ -38,6 +38,7 @@ cc_library { "libidmap2/CommandLineOptions.cpp", "libidmap2/FileUtils.cpp", "libidmap2/Idmap.cpp", + "libidmap2/Policies.cpp", "libidmap2/PrettyPrintVisitor.cpp", "libidmap2/RawPrintVisitor.cpp", "libidmap2/ResourceUtils.cpp", @@ -87,6 +88,7 @@ cc_test { "tests/Idmap2BinaryTests.cpp", "tests/IdmapTests.cpp", "tests/Main.cpp", + "tests/PoliciesTests.cpp", "tests/PrettyPrintVisitorTests.cpp", "tests/RawPrintVisitorTests.cpp", "tests/ResourceUtilsTests.cpp", diff --git a/cmds/idmap2/idmap2/Create.cpp b/cmds/idmap2/idmap2/Create.cpp index b07567331e850..c455ac0f83af8 100644 --- a/cmds/idmap2/idmap2/Create.cpp +++ b/cmds/idmap2/idmap2/Create.cpp @@ -27,17 +27,25 @@ #include "idmap2/CommandLineOptions.h" #include "idmap2/FileUtils.h" #include "idmap2/Idmap.h" +#include "idmap2/Policies.h" +#include "idmap2/Result.h" using android::ApkAssets; using android::idmap2::BinaryStreamVisitor; using android::idmap2::CommandLineOptions; using android::idmap2::Idmap; +using android::idmap2::PoliciesToBitmask; +using android::idmap2::PolicyBitmask; +using android::idmap2::PolicyFlags; +using android::idmap2::Result; using android::idmap2::utils::kIdmapFilePermissionMask; bool Create(const std::vector& args, std::ostream& out_error) { std::string target_apk_path; std::string overlay_apk_path; std::string idmap_path; + std::vector policies; + bool ignore_overlayable; const CommandLineOptions opts = CommandLineOptions("idmap2 create") @@ -47,12 +55,28 @@ bool Create(const std::vector& args, std::ostream& out_error) { .MandatoryOption("--overlay-apk-path", "input: path to apk which contains the new resource values", &overlay_apk_path) - .MandatoryOption("--idmap-path", "output: path to where to write idmap file", - &idmap_path); + .MandatoryOption("--idmap-path", "output: path to where to write idmap file", &idmap_path) + .OptionalOption("--policy", + "input: an overlayable policy this overlay fulfills " + "(if none or supplied, the overlay policy will default to \"public\")", + &policies) + .OptionalFlag("--ignore-overlayable", "disables overlayable and policy checks", + &ignore_overlayable); if (!opts.Parse(args, out_error)) { return false; } + PolicyBitmask fulfilled_policies = 0; + if (auto result = PoliciesToBitmask(policies, out_error)) { + fulfilled_policies |= *result; + } else { + return false; + } + + if (fulfilled_policies == 0) { + fulfilled_policies |= PolicyFlags::POLICY_PUBLIC; + } + const std::unique_ptr target_apk = ApkAssets::Load(target_apk_path); if (!target_apk) { out_error << "error: failed to load apk " << target_apk_path << std::endl; @@ -66,7 +90,8 @@ bool Create(const std::vector& args, std::ostream& out_error) { } const std::unique_ptr idmap = - Idmap::FromApkAssets(target_apk_path, *target_apk, overlay_apk_path, *overlay_apk, out_error); + Idmap::FromApkAssets(target_apk_path, *target_apk, overlay_apk_path, *overlay_apk, + fulfilled_policies, !ignore_overlayable, out_error); if (!idmap) { return false; } diff --git a/cmds/idmap2/idmap2/Scan.cpp b/cmds/idmap2/idmap2/Scan.cpp index 4f88127f8af1c..a269ee9584972 100644 --- a/cmds/idmap2/idmap2/Scan.cpp +++ b/cmds/idmap2/idmap2/Scan.cpp @@ -21,8 +21,11 @@ #include #include #include +#include #include +#include "android-base/properties.h" + #include "idmap2/CommandLineOptions.h" #include "idmap2/FileUtils.h" #include "idmap2/Idmap.h" @@ -34,6 +37,10 @@ using android::idmap2::CommandLineOptions; using android::idmap2::Idmap; using android::idmap2::MemoryChunk; +using android::idmap2::PoliciesToBitmask; +using android::idmap2::PolicyBitmask; +using android::idmap2::PolicyFlags; +using android::idmap2::Result; using android::idmap2::Xml; using android::idmap2::ZipFile; using android::idmap2::utils::FindFiles; @@ -45,11 +52,19 @@ struct InputOverlay { return priority < rhs.priority || (priority == rhs.priority && apk_path < rhs.apk_path); } - std::string apk_path; // NOLINT(misc-non-private-member-variables-in-classes) - std::string idmap_path; // NOLINT(misc-non-private-member-variables-in-classes) - int priority; // NOLINT(misc-non-private-member-variables-in-classes) + std::string apk_path; // NOLINT(misc-non-private-member-variables-in-classes) + std::string idmap_path; // NOLINT(misc-non-private-member-variables-in-classes) + int priority; // NOLINT(misc-non-private-member-variables-in-classes) + std::vector policies; // NOLINT(misc-non-private-member-variables-in-classes) + bool ignore_overlayable; // NOLINT(misc-non-private-member-variables-in-classes) }; +bool VendorIsQOrLater() { + // STOPSHIP(b/119390857): Check api version once Q sdk version is finalized + std::string version = android::base::GetProperty("ro.vndk.version", "Q"); + return version == "Q" || version == "q"; +} + std::unique_ptr> FindApkFiles(const std::vector& dirs, bool recursive, std::ostream& out_error) { const auto predicate = [](unsigned char type, const std::string& path) -> bool { @@ -70,6 +85,22 @@ std::unique_ptr> FindApkFiles(const std::vector>(paths.cbegin(), paths.cend()); } +PolicyBitmask PolicyForPath(const std::string& apk_path) { + static const std::vector> values = { + {"/product/", PolicyFlags::POLICY_PRODUCT_PARTITION}, + {"/system/", PolicyFlags::POLICY_SYSTEM_PARTITION}, + {"/vendor/", PolicyFlags::POLICY_VENDOR_PARTITION}, + }; + + for (auto const& pair : values) { + if (apk_path.compare(0, pair.first.size(), pair.first) == 0) { + return pair.second | PolicyFlags::POLICY_PUBLIC; + } + } + + return PolicyFlags::POLICY_PUBLIC; +} + } // namespace bool Scan(const std::vector& args, std::ostream& out_error) { @@ -77,6 +108,7 @@ bool Scan(const std::vector& args, std::ostream& out_error) { std::string target_package_name; std::string target_apk_path; std::string output_directory; + std::vector override_policies; bool recursive = false; const CommandLineOptions opts = @@ -89,7 +121,12 @@ bool Scan(const std::vector& args, std::ostream& out_error) { .MandatoryOption("--target-apk-path", "path to target apk", &target_apk_path) .MandatoryOption("--output-directory", "directory in which to write artifacts (idmap files and overlays.list)", - &output_directory); + &output_directory) + .OptionalOption( + "--override-policy", + "input: an overlayable policy this overlay fulfills " + "(if none or supplied, the overlays will not have their policies overriden", + &override_policies); if (!opts.Parse(args, out_error)) { return false; } @@ -144,29 +181,63 @@ bool Scan(const std::vector& args, std::ostream& out_error) { continue; } - // Sort the static overlays in ascending priority order + PolicyBitmask fulfilled_policies; + if (!override_policies.empty()) { + if (Result result = PoliciesToBitmask(override_policies, out_error)) { + fulfilled_policies = *result; + } else { + return false; + } + } else { + fulfilled_policies = PolicyForPath(path); + } + + bool ignore_overlayable = false; + if ((fulfilled_policies & PolicyFlags::POLICY_VENDOR_PARTITION) != 0 && !VendorIsQOrLater()) { + // If the overlay is on a pre-Q vendor partition, do not enforce overlayable + // restrictions on this overlay because the pre-Q platform has no understanding of + // overlayable. + ignore_overlayable = true; + } + std::string idmap_path = Idmap::CanonicalIdmapPathFor(output_directory, path); - InputOverlay input{path, idmap_path, priority}; + + // Sort the static overlays in ascending priority order + InputOverlay input{path, idmap_path, priority, override_policies, ignore_overlayable}; interesting_apks.insert( std::lower_bound(interesting_apks.begin(), interesting_apks.end(), input), input); } std::stringstream stream; for (const auto& overlay : interesting_apks) { + // Create the idmap for the overlay if it currently does not exist or if it is not up to date. std::stringstream dev_null; - if (!Verify(std::vector({"--idmap-path", overlay.idmap_path}), dev_null) && - !Create(std::vector({ - "--target-apk-path", - target_apk_path, - "--overlay-apk-path", - overlay.apk_path, - "--idmap-path", - overlay.idmap_path, - }), - out_error)) { - return false; + + std::vector verify_args = {"--idmap-path", overlay.idmap_path}; + for (const std::string& policy : overlay.policies) { + verify_args.emplace_back("--policy"); + verify_args.emplace_back(policy); } - stream << overlay.idmap_path << std::endl; + + if (!Verify(std::vector(verify_args), dev_null)) { + std::vector create_args = {"--target-apk-path", target_apk_path, + "--overlay-apk-path", overlay.apk_path, + "--idmap-path", overlay.idmap_path}; + if (overlay.ignore_overlayable) { + create_args.emplace_back("--ignore-overlayable"); + } + + for (const std::string& policy : overlay.policies) { + verify_args.emplace_back("--policy"); + verify_args.emplace_back(policy); + } + + if (!Create(create_args, out_error)) { + return false; + } + } + + stream << overlay.idmap_path << std::endl; } std::cout << stream.str(); diff --git a/cmds/idmap2/idmap2d/Idmap2Service.cpp b/cmds/idmap2/idmap2d/Idmap2Service.cpp index d2e46e15fc59a..a3c752718ee26 100644 --- a/cmds/idmap2/idmap2d/Idmap2Service.cpp +++ b/cmds/idmap2/idmap2d/Idmap2Service.cpp @@ -26,12 +26,15 @@ #include #include "android-base/macros.h" +#include "android-base/stringprintf.h" #include "utils/String8.h" #include "utils/Trace.h" #include "idmap2/BinaryStreamVisitor.h" #include "idmap2/FileUtils.h" #include "idmap2/Idmap.h" +#include "idmap2/Policies.h" +#include "idmap2/Result.h" #include "idmap2d/Idmap2Service.h" @@ -39,6 +42,8 @@ using android::binder::Status; using android::idmap2::BinaryStreamVisitor; using android::idmap2::Idmap; using android::idmap2::IdmapHeader; +using android::idmap2::PolicyBitmask; +using android::idmap2::Result; using android::idmap2::utils::kIdmapFilePermissionMask; namespace { @@ -54,6 +59,10 @@ Status error(const std::string& msg) { return Status::fromExceptionCode(Status::EX_NONE, msg.c_str()); } +PolicyBitmask ConvertAidlArgToPolicyBitmask(int32_t arg) { + return static_cast(arg); +} + } // namespace namespace android::os { @@ -78,6 +87,8 @@ Status Idmap2Service::removeIdmap(const std::string& overlay_apk_path, } Status Idmap2Service::verifyIdmap(const std::string& overlay_apk_path, + int32_t fulfilled_policies ATTRIBUTE_UNUSED, + bool enforce_overlayable ATTRIBUTE_UNUSED, int32_t user_id ATTRIBUTE_UNUSED, bool* _aidl_return) { assert(_aidl_return); const std::string idmap_path = Idmap::CanonicalIdmapPathFor(kIdmapCacheDir, overlay_apk_path); @@ -86,11 +97,15 @@ Status Idmap2Service::verifyIdmap(const std::string& overlay_apk_path, fin.close(); std::stringstream dev_null; *_aidl_return = header && header->IsUpToDate(dev_null); + + // TODO(b/119328308): Check that the set of fulfilled policies of the overlay has not changed + return ok(); } Status Idmap2Service::createIdmap(const std::string& target_apk_path, - const std::string& overlay_apk_path, int32_t user_id, + const std::string& overlay_apk_path, int32_t fulfilled_policies, + bool enforce_overlayable, int32_t user_id, std::unique_ptr* _aidl_return) { assert(_aidl_return); std::stringstream trace; @@ -101,6 +116,8 @@ Status Idmap2Service::createIdmap(const std::string& target_apk_path, _aidl_return->reset(nullptr); + const PolicyBitmask policy_bitmask = ConvertAidlArgToPolicyBitmask(fulfilled_policies); + const std::unique_ptr target_apk = ApkAssets::Load(target_apk_path); if (!target_apk) { return error("failed to load apk " + target_apk_path); @@ -113,7 +130,8 @@ Status Idmap2Service::createIdmap(const std::string& target_apk_path, std::stringstream err; const std::unique_ptr idmap = - Idmap::FromApkAssets(target_apk_path, *target_apk, overlay_apk_path, *overlay_apk, err); + Idmap::FromApkAssets(target_apk_path, *target_apk, overlay_apk_path, *overlay_apk, + policy_bitmask, enforce_overlayable, err); if (!idmap) { return error(err.str()); } diff --git a/cmds/idmap2/idmap2d/Idmap2Service.h b/cmds/idmap2/idmap2d/Idmap2Service.h index e0bc22e9e9e64..1aab0598449bd 100644 --- a/cmds/idmap2/idmap2d/Idmap2Service.h +++ b/cmds/idmap2/idmap2d/Idmap2Service.h @@ -39,11 +39,12 @@ class Idmap2Service : public BinderService, public BnIdmap2 { binder::Status removeIdmap(const std::string& overlay_apk_path, int32_t user_id, bool* _aidl_return); - binder::Status verifyIdmap(const std::string& overlay_apk_path, int32_t user_id, - bool* _aidl_return); + binder::Status verifyIdmap(const std::string& overlay_apk_path, int32_t fulfilled_policies, + bool enforce_overlayable, int32_t user_id, bool* _aidl_return); binder::Status createIdmap(const std::string& target_apk_path, - const std::string& overlay_apk_path, int32_t user_id, + const std::string& overlay_apk_path, int32_t fulfilled_policies, + bool enforce_overlayable, int32_t user_id, std::unique_ptr* _aidl_return); }; diff --git a/cmds/idmap2/idmap2d/aidl/android/os/IIdmap2.aidl b/cmds/idmap2/idmap2d/aidl/android/os/IIdmap2.aidl index d475417a0935c..ea7274f3ea583 100644 --- a/cmds/idmap2/idmap2d/aidl/android/os/IIdmap2.aidl +++ b/cmds/idmap2/idmap2d/aidl/android/os/IIdmap2.aidl @@ -20,9 +20,18 @@ package android.os; * @hide */ interface IIdmap2 { + const int POLICY_PUBLIC = 0x00000001; + const int POLICY_SYSTEM_PARTITION = 0x00000002; + const int POLICY_VENDOR_PARTITION = 0x00000004; + const int POLICY_PRODUCT_PARTITION = 0x00000008; + @utf8InCpp String getIdmapPath(@utf8InCpp String overlayApkPath, int userId); boolean removeIdmap(@utf8InCpp String overlayApkPath, int userId); - boolean verifyIdmap(@utf8InCpp String overlayApkPath, int userId); + boolean verifyIdmap(@utf8InCpp String overlayApkPath, int fulfilledPolicies, + boolean enforceOverlayable, int userId); @nullable @utf8InCpp String createIdmap(@utf8InCpp String targetApkPath, - @utf8InCpp String overlayApkPath, int userId); + @utf8InCpp String overlayApkPath, + int fulfilledPolicies, + boolean enforceOverlayable, + int userId); } diff --git a/cmds/idmap2/include/idmap2/CommandLineOptions.h b/cmds/idmap2/include/idmap2/CommandLineOptions.h index b93e71631d084..6db6bf9ea8ad1 100644 --- a/cmds/idmap2/include/idmap2/CommandLineOptions.h +++ b/cmds/idmap2/include/idmap2/CommandLineOptions.h @@ -44,6 +44,8 @@ class CommandLineOptions { std::vector* value); CommandLineOptions& OptionalOption(const std::string& name, const std::string& description, std::string* value); + CommandLineOptions& OptionalOption(const std::string& name, const std::string& description, + std::vector* value); bool Parse(const std::vector& argv, std::ostream& outError) const; void Usage(std::ostream& out) const; @@ -56,6 +58,7 @@ class CommandLineOptions { COUNT_OPTIONAL, COUNT_EXACTLY_ONCE, COUNT_ONCE_OR_MORE, + COUNT_OPTIONAL_ONCE_OR_MORE, } count; bool argument; }; diff --git a/cmds/idmap2/include/idmap2/Idmap.h b/cmds/idmap2/include/idmap2/Idmap.h index b989e4c3f9d18..1666dc8a3bbd1 100644 --- a/cmds/idmap2/include/idmap2/Idmap.h +++ b/cmds/idmap2/include/idmap2/Idmap.h @@ -57,6 +57,8 @@ #include "androidfw/ResourceTypes.h" #include "androidfw/StringPiece.h" +#include "idmap2/Policies.h" + namespace android::idmap2 { class Idmap; @@ -233,11 +235,10 @@ class Idmap { // file is used; change this in the next version of idmap to use a named // package instead; also update FromApkAssets to take additional parameters: // the target and overlay package names - static std::unique_ptr FromApkAssets(const std::string& target_apk_path, - const ApkAssets& target_apk_assets, - const std::string& overlay_apk_path, - const ApkAssets& overlay_apk_assets, - std::ostream& out_error); + static std::unique_ptr FromApkAssets( + const std::string& target_apk_path, const ApkAssets& target_apk_assets, + const std::string& overlay_apk_path, const ApkAssets& overlay_apk_assets, + const PolicyBitmask& fulfilled_policies, bool enforce_overlayable, std::ostream& out_error); inline const std::unique_ptr& GetHeader() const { return header_; diff --git a/cmds/idmap2/include/idmap2/Policies.h b/cmds/idmap2/include/idmap2/Policies.h new file mode 100644 index 0000000000000..eecee25445e22 --- /dev/null +++ b/cmds/idmap2/include/idmap2/Policies.h @@ -0,0 +1,41 @@ +/* + * 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. + */ + +#include +#include +#include + +#include "androidfw/ResourceTypes.h" +#include "androidfw/StringPiece.h" + +#include "Result.h" + +#ifndef IDMAP2_INCLUDE_IDMAP2_POLICIES_H_ +#define IDMAP2_INCLUDE_IDMAP2_POLICIES_H_ + +namespace android::idmap2 { + +using PolicyFlags = ResTable_overlayable_policy_header::PolicyFlags; +using PolicyBitmask = uint32_t; + +// Parses a the string representation of a set of policies into a bitmask. The format of the string +// is the same as for the element. +Result PoliciesToBitmask(const std::vector& policies, + std::ostream& err); + +} // namespace android::idmap2 + +#endif // IDMAP2_INCLUDE_IDMAP2_POLICIES_H_ diff --git a/cmds/idmap2/libidmap2/CommandLineOptions.cpp b/cmds/idmap2/libidmap2/CommandLineOptions.cpp index cabc8f398c469..a49a607091a45 100644 --- a/cmds/idmap2/libidmap2/CommandLineOptions.cpp +++ b/cmds/idmap2/libidmap2/CommandLineOptions.cpp @@ -68,9 +68,18 @@ CommandLineOptions& CommandLineOptions::OptionalOption(const std::string& name, return *this; } +CommandLineOptions& CommandLineOptions::OptionalOption(const std::string& name, + const std::string& description, + std::vector* value) { + assert(value != nullptr); + auto func = [value](const std::string& arg) -> void { value->push_back(arg); }; + options_.push_back(Option{name, description, func, Option::COUNT_OPTIONAL_ONCE_OR_MORE, true}); + return *this; +} + bool CommandLineOptions::Parse(const std::vector& argv, std::ostream& outError) const { const auto pivot = std::partition(options_.begin(), options_.end(), [](const Option& opt) { - return opt.count != Option::COUNT_OPTIONAL; + return opt.count != Option::COUNT_OPTIONAL && opt.count != Option::COUNT_OPTIONAL_ONCE_OR_MORE; }); std::set mandatory_opts; std::transform(options_.begin(), pivot, std::inserter(mandatory_opts, mandatory_opts.end()), @@ -122,7 +131,8 @@ void CommandLineOptions::Usage(std::ostream& out) const { size_t maxLength = 0; out << "usage: " << name_; for (const Option& opt : options_) { - const bool mandatory = opt.count != Option::COUNT_OPTIONAL; + const bool mandatory = + opt.count != Option::COUNT_OPTIONAL && opt.count != Option::COUNT_OPTIONAL_ONCE_OR_MORE; out << " "; if (!mandatory) { out << "["; @@ -134,9 +144,15 @@ void CommandLineOptions::Usage(std::ostream& out) const { out << opt.name; maxLength = std::max(maxLength, opt.name.size()); } + + if (opt.count == Option::COUNT_OPTIONAL_ONCE_OR_MORE) { + out << " [..]"; + } + if (!mandatory) { out << "]"; } + if (opt.count == Option::COUNT_ONCE_OR_MORE) { out << " [" << opt.name << " arg [..]]"; } @@ -150,7 +166,8 @@ void CommandLineOptions::Usage(std::ostream& out) const { out << opt.name; } out << " " << opt.description; - if (opt.count == Option::COUNT_ONCE_OR_MORE) { + if (opt.count == Option::COUNT_ONCE_OR_MORE || + opt.count == Option::COUNT_OPTIONAL_ONCE_OR_MORE) { out << " (can be provided multiple times)"; } out << std::endl; diff --git a/cmds/idmap2/libidmap2/Idmap.cpp b/cmds/idmap2/libidmap2/Idmap.cpp index 37d6af8fa4775..2890ae11b0aff 100644 --- a/cmds/idmap2/libidmap2/Idmap.cpp +++ b/cmds/idmap2/libidmap2/Idmap.cpp @@ -274,11 +274,23 @@ std::unique_ptr Idmap::FromBinaryStream(std::istream& stream, return std::move(idmap); } -std::unique_ptr Idmap::FromApkAssets(const std::string& target_apk_path, - const ApkAssets& target_apk_assets, - const std::string& overlay_apk_path, - const ApkAssets& overlay_apk_assets, - std::ostream& out_error) { +bool CheckOverlayable(const LoadedPackage& target_package, PolicyBitmask fulfilled_polices, + ResourceId resid) { + const OverlayableInfo* info = target_package.GetOverlayableInfo(resid); + if (info == nullptr) { + // If the resource does not have an overlayable definition, allow the resource to be overlaid. + // Once overlayable enforcement is turned on, this check will return false. + return true; + } + + // Enforce policy restrictions if the resource is declared as overlayable. + return (info->policy_flags & fulfilled_polices) != 0; +} + +std::unique_ptr Idmap::FromApkAssets( + const std::string& target_apk_path, const ApkAssets& target_apk_assets, + const std::string& overlay_apk_path, const ApkAssets& overlay_apk_assets, + const PolicyBitmask& fulfilled_policies, bool enforce_overlayable, std::ostream& out_error) { AssetManager2 target_asset_manager; if (!target_asset_manager.SetApkAssets({&target_apk_assets}, true, false)) { out_error << "error: failed to create target asset manager" << std::endl; @@ -380,6 +392,15 @@ std::unique_ptr Idmap::FromApkAssets(const std::string& target_apk_ if (target_resid == 0) { continue; } + + if (enforce_overlayable && !CheckOverlayable(*target_pkg, fulfilled_policies, target_resid)) { + // The resources must be defined as overlayable and the overlay must fulfill at least one + // policy enforced on the overlayable resource + LOG(WARNING) << "overlay \"" << overlay_apk_path << "\" is not allowed to overlay resource \"" + << full_name << "\"" << std::endl; + continue; + } + matching_resources.Add(target_resid, overlay_resid); } diff --git a/cmds/idmap2/libidmap2/Policies.cpp b/cmds/idmap2/libidmap2/Policies.cpp new file mode 100644 index 0000000000000..0f87ef0c4ea91 --- /dev/null +++ b/cmds/idmap2/libidmap2/Policies.cpp @@ -0,0 +1,57 @@ +/* + * 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. + */ + +#include +#include +#include +#include +#include + +#include "androidfw/ResourceTypes.h" + +#include "idmap2/Idmap.h" +#include "idmap2/Policies.h" +#include "idmap2/Result.h" + +namespace android::idmap2 { + +namespace { + +const std::map kStringToFlag = { + {"public", PolicyFlags::POLICY_PUBLIC}, + {"product", PolicyFlags::POLICY_PRODUCT_PARTITION}, + {"system", PolicyFlags::POLICY_SYSTEM_PARTITION}, + {"vendor", PolicyFlags::POLICY_VENDOR_PARTITION}, +}; +} // namespace + +Result PoliciesToBitmask(const std::vector& policies, + std::ostream& err) { + PolicyBitmask bitmask = 0; + for (const std::string& policy : policies) { + const auto iter = kStringToFlag.find(policy); + if (iter != kStringToFlag.end()) { + bitmask |= iter->second; + } else { + err << "error: unknown policy \"" << policy << "\""; + return kResultError; + } + } + + return Result(bitmask); +} + +} // namespace android::idmap2 diff --git a/cmds/idmap2/tests/BinaryStreamVisitorTests.cpp b/cmds/idmap2/tests/BinaryStreamVisitorTests.cpp index 2698ac0a734d7..35ec1ff1dbb5e 100644 --- a/cmds/idmap2/tests/BinaryStreamVisitorTests.cpp +++ b/cmds/idmap2/tests/BinaryStreamVisitorTests.cpp @@ -78,7 +78,8 @@ TEST(BinaryStreamVisitorTests, CreateIdmapFromApkAssetsInteropWithLoadedIdmap) { std::stringstream error; std::unique_ptr idmap = - Idmap::FromApkAssets(target_apk_path, *target_apk, overlay_apk_path, *overlay_apk, error); + Idmap::FromApkAssets(target_apk_path, *target_apk, overlay_apk_path, *overlay_apk, + PolicyFlags::POLICY_PUBLIC, /* enforce_overlayable */ true, error); ASSERT_THAT(idmap, NotNull()); std::stringstream stream; @@ -101,25 +102,52 @@ TEST(BinaryStreamVisitorTests, CreateIdmapFromApkAssetsInteropWithLoadedIdmap) { header = loaded_idmap->GetEntryMapForType(0x02); ASSERT_THAT(header, NotNull()); - success = LoadedIdmap::Lookup(header, 0x0002, &entry); + success = LoadedIdmap::Lookup(header, 0x0000, &entry); // string/a ASSERT_FALSE(success); - success = LoadedIdmap::Lookup(header, 0x0003, &entry); + success = LoadedIdmap::Lookup(header, 0x0001, &entry); // string/b + ASSERT_FALSE(success); + + success = LoadedIdmap::Lookup(header, 0x0002, &entry); // string/c + ASSERT_FALSE(success); + + success = LoadedIdmap::Lookup(header, 0x0003, &entry); // string/not_overlayable + ASSERT_FALSE(success); + + success = LoadedIdmap::Lookup(header, 0x0004, &entry); // string/policy_product + ASSERT_FALSE(success); + + success = LoadedIdmap::Lookup(header, 0x0005, &entry); // string/policy_public + ASSERT_FALSE(success); + + success = LoadedIdmap::Lookup(header, 0x0006, &entry); // string/policy_system + ASSERT_FALSE(success); + + success = LoadedIdmap::Lookup(header, 0x0007, &entry); // string/policy_system_vendor + ASSERT_FALSE(success); + + success = LoadedIdmap::Lookup(header, 0x0008, &entry); // string/str1 ASSERT_TRUE(success); ASSERT_EQ(entry, 0x0000); - success = LoadedIdmap::Lookup(header, 0x0004, &entry); + success = LoadedIdmap::Lookup(header, 0x0009, &entry); // string/str2 ASSERT_FALSE(success); - success = LoadedIdmap::Lookup(header, 0x0005, &entry); + success = LoadedIdmap::Lookup(header, 0x000a, &entry); // string/str3 ASSERT_TRUE(success); ASSERT_EQ(entry, 0x0001); - success = LoadedIdmap::Lookup(header, 0x0006, &entry); + success = LoadedIdmap::Lookup(header, 0x000b, &entry); // string/str4 ASSERT_TRUE(success); ASSERT_EQ(entry, 0x0002); - success = LoadedIdmap::Lookup(header, 0x0007, &entry); + success = LoadedIdmap::Lookup(header, 0x000c, &entry); // string/x + ASSERT_FALSE(success); + + success = LoadedIdmap::Lookup(header, 0x000d, &entry); // string/y + ASSERT_FALSE(success); + + success = LoadedIdmap::Lookup(header, 0x000e, &entry); // string/z ASSERT_FALSE(success); } diff --git a/cmds/idmap2/tests/CommandLineOptionsTests.cpp b/cmds/idmap2/tests/CommandLineOptionsTests.cpp index c27d27a16b94a..39f18d3336afb 100644 --- a/cmds/idmap2/tests/CommandLineOptionsTests.cpp +++ b/cmds/idmap2/tests/CommandLineOptionsTests.cpp @@ -121,6 +121,56 @@ TEST(CommandLineOptionsTests, OptionalOption) { ASSERT_FALSE(success); } +TEST(CommandLineOptionsTests, OptionalOptionList) { + std::vector foo; + std::vector bar; + CommandLineOptions opts = CommandLineOptions("test") + .OptionalOption("--foo", "", &foo) + .OptionalOption("--bar", "", &bar); + std::ostream fakeStdErr(nullptr); + bool success = opts.Parse({"--foo", "FOO", "--bar", "BAR"}, fakeStdErr); + ASSERT_TRUE(success); + ASSERT_EQ(foo.size(), 1U); + ASSERT_EQ(foo[0], "FOO"); + ASSERT_EQ(bar.size(), 1U); + ASSERT_EQ(bar[0], "BAR"); + + foo.clear(); + bar.clear(); + success = opts.Parse({"--foo", "BAZ"}, fakeStdErr); + ASSERT_TRUE(success); + ASSERT_EQ(foo.size(), 1U); + ASSERT_EQ(foo[0], "BAZ"); + ASSERT_EQ(bar.size(), 0U); + + foo.clear(); + bar.clear(); + success = + opts.Parse({"--foo", "BAZ", "--foo", "BIZ", "--bar", "FIZ", "--bar", "FUZZ"}, fakeStdErr); + ASSERT_TRUE(success); + ASSERT_EQ(foo.size(), 2U); + ASSERT_EQ(foo[0], "BAZ"); + ASSERT_EQ(foo[1], "BIZ"); + ASSERT_EQ(bar.size(), 2U); + ASSERT_EQ(bar[0], "FIZ"); + ASSERT_EQ(bar[1], "FUZZ"); + + foo.clear(); + bar.clear(); + success = opts.Parse({"--foo"}, fakeStdErr); + ASSERT_FALSE(success); + + foo.clear(); + bar.clear(); + success = opts.Parse({"--foo", "--bar", "BAR"}, fakeStdErr); + ASSERT_FALSE(success); + + foo.clear(); + bar.clear(); + success = opts.Parse({"--foo", "FOO", "--bar"}, fakeStdErr); + ASSERT_FALSE(success); +} + TEST(CommandLineOptionsTests, CornerCases) { std::string foo; std::string bar; @@ -172,6 +222,7 @@ TEST(CommandLineOptionsTests, Usage) { bool arg5 = false; bool arg6 = false; std::vector arg7; + std::vector arg8; CommandLineOptions opts = CommandLineOptions("test") .MandatoryOption("--aa", "description-aa", &arg1) .OptionalFlag("--bb", "description-bb", &arg5) @@ -179,12 +230,13 @@ TEST(CommandLineOptionsTests, Usage) { .OptionalOption("--dd", "description-dd", &arg3) .MandatoryOption("--ee", "description-ee", &arg4) .OptionalFlag("--ff", "description-ff", &arg6) - .MandatoryOption("--gg", "description-gg", &arg7); + .MandatoryOption("--gg", "description-gg", &arg7) + .OptionalOption("--hh", "description-hh", &arg8); std::stringstream stream; opts.Usage(stream); const std::string s = stream.str(); ASSERT_NE(s.find("usage: test --aa arg [--bb] [--cc arg] [--dd arg] --ee arg [--ff] --gg arg " - "[--gg arg [..]]"), + "[--gg arg [..]] [--hh arg [..]]"), std::string::npos); ASSERT_NE(s.find("--aa arg description-aa"), std::string::npos); ASSERT_NE(s.find("--ff description-ff"), std::string::npos); diff --git a/cmds/idmap2/tests/FileUtilsTests.cpp b/cmds/idmap2/tests/FileUtilsTests.cpp index 4bf832a34691a..d9d9a7f829cf6 100644 --- a/cmds/idmap2/tests/FileUtilsTests.cpp +++ b/cmds/idmap2/tests/FileUtilsTests.cpp @@ -37,10 +37,10 @@ TEST(FileUtilsTests, FindFilesFindEverythingNonRecursive) { [](unsigned char type ATTRIBUTE_UNUSED, const std::string& path ATTRIBUTE_UNUSED) -> bool { return true; }); ASSERT_THAT(v, NotNull()); - ASSERT_EQ(v->size(), 4U); - ASSERT_EQ( - std::set(v->begin(), v->end()), - std::set({root + "/.", root + "/..", root + "/overlay", root + "/target"})); + ASSERT_EQ(v->size(), 6U); + ASSERT_EQ(std::set(v->begin(), v->end()), + std::set({root + "/.", root + "/..", root + "/overlay", root + "/target", + root + "/system-overlay", root + "/system-overlay-invalid"})); } TEST(FileUtilsTests, FindFilesFindApkFilesRecursive) { @@ -49,11 +49,13 @@ TEST(FileUtilsTests, FindFilesFindApkFilesRecursive) { return type == DT_REG && path.size() > 4 && path.compare(path.size() - 4, 4, ".apk") == 0; }); ASSERT_THAT(v, NotNull()); - ASSERT_EQ(v->size(), 4U); + ASSERT_EQ(v->size(), 6U); ASSERT_EQ(std::set(v->begin(), v->end()), std::set({root + "/target/target.apk", root + "/overlay/overlay.apk", root + "/overlay/overlay-static-1.apk", - root + "/overlay/overlay-static-2.apk"})); + root + "/overlay/overlay-static-2.apk", + root + "/system-overlay/system-overlay.apk", + root + "/system-overlay-invalid/system-overlay-invalid.apk"})); } TEST(FileUtilsTests, ReadFile) { diff --git a/cmds/idmap2/tests/Idmap2BinaryTests.cpp b/cmds/idmap2/tests/Idmap2BinaryTests.cpp index 22f48e9a396f6..0c8f164bf0968 100644 --- a/cmds/idmap2/tests/Idmap2BinaryTests.cpp +++ b/cmds/idmap2/tests/Idmap2BinaryTests.cpp @@ -38,6 +38,7 @@ #include "gtest/gtest.h" #include "androidfw/PosixUtils.h" + #include "idmap2/FileUtils.h" #include "idmap2/Idmap.h" @@ -114,8 +115,9 @@ TEST_F(Idmap2BinaryTests, Dump) { ASSERT_THAT(result, NotNull()); ASSERT_EQ(result->status, EXIT_SUCCESS) << result->stderr; ASSERT_NE(result->stdout.find("0x7f010000 -> 0x7f010000 integer/int1"), std::string::npos); - ASSERT_NE(result->stdout.find("0x7f020003 -> 0x7f020000 string/str1"), std::string::npos); - ASSERT_NE(result->stdout.find("0x7f020005 -> 0x7f020001 string/str3"), std::string::npos); + ASSERT_NE(result->stdout.find("0x7f020008 -> 0x7f020000 string/str1"), std::string::npos); + ASSERT_NE(result->stdout.find("0x7f02000a -> 0x7f020001 string/str3"), std::string::npos); + ASSERT_NE(result->stdout.find("0x7f02000b -> 0x7f020002 string/str4"), std::string::npos); ASSERT_EQ(result->stdout.find("00000210: 007f target package id"), std::string::npos); // clang-format off @@ -157,7 +159,8 @@ TEST_F(Idmap2BinaryTests, Scan) { "--recursive", "--target-package-name", "test.target", "--target-apk-path", GetTargetApkPath(), - "--output-directory", GetTempDirPath()}); + "--output-directory", GetTempDirPath(), + "--override-policy", "public"}); // clang-format on ASSERT_THAT(result, NotNull()); ASSERT_EQ(result->status, EXIT_SUCCESS) << result->stderr; @@ -190,7 +193,8 @@ TEST_F(Idmap2BinaryTests, Scan) { "--input-directory", GetTestDataPath() + "/overlay", "--target-package-name", "test.target", "--target-apk-path", GetTargetApkPath(), - "--output-directory", GetTempDirPath()}); + "--output-directory", GetTempDirPath(), + "--override-policy", "public"}); // clang-format on ASSERT_THAT(result, NotNull()); ASSERT_EQ(result->status, EXIT_SUCCESS) << result->stderr; @@ -207,7 +211,8 @@ TEST_F(Idmap2BinaryTests, Scan) { "--recursive", "--target-package-name", "test.target", "--target-apk-path", GetTargetApkPath(), - "--output-directory", GetTempDirPath()}); + "--output-directory", GetTempDirPath(), + "--override-policy", "public"}); // clang-format on ASSERT_THAT(result, NotNull()); ASSERT_EQ(result->status, EXIT_SUCCESS) << result->stderr; @@ -222,7 +227,8 @@ TEST_F(Idmap2BinaryTests, Scan) { "--input-directory", GetTempDirPath(), "--target-package-name", "test.target", "--target-apk-path", GetTargetApkPath(), - "--output-directory", GetTempDirPath()}); + "--output-directory", GetTempDirPath(), + "--override-policy", "public"}); // clang-format on ASSERT_THAT(result, NotNull()); ASSERT_EQ(result->status, EXIT_SUCCESS) << result->stderr; @@ -245,7 +251,7 @@ TEST_F(Idmap2BinaryTests, Lookup) { "lookup", "--idmap-path", GetIdmapPath(), "--config", "", - "--resid", "0x7f020003"}); // string/str1 + "--resid", "0x7f020008"}); // string/str1 // clang-format on ASSERT_THAT(result, NotNull()); ASSERT_EQ(result->status, EXIT_SUCCESS) << result->stderr; @@ -310,6 +316,18 @@ TEST_F(Idmap2BinaryTests, InvalidCommandLineOptions) { // clang-format on ASSERT_THAT(result, NotNull()); ASSERT_NE(result->status, EXIT_SUCCESS); + + // unknown policy + // clang-format off + result = ExecuteBinary({"idmap2", + "create", + "--target-apk-path", GetTargetApkPath(), + "--overlay-apk-path", GetOverlayApkPath(), + "--idmap-path", GetIdmapPath(), + "--policy", "this-does-not-exist"}); + // clang-format on + ASSERT_THAT(result, NotNull()); + ASSERT_NE(result->status, EXIT_SUCCESS); } } // namespace android::idmap2 diff --git a/cmds/idmap2/tests/IdmapTests.cpp b/cmds/idmap2/tests/IdmapTests.cpp index 963f22ec8d723..c6eb71cbeb72a 100644 --- a/cmds/idmap2/tests/IdmapTests.cpp +++ b/cmds/idmap2/tests/IdmapTests.cpp @@ -184,13 +184,14 @@ TEST(IdmapTests, CreateIdmapFromApkAssets) { std::stringstream error; std::unique_ptr idmap = - Idmap::FromApkAssets(target_apk_path, *target_apk, overlay_apk_path, *overlay_apk, error); + Idmap::FromApkAssets(target_apk_path, *target_apk, overlay_apk_path, *overlay_apk, + PolicyFlags::POLICY_PUBLIC, /* enforce_overlayable */ true, error); ASSERT_THAT(idmap, NotNull()); ASSERT_THAT(idmap->GetHeader(), NotNull()); ASSERT_EQ(idmap->GetHeader()->GetMagic(), 0x504d4449U); ASSERT_EQ(idmap->GetHeader()->GetVersion(), 0x01U); - ASSERT_EQ(idmap->GetHeader()->GetTargetCrc(), 0xf5ad1d1d); + ASSERT_EQ(idmap->GetHeader()->GetTargetCrc(), 0xca2093da); ASSERT_EQ(idmap->GetHeader()->GetOverlayCrc(), 0xd470336b); ASSERT_EQ(idmap->GetHeader()->GetTargetPath().to_string(), target_apk_path); ASSERT_EQ(idmap->GetHeader()->GetOverlayPath(), overlay_apk_path); @@ -216,13 +217,127 @@ TEST(IdmapTests, CreateIdmapFromApkAssets) { ASSERT_EQ(types[1]->GetTargetTypeId(), 0x02U); ASSERT_EQ(types[1]->GetOverlayTypeId(), 0x02U); ASSERT_EQ(types[1]->GetEntryCount(), 4U); - ASSERT_EQ(types[1]->GetEntryOffset(), 3U); + ASSERT_EQ(types[1]->GetEntryOffset(), 8U); ASSERT_EQ(types[1]->GetEntry(0), 0x0000U); ASSERT_EQ(types[1]->GetEntry(1), kNoEntry); ASSERT_EQ(types[1]->GetEntry(2), 0x0001U); ASSERT_EQ(types[1]->GetEntry(3), 0x0002U); } +TEST(IdmapTests, CreateIdmapFromApkAssetsPolicySystemPublic) { + const std::string target_apk_path(GetTestDataPath() + "/target/target.apk"); + std::unique_ptr target_apk = ApkAssets::Load(target_apk_path); + ASSERT_THAT(target_apk, NotNull()); + + const std::string overlay_apk_path(GetTestDataPath() + "/system-overlay/system-overlay.apk"); + std::unique_ptr overlay_apk = ApkAssets::Load(overlay_apk_path); + ASSERT_THAT(overlay_apk, NotNull()); + + std::stringstream error; + std::unique_ptr idmap = + Idmap::FromApkAssets(target_apk_path, *target_apk, overlay_apk_path, *overlay_apk, + PolicyFlags::POLICY_SYSTEM_PARTITION | PolicyFlags::POLICY_PUBLIC, + /* enforce_overlayable */ true, error); + ASSERT_THAT(idmap, NotNull()); + + const std::vector>& dataBlocks = idmap->GetData(); + ASSERT_EQ(dataBlocks.size(), 1U); + + const std::unique_ptr& data = dataBlocks[0]; + + ASSERT_EQ(data->GetHeader()->GetTargetPackageId(), 0x7fU); + ASSERT_EQ(data->GetHeader()->GetTypeCount(), 1U); + + const std::vector>& types = data->GetTypeEntries(); + ASSERT_EQ(types.size(), 1U); + + ASSERT_EQ(types[0]->GetTargetTypeId(), 0x02U); + ASSERT_EQ(types[0]->GetOverlayTypeId(), 0x01U); + ASSERT_EQ(types[0]->GetEntryCount(), 3U); + ASSERT_EQ(types[0]->GetEntryOffset(), 5U); + ASSERT_EQ(types[0]->GetEntry(0), 0x0000U); // string/policy_public + ASSERT_EQ(types[0]->GetEntry(1), 0x0001U); // string/policy_system + ASSERT_EQ(types[0]->GetEntry(2), 0x0002U); // string/policy_system_vendor +} + +TEST(IdmapTests, CreateIdmapFromApkAssetsPolicySystemPublicInvalid) { + const std::string target_apk_path(GetTestDataPath() + "/target/target.apk"); + std::unique_ptr target_apk = ApkAssets::Load(target_apk_path); + ASSERT_THAT(target_apk, NotNull()); + + const std::string overlay_apk_path(GetTestDataPath() + + "/system-overlay-invalid/system-overlay-invalid.apk"); + std::unique_ptr overlay_apk = ApkAssets::Load(overlay_apk_path); + ASSERT_THAT(overlay_apk, NotNull()); + + std::stringstream error; + std::unique_ptr idmap = + Idmap::FromApkAssets(target_apk_path, *target_apk, overlay_apk_path, *overlay_apk, + PolicyFlags::POLICY_SYSTEM_PARTITION | PolicyFlags::POLICY_PUBLIC, + /* enforce_overlayable */ true, error); + ASSERT_THAT(idmap, NotNull()); + + const std::vector>& dataBlocks = idmap->GetData(); + ASSERT_EQ(dataBlocks.size(), 1U); + + const std::unique_ptr& data = dataBlocks[0]; + + ASSERT_EQ(data->GetHeader()->GetTargetPackageId(), 0x7fU); + ASSERT_EQ(data->GetHeader()->GetTypeCount(), 1U); + + const std::vector>& types = data->GetTypeEntries(); + ASSERT_EQ(types.size(), 1U); + + ASSERT_EQ(types[0]->GetTargetTypeId(), 0x02U); + ASSERT_EQ(types[0]->GetOverlayTypeId(), 0x01U); + ASSERT_EQ(types[0]->GetEntryCount(), 5U); + ASSERT_EQ(types[0]->GetEntryOffset(), 3U); + ASSERT_EQ(types[0]->GetEntry(0), 0x0000U); // string/not_overlayable + ASSERT_EQ(types[0]->GetEntry(1), kNoEntry); // string/policy_product + ASSERT_EQ(types[0]->GetEntry(2), 0x0002U); // string/policy_public + ASSERT_EQ(types[0]->GetEntry(3), 0x0003U); // string/policy_system + ASSERT_EQ(types[0]->GetEntry(4), 0x0004U); // string/policy_system_vendor +} + +TEST(IdmapTests, CreateIdmapFromApkAssetsPolicySystemPublicInvalidIgnoreOverlayable) { + const std::string target_apk_path(GetTestDataPath() + "/target/target.apk"); + std::unique_ptr target_apk = ApkAssets::Load(target_apk_path); + ASSERT_THAT(target_apk, NotNull()); + + const std::string overlay_apk_path(GetTestDataPath() + + "/system-overlay-invalid/system-overlay-invalid.apk"); + std::unique_ptr overlay_apk = ApkAssets::Load(overlay_apk_path); + ASSERT_THAT(overlay_apk, NotNull()); + + std::stringstream error; + std::unique_ptr idmap = + Idmap::FromApkAssets(target_apk_path, *target_apk, overlay_apk_path, *overlay_apk, + PolicyFlags::POLICY_SYSTEM_PARTITION | PolicyFlags::POLICY_PUBLIC, + /* enforce_overlayable */ false, error); + ASSERT_THAT(idmap, NotNull()); + + const std::vector>& dataBlocks = idmap->GetData(); + ASSERT_EQ(dataBlocks.size(), 1U); + + const std::unique_ptr& data = dataBlocks[0]; + + ASSERT_EQ(data->GetHeader()->GetTargetPackageId(), 0x7fU); + ASSERT_EQ(data->GetHeader()->GetTypeCount(), 1U); + + const std::vector>& types = data->GetTypeEntries(); + ASSERT_EQ(types.size(), 1U); + + ASSERT_EQ(types[0]->GetTargetTypeId(), 0x02U); + ASSERT_EQ(types[0]->GetOverlayTypeId(), 0x01U); + ASSERT_EQ(types[0]->GetEntryCount(), 5U); + ASSERT_EQ(types[0]->GetEntryOffset(), 3U); + ASSERT_EQ(types[0]->GetEntry(0), 0x0000U); // string/not_overlayable + ASSERT_EQ(types[0]->GetEntry(1), 0x0001U); // string/policy_product + ASSERT_EQ(types[0]->GetEntry(2), 0x0002U); // string/policy_public + ASSERT_EQ(types[0]->GetEntry(3), 0x0003U); // string/policy_system + ASSERT_EQ(types[0]->GetEntry(4), 0x0004U); // string/policy_system_vendor +} + TEST(IdmapTests, FailToCreateIdmapFromApkAssetsIfPathTooLong) { std::string target_apk_path(GetTestDataPath()); for (int i = 0; i < 32; i++) { @@ -239,7 +354,8 @@ TEST(IdmapTests, FailToCreateIdmapFromApkAssetsIfPathTooLong) { std::stringstream error; std::unique_ptr idmap = - Idmap::FromApkAssets(target_apk_path, *target_apk, overlay_apk_path, *overlay_apk, error); + Idmap::FromApkAssets(target_apk_path, *target_apk, overlay_apk_path, *overlay_apk, + PolicyFlags::POLICY_PUBLIC, /* enforce_overlayable */ true, error); ASSERT_THAT(idmap, IsNull()); } @@ -255,8 +371,9 @@ TEST(IdmapTests, IdmapHeaderIsUpToDate) { ASSERT_THAT(overlay_apk, NotNull()); std::stringstream error; - std::unique_ptr idmap = - Idmap::FromApkAssets(target_apk_path, *target_apk, overlay_apk_path, *overlay_apk, error); + std::unique_ptr idmap = Idmap::FromApkAssets( + target_apk_path, *target_apk, overlay_apk_path, *overlay_apk, PolicyFlags::POLICY_PUBLIC, + /* enforce_overlayable */ true, error); ASSERT_THAT(idmap, NotNull()); std::stringstream stream; diff --git a/cmds/idmap2/tests/PoliciesTests.cpp b/cmds/idmap2/tests/PoliciesTests.cpp new file mode 100644 index 0000000000000..ab567adc6f19d --- /dev/null +++ b/cmds/idmap2/tests/PoliciesTests.cpp @@ -0,0 +1,70 @@ +/* + * 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. + */ + +#include + +#include "gtest/gtest.h" + +#include "TestHelpers.h" +#include "idmap2/Policies.h" + +using android::idmap2::PolicyBitmask; +using android::idmap2::PolicyFlags; + +namespace android::idmap2 { + +TEST(PoliciesTests, PoliciesToBitmasks) { + const Result bitmask1 = PoliciesToBitmask({"system"}, std::cerr); + ASSERT_NE(bitmask1, kResultError); + ASSERT_EQ(bitmask1, PolicyFlags::POLICY_SYSTEM_PARTITION); + + const Result bitmask2 = PoliciesToBitmask({"system", "vendor"}, std::cerr); + ASSERT_NE(bitmask2, kResultError); + ASSERT_EQ(bitmask2, PolicyFlags::POLICY_SYSTEM_PARTITION | PolicyFlags::POLICY_VENDOR_PARTITION); + + const Result bitmask3 = PoliciesToBitmask({"vendor", "system"}, std::cerr); + ASSERT_NE(bitmask3, kResultError); + ASSERT_EQ(bitmask3, PolicyFlags::POLICY_SYSTEM_PARTITION | PolicyFlags::POLICY_VENDOR_PARTITION); + + const Result bitmask4 = + PoliciesToBitmask({"public", "product", "system", "vendor"}, std::cerr); + ASSERT_NE(bitmask4, kResultError); + ASSERT_EQ(bitmask4, PolicyFlags::POLICY_PUBLIC | PolicyFlags::POLICY_PRODUCT_PARTITION | + PolicyFlags::POLICY_SYSTEM_PARTITION | + PolicyFlags::POLICY_VENDOR_PARTITION); + + const Result bitmask5 = + PoliciesToBitmask({"system", "system", "system"}, std::cerr); + ASSERT_NE(bitmask5, kResultError); + ASSERT_EQ(bitmask5, PolicyFlags::POLICY_SYSTEM_PARTITION); + + const Result bitmask6 = PoliciesToBitmask({""}, std::cerr); + ASSERT_EQ(bitmask6, kResultError); + + const Result bitmask7 = PoliciesToBitmask({"foo"}, std::cerr); + ASSERT_EQ(bitmask7, kResultError); + + const Result bitmask8 = PoliciesToBitmask({"system", "foo"}, std::cerr); + ASSERT_EQ(bitmask8, kResultError); + + const Result bitmask9 = PoliciesToBitmask({"system", ""}, std::cerr); + ASSERT_EQ(bitmask9, kResultError); + + const Result bitmask10 = PoliciesToBitmask({"system "}, std::cerr); + ASSERT_EQ(bitmask10, kResultError); +} + +} // namespace android::idmap2 diff --git a/cmds/idmap2/tests/PrettyPrintVisitorTests.cpp b/cmds/idmap2/tests/PrettyPrintVisitorTests.cpp index 7736bc0631319..eaa47cd795333 100644 --- a/cmds/idmap2/tests/PrettyPrintVisitorTests.cpp +++ b/cmds/idmap2/tests/PrettyPrintVisitorTests.cpp @@ -25,6 +25,7 @@ #include "androidfw/Idmap.h" #include "idmap2/Idmap.h" +#include "idmap2/Policies.h" #include "idmap2/PrettyPrintVisitor.h" #include "TestHelpers.h" @@ -32,6 +33,7 @@ using ::testing::NotNull; using android::ApkAssets; +using android::idmap2::PolicyBitmask; namespace android::idmap2 { @@ -46,7 +48,8 @@ TEST(PrettyPrintVisitorTests, CreatePrettyPrintVisitor) { std::stringstream error; std::unique_ptr idmap = - Idmap::FromApkAssets(target_apk_path, *target_apk, overlay_apk_path, *overlay_apk, error); + Idmap::FromApkAssets(target_apk_path, *target_apk, overlay_apk_path, *overlay_apk, + PolicyFlags::POLICY_PUBLIC, /* enforce_overlayable */ true, error); ASSERT_THAT(idmap, NotNull()); std::stringstream stream; diff --git a/cmds/idmap2/tests/RawPrintVisitorTests.cpp b/cmds/idmap2/tests/RawPrintVisitorTests.cpp index 0318cd20a9bbb..b58c61a0c2841 100644 --- a/cmds/idmap2/tests/RawPrintVisitorTests.cpp +++ b/cmds/idmap2/tests/RawPrintVisitorTests.cpp @@ -42,7 +42,8 @@ TEST(RawPrintVisitorTests, CreateRawPrintVisitor) { std::stringstream error; std::unique_ptr idmap = - Idmap::FromApkAssets(target_apk_path, *target_apk, overlay_apk_path, *overlay_apk, error); + Idmap::FromApkAssets(target_apk_path, *target_apk, overlay_apk_path, *overlay_apk, + PolicyFlags::POLICY_PUBLIC, /* enforce_overlayable */ true, error); ASSERT_THAT(idmap, NotNull()); std::stringstream stream; @@ -51,7 +52,7 @@ TEST(RawPrintVisitorTests, CreateRawPrintVisitor) { ASSERT_NE(stream.str().find("00000000: 504d4449 magic\n"), std::string::npos); ASSERT_NE(stream.str().find("00000004: 00000001 version\n"), std::string::npos); - ASSERT_NE(stream.str().find("00000008: f5ad1d1d target crc\n"), std::string::npos); + ASSERT_NE(stream.str().find("00000008: ca2093da target crc\n"), std::string::npos); ASSERT_NE(stream.str().find("0000000c: d470336b overlay crc\n"), std::string::npos); ASSERT_NE(stream.str().find("0000021c: 00000000 0x7f010000 -> 0x7f010000 integer/int1\n"), std::string::npos); diff --git a/cmds/idmap2/tests/data/system-overlay-invalid/AndroidManifest.xml b/cmds/idmap2/tests/data/system-overlay-invalid/AndroidManifest.xml new file mode 100644 index 0000000000000..977cd97f21c3e --- /dev/null +++ b/cmds/idmap2/tests/data/system-overlay-invalid/AndroidManifest.xml @@ -0,0 +1,21 @@ + + + + + diff --git a/cmds/idmap2/tests/data/system-overlay-invalid/build b/cmds/idmap2/tests/data/system-overlay-invalid/build new file mode 100644 index 0000000000000..920e1f8ad6f32 --- /dev/null +++ b/cmds/idmap2/tests/data/system-overlay-invalid/build @@ -0,0 +1,26 @@ +# 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. + +FRAMEWORK_RES_APK=${ANDROID_BUILD_TOP}/prebuilts/sdk/current/public/android.jar + +aapt2 compile --dir res -o compiled.flata + +aapt2 link \ + --no-resource-removal \ + -I "$FRAMEWORK_RES_APK" \ + --manifest AndroidManifest.xml \ + -o system-overlay-invalid.apk \ + compiled.flata + +rm compiled.flata diff --git a/cmds/idmap2/tests/data/system-overlay-invalid/res/values/values.xml b/cmds/idmap2/tests/data/system-overlay-invalid/res/values/values.xml new file mode 100644 index 0000000000000..512770722a4bb --- /dev/null +++ b/cmds/idmap2/tests/data/system-overlay-invalid/res/values/values.xml @@ -0,0 +1,29 @@ + + + + + policy_system + policy_system_vendor + policy_public + + + policy_product + + + not_overlayable + diff --git a/cmds/idmap2/tests/data/system-overlay-invalid/system-overlay-invalid.apk b/cmds/idmap2/tests/data/system-overlay-invalid/system-overlay-invalid.apk new file mode 100644 index 0000000000000000000000000000000000000000..c367f82e21e62625a62ed46846a2cab5483ada89 GIT binary patch literal 1427 zcmWIWW@cev-~d8~hWU&PNI)3KcFapD%Fj&kP0Y(oOD!(ZtH{miop{jeu!BI`|F==I zqguLbHN+hIUS)64$N ztWP%&n`oCcDKPxKTUKW7^rb%pVj99Xu;f1wu4y!@J^p|%CwtvIBil@e64yTq(!;h- zFT3GudeKL@_vD=w{MFu5G}X;Ry{`VVUv)0>cS_I}^|;$FW^Fxa*Lw1Sd&Nr^XSJD& z%a*n1yVrUgZ#flHxYUj>UHwV=oLL8WR(RLFbc<6={r*Dx*}pF<LoTN5>NucFRm;uNzG*t5rN7> z`0-__c`5ltK(iT`6c~2^eNzu)mN29;6az^;hJ1!HAiIbmhar)n638zGk|3E>hFl;o zlOYc%p9qxAWJm#-F-(90G#>>ZFf#L>9h(Q6#zii-eDFTN{I#}J{iCZQn0R|PQ z+u>;q$?YH}BU1>&4kR@hK(+%AQwjut1}FeADC``77+6k$0gwjf5C|U>rXW6)2Idh6 zA4-E2g5*IOK>C3N9#}ny4)A6KDFgC>GQj+YoHIcsAS%F89>NR&E5yja$l9BL?nBMa S0p6@^AQenNSPNo+Ll*$sdn_XW literal 0 HcmV?d00001 diff --git a/cmds/idmap2/tests/data/system-overlay/AndroidManifest.xml b/cmds/idmap2/tests/data/system-overlay/AndroidManifest.xml new file mode 100644 index 0000000000000..8af9064ba64fe --- /dev/null +++ b/cmds/idmap2/tests/data/system-overlay/AndroidManifest.xml @@ -0,0 +1,21 @@ + + + + + diff --git a/cmds/idmap2/tests/data/system-overlay/build b/cmds/idmap2/tests/data/system-overlay/build new file mode 100644 index 0000000000000..be0d2390f535f --- /dev/null +++ b/cmds/idmap2/tests/data/system-overlay/build @@ -0,0 +1,26 @@ +# 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. + +FRAMEWORK_RES_APK=${ANDROID_BUILD_TOP}/prebuilts/sdk/current/public/android.jar + +aapt2 compile --dir res -o compiled.flata + +aapt2 link \ + --no-resource-removal \ + -I "$FRAMEWORK_RES_APK" \ + --manifest AndroidManifest.xml \ + -o system-overlay.apk \ + compiled.flata + +rm compiled.flata diff --git a/cmds/idmap2/tests/data/system-overlay/res/values/values.xml b/cmds/idmap2/tests/data/system-overlay/res/values/values.xml new file mode 100644 index 0000000000000..6aaa0b02639ed --- /dev/null +++ b/cmds/idmap2/tests/data/system-overlay/res/values/values.xml @@ -0,0 +1,22 @@ + + + + + policy_system + policy_system_vendor + policy_public + diff --git a/cmds/idmap2/tests/data/system-overlay/system-overlay.apk b/cmds/idmap2/tests/data/system-overlay/system-overlay.apk new file mode 100644 index 0000000000000000000000000000000000000000..90f30eb68c1576a4f13b08a9069c9025d86b4f99 GIT binary patch literal 1283 zcmWIWW@cev-~d8~hWU&PNI)3KcFapD%Fj&kP0Y(oOD!(ZtH{miop{iT*-)VEzsa>c z(S=F;T&@=ux8Ge|akaT!PUPU`q}96HcBgLV_SBlA zvlVkS6Gc|^ub6R2N96jCLJMm}yT?~r&IrVP@;3j(rMcVv{EXk1jrabrQ-306XS90n zLAf)c_Ukq#FXEnGcz5T1jhR=&OShR!+_lEtNG)F~m|xsuOX8+@^Ro9L@MS^%(LQ%7E-5h8%`OhDsp6 z7)XL-QWV_*&faZw+C=K!_DEtk;G{V;k2qps?I9!StG8yt1(!uIz8(&OJ0zemm z+=Y}TpzdL03SpRlB$ohWI{-1I0G7spf7`@j>nY@u4&@?Lzn4I|M8QiO r!DW1$eWvfmAR7VJ(m@0Ovmd-3tsT literal 0 HcmV?d00001 diff --git a/cmds/idmap2/tests/data/target/res/values/overlayable.xml b/cmds/idmap2/tests/data/target/res/values/overlayable.xml new file mode 100644 index 0000000000000..de19e6fcd0221 --- /dev/null +++ b/cmds/idmap2/tests/data/target/res/values/overlayable.xml @@ -0,0 +1,48 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/cmds/idmap2/tests/data/target/res/values/values.xml b/cmds/idmap2/tests/data/target/res/values/values.xml index 56bf0d60021ab..ef9012e493000 100644 --- a/cmds/idmap2/tests/data/target/res/values/values.xml +++ b/cmds/idmap2/tests/data/target/res/values/values.xml @@ -25,4 +25,12 @@ y z 1 + + + not_overlayable + + policy_system + policy_system_vendor + policy_product + policy_public diff --git a/cmds/idmap2/tests/data/target/target.apk b/cmds/idmap2/tests/data/target/target.apk index 18ecc276caae7b9be964bf4eeac8844b5801502e..9a6220dbe3caa659e4a37b9345c328e3e3252323 100644 GIT binary patch delta 952 zcmb`Gzi-n(6vyBBcbeEvq^Jx8sDwZ)rLeFdNEu+^NBAXJkV4`bsUnRl+o`ZRI6@s@ zK#-mo=mHA^0}>JoY|ID&Ls^(W`~koQ5=eZXbA>Xnamh#Tz3=&*?>*oBtiEe_r;qCf zk%J!%?~9T5S%+B_-;Tn(ms2m-Cx{r2bc(|y8pJ`E?Cr!!KtviVH0#`r=R={nr1|EV(;4u)Q1G*p}-|t6hJ6a0kUMK5xdm(w=V$_=t zvi4#e%?*N-Tz5klbTRQKPO>Bo7ih9xR}YMZ_EOlNi(ozN@R zb)^^2aBR!W+If;!D61!w#n~8VC`+!2mBo=7&nrv5ihX&{N~ZZ7@%p^7p*hA1Hg=vu zO3?kYg^g%HF$L_S>QAfs`o{l8ZfaGUMJAe1>;qnoe&hmZzyuB`feBCs72pC7aDE-R z+(BN$+N5y@ShF{2yi3*$Xt+{k?jjG|BDvdc@uia2?8Ak9ZXb02x?NH@GdrNX`FQSP V14+yzXa62wDzfG5v1Yk-^b4@#tRVmZ delta 441 zcmY+A&q~8U5XNVB^G}K*2BX?iH7%n2&*pp(wRDzqBYhwOB8)s5qGcD8d7jo9xFpi%CX! z^DD+xOp{M==xjc~a)){H1s)boMg|53FrIvn!x%2Sc?X9E%j6>*25=F$LYB#Qc|y1l Q)=swOHDY5B1scNu01D758vp= VERSION_CODES.Q) { + // Always enforce policies for overlays targeting Q+. + return true; + } + + if (ai.isVendor() && !VENDOR_IS_Q_OR_LATER) { + // If the overlay is on a pre-Q vendor partition, do not enforce overlayable + // restrictions on this overlay because the pre-Q platform has no understanding of + // overlayable. + return false; + } + + // Do not enforce overlayable restrictions on pre-Q overlays signed with the + // platform signature. + return !ai.isSignedWithPlatformKey(); + } + + /** + * Retrieves a bitmask for idmap2 that represents the policies the specified overlay fulfills. + * @throws SecurityException if the overlay is not allowed to overlay any resource + */ + private int determineFulfilledPolicies(@NonNull final PackageInfo overlayPackage) + throws SecurityException { + final ApplicationInfo ai = overlayPackage.applicationInfo; + final boolean overlayIsQOrLater = ai.targetSdkVersion >= VERSION_CODES.Q; + + int fulfilledPolicies = 0; + + // TODO(b/119402606) : Add signature policy + + // Vendor partition (/vendor) + if (ai.isVendor()) { + if (overlayIsQOrLater) { + fulfilledPolicies |= IIdmap2.POLICY_VENDOR_PARTITION; + } else if (VENDOR_IS_Q_OR_LATER) { + throw new SecurityException("Overlay must target Q sdk or higher"); + } + } + + // Product partition (/product) + if (ai.isProduct()) { + if (overlayIsQOrLater) { + fulfilledPolicies |= IIdmap2.POLICY_PRODUCT_PARTITION; + } else { + throw new SecurityException("Overlay must target Q sdk or higher"); + } + } + + // System partition (/system) + if (ai.isSystemApp()) { + if (overlayIsQOrLater) { + fulfilledPolicies |= IIdmap2.POLICY_SYSTEM_PARTITION; + } else { + throw new SecurityException("Overlay must target Q sdk or higher"); + } + } + + // All overlays can overlay resources with the public policy + return fulfilledPolicies | IIdmap2.POLICY_PUBLIC; + } }