diff options
author | Sandro Montanari <sandrom@google.com> | 2022-02-10 10:01:31 +0000 |
---|---|---|
committer | Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com> | 2022-02-10 10:01:31 +0000 |
commit | 4e270ab15ff979a92c5c7686d5ff26d9c159c8f8 (patch) | |
tree | df8e09fcc5fde345b8e4bb9cb9181015660cfb8c | |
parent | a3424dd0ea57444a1687df9e392ab002f8f97edb (diff) | |
parent | 249aa0fd0952e571f6e85ff766ee101ff7f0c373 (diff) | |
download | apex-4e270ab15ff979a92c5c7686d5ff26d9c159c8f8.tar.gz |
Merge "Copy sepolicy apex data to /metadata/sepolicy/staged" am: 249aa0fd09
Original change: https://android-review.googlesource.com/c/platform/system/apex/+/1965022
Change-Id: I4e6906a965203c1dde4f867ab9b593644c75ef44
-rw-r--r-- | apexd/Android.bp | 1 | ||||
-rw-r--r-- | apexd/apex_constants.h | 3 | ||||
-rw-r--r-- | apexd/apexd.cpp | 89 | ||||
-rw-r--r-- | apexd/apexd.h | 2 | ||||
-rw-r--r-- | apexd/apexd_test.cpp | 38 |
5 files changed, 127 insertions, 6 deletions
diff --git a/apexd/Android.bp b/apexd/Android.bp index 8d6cd8fb..d5149c0f 100644 --- a/apexd/Android.bp +++ b/apexd/Android.bp @@ -466,6 +466,7 @@ cc_test { ":com.android.apex.compressed.v1_original", ":com.android.apex.compressed.v2", ":com.android.apex.compressed.v2_original", + ":com.android.sepolicy", ":gen_manifest_mismatch_compressed_apex_v2", "apexd_testdata/com.android.apex.test_package.avbpubkey", "apexd_testdata/com.android.apex.compressed.avbpubkey", diff --git a/apexd/apex_constants.h b/apexd/apex_constants.h index 5f141577..edb51c38 100644 --- a/apexd/apex_constants.h +++ b/apexd/apex_constants.h @@ -75,6 +75,9 @@ static constexpr const char* kVmPayloadMetadataPartitionProp = "apexd.payload_metadata.path"; static constexpr const std::chrono::seconds kBlockApexWaitTime(10); +static constexpr const char* kMetadataSepolicyStagedDir = + "/metadata/sepolicy/staged"; + // Banned APEX names static const std::unordered_set<std::string> kBannedApexName = { kApexSharedLibsSubDir, // To avoid conflicts with predefined diff --git a/apexd/apexd.cpp b/apexd/apexd.cpp index fd7f9fd8..8cb0f07f 100644 --- a/apexd/apexd.cpp +++ b/apexd/apexd.cpp @@ -721,6 +721,86 @@ Result<void> Unmount(const MountedApexData& data, bool deferred) { namespace { +// TODO(b/218672709): get the ro.build.version.sdk version of the device. +const auto kSepolicyLevel = std::to_string(__ANDROID_API_T__); +const auto kVersionedSepolicyZip = "SEPolicy-" + kSepolicyLevel + ".zip"; +const auto kVersionedSepolicySig = "SEPolicy-" + kSepolicyLevel + ".zip.sig"; +const auto kVersionedSepolicyFsv = + "SEPolicy-" + kSepolicyLevel + ".zip.fsv_sig"; + +const auto kSepolicyZip = "SEPolicy.zip"; +const auto kSepolicySig = "SEPolicy.zip.sig"; +const auto kSepolicyFsv = "SEPolicy.zip.fsv_sig"; + +Result<void> CopySepolicyToMetadata(const std::string& mount_point) { + LOG(DEBUG) << "Copying SEPolicy files to /metadata/sepolicy/staged."; + const auto policy_dir = mount_point + "/etc"; + + // Find SEPolicy zip and signature files. + std::optional<std::string> sepolicy_zip; + std::optional<std::string> sepolicy_sig; + std::optional<std::string> sepolicy_fsv; + auto status = + WalkDir(policy_dir, [&sepolicy_zip, &sepolicy_sig, &sepolicy_fsv]( + const std::filesystem::directory_entry& entry) { + if (!entry.is_regular_file()) { + return; + } + const auto& path = entry.path().string(); + if (base::EndsWith(path, kVersionedSepolicyZip)) { + sepolicy_zip = path; + } else if (base::EndsWith(path, kVersionedSepolicySig)) { + sepolicy_sig = path; + } else if (base::EndsWith(path, kVersionedSepolicyFsv)) { + sepolicy_fsv = path; + } + }); + if (!status.ok()) { + return status.error(); + } + if (sepolicy_zip->empty() || sepolicy_sig->empty() || sepolicy_fsv->empty()) { + return Error() << "SEPolicy files not found."; + } + LOG(INFO) << "SEPolicy files found."; + + // Set up staging directory. + std::error_code ec; + const auto staged_dir = + std::string(gConfig->metadata_sepolicy_staged_dir) + "/"; + status = CreateDirIfNeeded(staged_dir, 0755); + if (!status.ok()) { + return status.error(); + } + + // Clean up after myself. + auto scope_guard = android::base::make_scope_guard([&staged_dir]() { + std::error_code ec; + std::filesystem::remove_all(staged_dir, ec); + if (ec) { + LOG(WARNING) << "Failed to clear " << staged_dir << ": " << ec.message(); + } + }); + + // Copy files to staged folder. + std::map<std::string, std::string> from_to = { + {*sepolicy_zip, staged_dir + kSepolicyZip}, + {*sepolicy_sig, staged_dir + kSepolicySig}, + {*sepolicy_fsv, staged_dir + kSepolicyFsv}}; + for (const auto& [from, to] : from_to) { + std::filesystem::copy_file( + from, to, std::filesystem::copy_options::overwrite_existing, ec); + if (ec) { + return Error() << "Failed to copy " << from << " to " << to << ": " + << ec.message(); + } + } + + // TODO(b/218672709): if the kernel supports fs-verity, apply it after copy. + + scope_guard.Disable(); + return {}; +} + template <typename VerifyFn> Result<void> RunVerifyFnInsideTempMount(const ApexFile& apex, const VerifyFn& verify_fn, @@ -828,10 +908,13 @@ Result<void> VerifyPackageStagedInstall(const ApexFile& apex_file) { return verify_package_boot_status; } - constexpr const auto kSuccessFn = [](const std::string& /*mount_point*/) { + const auto validate_fn = [&apex_file](const std::string& mount_point) { + if (apex_file.GetManifest().name() == "com.android.sepolicy.apex") { + return CopySepolicyToMetadata(mount_point); + } return Result<void>{}; }; - return RunVerifyFnInsideTempMount(apex_file, kSuccessFn, false); + return RunVerifyFnInsideTempMount(apex_file, validate_fn, false); } template <typename VerifyApexFn> @@ -1591,6 +1674,7 @@ Result<ApexFile> GetActivePackage(const std::string& packageName) { * Returns without error only if session was successfully aborted. **/ Result<void> AbortStagedSession(int session_id) { + // TODO(b/218672709): Delete staged SEPolicy file if the session is aborted. auto session = ApexSession::GetSession(session_id); if (!session.ok()) { return Error() << "No session found with id " << session_id; @@ -2857,6 +2941,7 @@ void OnStart() { const auto& all_apex = instance.AllApexFilesByName(); // There can be multiple APEX packages with package name X. Determine which // one to activate. + // TODO(b/218672709): skip activation of sepolicy APEX during boot. auto activation_list = SelectApexForActivation(all_apex, instance); // Process compressed APEX, if any diff --git a/apexd/apexd.h b/apexd/apexd.h index d08c9ac0..cdcc193a 100644 --- a/apexd/apexd.h +++ b/apexd/apexd.h @@ -48,6 +48,7 @@ struct ApexdConfig { const char* ota_reserved_dir; const char* apex_hash_tree_dir; const char* staged_session_dir; + const char* metadata_sepolicy_staged_dir; // Overrides the path to the "metadata" partition which is by default // /dev/block/by-name/payload-metadata It should be a path pointing the first // partition of the VM payload disk. So, realpath() of this path is checked if @@ -65,6 +66,7 @@ static const ApexdConfig kDefaultConfig = { kOtaReservedDir, kApexHashTreeDir, kStagedSessionsDir, + kMetadataSepolicyStagedDir, kVmPayloadMetadataPartitionProp, "u:object_r:staging_data_file", }; diff --git a/apexd/apexd_test.cpp b/apexd/apexd_test.cpp index a101bbb8..784a9397 100644 --- a/apexd/apexd_test.cpp +++ b/apexd/apexd_test.cpp @@ -139,13 +139,19 @@ class ApexdUnitTest : public ::testing::Test { ota_reserved_dir_ = StringPrintf("%s/ota-reserved", td_.path); hash_tree_dir_ = StringPrintf("%s/apex-hash-tree", td_.path); staged_session_dir_ = StringPrintf("%s/staged-session-dir", td_.path); + metadata_sepolicy_dir_ = StringPrintf("%s/metadata-sepolicy-dir", td_.path); vm_payload_disk_ = StringPrintf("%s/vm-payload", td_.path); - config_ = {kTestApexdStatusSysprop, {built_in_dir_}, - data_dir_.c_str(), decompression_dir_.c_str(), - ota_reserved_dir_.c_str(), hash_tree_dir_.c_str(), - staged_session_dir_.c_str(), kTestVmPayloadMetadataPartitionProp, + config_ = {kTestApexdStatusSysprop, + {built_in_dir_}, + data_dir_.c_str(), + decompression_dir_.c_str(), + ota_reserved_dir_.c_str(), + hash_tree_dir_.c_str(), + staged_session_dir_.c_str(), + metadata_sepolicy_dir_.c_str(), + kTestVmPayloadMetadataPartitionProp, kTestActiveApexSelinuxCtx}; } @@ -158,6 +164,7 @@ class ApexdUnitTest : public ::testing::Test { return StringPrintf("%s/session_%d", staged_session_dir_.c_str(), session_id); } + const std::string& GetMetadataSepolicyDir() { return metadata_sepolicy_dir_; } std::string GetRootDigest(const ApexFile& apex) { if (apex.IsCompressed()) { @@ -240,6 +247,7 @@ class ApexdUnitTest : public ::testing::Test { ASSERT_EQ(mkdir(ota_reserved_dir_.c_str(), 0755), 0); ASSERT_EQ(mkdir(hash_tree_dir_.c_str(), 0755), 0); ASSERT_EQ(mkdir(staged_session_dir_.c_str(), 0755), 0); + ASSERT_EQ(mkdir(metadata_sepolicy_dir_.c_str(), 0755), 0); DeleteDirContent(ApexSession::GetSessionsDir()); } @@ -276,6 +284,7 @@ class ApexdUnitTest : public ::testing::Test { std::string vm_payload_disk_; std::string vm_payload_metadata_path_; std::string staged_session_dir_; + std::string metadata_sepolicy_dir_; ApexdConfig config_; std::vector<loop::LoopbackDeviceUniqueFd> loop_devices_; // to be cleaned up }; @@ -4269,6 +4278,27 @@ TEST_F(ApexdMountTest, AddBlockApexFailsWithCompressedDuplicate) { "duplicate of com.android.apex.compressed found")))); } +TEST_F(ApexdMountTest, CopySepolicyToMetadata) { + std::string file_path = AddPreInstalledApex("com.android.sepolicy.apex"); + ASSERT_THAT( + ApexFileRepository::GetInstance().AddPreInstalledApex({GetBuiltInDir()}), + Ok()); + ASSERT_THAT(ActivatePackage(file_path), Ok()); + UnmountOnTearDown(file_path); + ASSERT_THAT(CreateStagedSession("com.android.sepolicy.apex", 666), Ok()); + + ASSERT_THAT( + SubmitStagedSession(666, {}, /* has_rollback_enabled= */ false, + /* is_rollback= */ false, /* rollback_id= */ -1), + Ok()); + + auto metadata_dir = GetMetadataSepolicyDir(); + ASSERT_THAT(PathExists(metadata_dir + "/SEPolicy.zip"), HasValue(true)); + ASSERT_THAT(PathExists(metadata_dir + "/SEPolicy.zip.sig"), HasValue(true)); + ASSERT_THAT(PathExists(metadata_dir + "/SEPolicy.zip.fsv_sig"), + HasValue(true)); +} + class ApexActivationFailureTests : public ApexdMountTest {}; TEST_F(ApexActivationFailureTests, BuildFingerprintDifferent) { |