diff options
Diffstat (limited to 'init/selinux.cpp')
-rw-r--r-- | init/selinux.cpp | 234 |
1 files changed, 85 insertions, 149 deletions
diff --git a/init/selinux.cpp b/init/selinux.cpp index 42d302324..5a0255acd 100644 --- a/init/selinux.cpp +++ b/init/selinux.cpp @@ -45,7 +45,7 @@ // 2) If these hashes do not match, then either /system or /system_ext or /product (or some of them) // have been updated out of sync with /vendor (or /odm if it is present) and the init needs to // compile the SEPolicy. /system contains the SEPolicy compiler, secilc, and it is used by the -// OpenSplitPolicy() function below to compile the SEPolicy to a temp directory and load it. +// LoadSplitPolicy() function below to compile the SEPolicy to a temp directory and load it. // That function contains even more documentation with the specific implementation details of how // the SEPolicy is compiled if needed. @@ -63,7 +63,6 @@ #include <android-base/file.h> #include <android-base/logging.h> #include <android-base/parseint.h> -#include <android-base/result.h> #include <android-base/strings.h> #include <android-base/unique_fd.h> #include <fs_avb/fs_avb.h> @@ -75,7 +74,6 @@ #include "block_dev_initializer.h" #include "debug_ramdisk.h" #include "reboot_utils.h" -#include "snapuserd_transition.h" #include "util.h" using namespace std::string_literals; @@ -93,7 +91,7 @@ namespace { enum EnforcingStatus { SELINUX_PERMISSIVE, SELINUX_ENFORCING }; -EnforcingStatus StatusFromProperty() { +EnforcingStatus StatusFromCmdline() { EnforcingStatus status = SELINUX_ENFORCING; ImportKernelCmdline([&](const std::string& key, const std::string& value) { @@ -102,20 +100,12 @@ EnforcingStatus StatusFromProperty() { } }); - if (status == SELINUX_ENFORCING) { - ImportBootconfig([&](const std::string& key, const std::string& value) { - if (key == "androidboot.selinux" && value == "permissive") { - status = SELINUX_PERMISSIVE; - } - }); - } - return status; } bool IsEnforcing() { if (ALLOW_PERMISSIVE_SELINUX) { - return StatusFromProperty() == SELINUX_ENFORCING; + return StatusFromCmdline() == SELINUX_ENFORCING; } return true; } @@ -223,8 +213,8 @@ bool ReadFirstLine(const char* file, std::string* line) { return true; } -Result<std::string> FindPrecompiledSplitPolicy() { - std::string precompiled_sepolicy; +bool FindPrecompiledSplitPolicy(std::string* file) { + file->clear(); // If there is an odm partition, precompiled_sepolicy will be in // odm/etc/selinux. Otherwise it will be in vendor/etc/selinux. static constexpr const char vendor_precompiled_sepolicy[] = @@ -232,49 +222,62 @@ Result<std::string> FindPrecompiledSplitPolicy() { static constexpr const char odm_precompiled_sepolicy[] = "/odm/etc/selinux/precompiled_sepolicy"; if (access(odm_precompiled_sepolicy, R_OK) == 0) { - precompiled_sepolicy = odm_precompiled_sepolicy; + *file = odm_precompiled_sepolicy; } else if (access(vendor_precompiled_sepolicy, R_OK) == 0) { - precompiled_sepolicy = vendor_precompiled_sepolicy; + *file = vendor_precompiled_sepolicy; } else { - return ErrnoError() << "No precompiled sepolicy at " << vendor_precompiled_sepolicy; + PLOG(INFO) << "No precompiled sepolicy"; + return false; } - - // Use precompiled sepolicy only when all corresponding hashes are equal. - std::vector<std::pair<std::string, std::string>> sepolicy_hashes{ - {"/system/etc/selinux/plat_sepolicy_and_mapping.sha256", - precompiled_sepolicy + ".plat_sepolicy_and_mapping.sha256"}, - {"/system_ext/etc/selinux/system_ext_sepolicy_and_mapping.sha256", - precompiled_sepolicy + ".system_ext_sepolicy_and_mapping.sha256"}, - {"/product/etc/selinux/product_sepolicy_and_mapping.sha256", - precompiled_sepolicy + ".product_sepolicy_and_mapping.sha256"}, - }; - - for (const auto& [actual_id_path, precompiled_id_path] : sepolicy_hashes) { - // Both of them should exist or both of them shouldn't exist. - if (access(actual_id_path.c_str(), R_OK) != 0) { - if (access(precompiled_id_path.c_str(), R_OK) == 0) { - return Error() << precompiled_id_path << " exists but " << actual_id_path - << " doesn't"; - } - continue; - } - - std::string actual_id; - if (!ReadFirstLine(actual_id_path.c_str(), &actual_id)) { - return ErrnoError() << "Failed to read " << actual_id_path; - } - - std::string precompiled_id; - if (!ReadFirstLine(precompiled_id_path.c_str(), &precompiled_id)) { - return ErrnoError() << "Failed to read " << precompiled_id_path; - } - - if (actual_id.empty() || actual_id != precompiled_id) { - return Error() << actual_id_path << " and " << precompiled_id_path << " differ"; - } + std::string actual_plat_id; + if (!ReadFirstLine("/system/etc/selinux/plat_sepolicy_and_mapping.sha256", &actual_plat_id)) { + PLOG(INFO) << "Failed to read " + "/system/etc/selinux/plat_sepolicy_and_mapping.sha256"; + return false; + } + std::string actual_system_ext_id; + if (!ReadFirstLine("/system_ext/etc/selinux/system_ext_sepolicy_and_mapping.sha256", + &actual_system_ext_id)) { + PLOG(INFO) << "Failed to read " + "/system_ext/etc/selinux/system_ext_sepolicy_and_mapping.sha256"; + return false; + } + std::string actual_product_id; + if (!ReadFirstLine("/product/etc/selinux/product_sepolicy_and_mapping.sha256", + &actual_product_id)) { + PLOG(INFO) << "Failed to read " + "/product/etc/selinux/product_sepolicy_and_mapping.sha256"; + return false; } - return precompiled_sepolicy; + std::string precompiled_plat_id; + std::string precompiled_plat_sha256 = *file + ".plat_sepolicy_and_mapping.sha256"; + if (!ReadFirstLine(precompiled_plat_sha256.c_str(), &precompiled_plat_id)) { + PLOG(INFO) << "Failed to read " << precompiled_plat_sha256; + file->clear(); + return false; + } + std::string precompiled_system_ext_id; + std::string precompiled_system_ext_sha256 = *file + ".system_ext_sepolicy_and_mapping.sha256"; + if (!ReadFirstLine(precompiled_system_ext_sha256.c_str(), &precompiled_system_ext_id)) { + PLOG(INFO) << "Failed to read " << precompiled_system_ext_sha256; + file->clear(); + return false; + } + std::string precompiled_product_id; + std::string precompiled_product_sha256 = *file + ".product_sepolicy_and_mapping.sha256"; + if (!ReadFirstLine(precompiled_product_sha256.c_str(), &precompiled_product_id)) { + PLOG(INFO) << "Failed to read " << precompiled_product_sha256; + file->clear(); + return false; + } + if (actual_plat_id.empty() || actual_plat_id != precompiled_plat_id || + actual_system_ext_id.empty() || actual_system_ext_id != precompiled_system_ext_id || + actual_product_id.empty() || actual_product_id != precompiled_product_id) { + file->clear(); + return false; + } + return true; } bool GetVendorMappingVersion(std::string* plat_vers) { @@ -295,12 +298,7 @@ bool IsSplitPolicyDevice() { return access(plat_policy_cil_file, R_OK) != -1; } -struct PolicyFile { - unique_fd fd; - std::string path; -}; - -bool OpenSplitPolicy(PolicyFile* policy_file) { +bool LoadSplitPolicy() { // IMPLEMENTATION NOTE: Split policy consists of three CIL files: // * platform -- policy needed due to logic contained in the system image, // * non-platform -- policy needed due to logic contained in the vendor image, @@ -321,18 +319,17 @@ bool OpenSplitPolicy(PolicyFile* policy_file) { // Load precompiled policy from vendor image, if a matching policy is found there. The policy // must match the platform policy on the system image. + std::string precompiled_sepolicy_file; // use_userdebug_policy requires compiling sepolicy with userdebug_plat_sepolicy.cil. // Thus it cannot use the precompiled policy from vendor image. - if (!use_userdebug_policy) { - if (auto res = FindPrecompiledSplitPolicy(); res.ok()) { - unique_fd fd(open(res->c_str(), O_RDONLY | O_CLOEXEC | O_BINARY)); - if (fd != -1) { - policy_file->fd = std::move(fd); - policy_file->path = std::move(*res); - return true; + if (!use_userdebug_policy && FindPrecompiledSplitPolicy(&precompiled_sepolicy_file)) { + unique_fd fd(open(precompiled_sepolicy_file.c_str(), O_RDONLY | O_CLOEXEC | O_BINARY)); + if (fd != -1) { + if (selinux_android_load_policy_from_fd(fd, precompiled_sepolicy_file.c_str()) < 0) { + LOG(ERROR) << "Failed to load SELinux policy from " << precompiled_sepolicy_file; + return false; } - } else { - LOG(INFO) << res.error(); + return true; } } // No suitable precompiled policy could be loaded @@ -372,12 +369,6 @@ bool OpenSplitPolicy(PolicyFile* policy_file) { system_ext_mapping_file.clear(); } - std::string system_ext_compat_cil_file("/system_ext/etc/selinux/mapping/" + vend_plat_vers + - ".compat.cil"); - if (access(system_ext_compat_cil_file.c_str(), F_OK) == -1) { - system_ext_compat_cil_file.clear(); - } - std::string product_policy_cil_file("/product/etc/selinux/product_sepolicy.cil"); if (access(product_policy_cil_file.c_str(), F_OK) == -1) { product_policy_cil_file.clear(); @@ -432,9 +423,6 @@ bool OpenSplitPolicy(PolicyFile* policy_file) { if (!system_ext_mapping_file.empty()) { compile_args.push_back(system_ext_mapping_file.c_str()); } - if (!system_ext_compat_cil_file.empty()) { - compile_args.push_back(system_ext_compat_cil_file.c_str()); - } if (!product_policy_cil_file.empty()) { compile_args.push_back(product_policy_cil_file.c_str()); } @@ -458,39 +446,34 @@ bool OpenSplitPolicy(PolicyFile* policy_file) { } unlink(compiled_sepolicy); - policy_file->fd = std::move(compiled_sepolicy_fd); - policy_file->path = compiled_sepolicy; + LOG(INFO) << "Loading compiled SELinux policy"; + if (selinux_android_load_policy_from_fd(compiled_sepolicy_fd, compiled_sepolicy) < 0) { + LOG(ERROR) << "Failed to load SELinux policy from " << compiled_sepolicy; + return false; + } + return true; } -bool OpenMonolithicPolicy(PolicyFile* policy_file) { - static constexpr char kSepolicyFile[] = "/sepolicy"; - - LOG(VERBOSE) << "Opening SELinux policy from monolithic file"; - policy_file->fd.reset(open(kSepolicyFile, O_RDONLY | O_CLOEXEC | O_NOFOLLOW)); - if (policy_file->fd < 0) { - PLOG(ERROR) << "Failed to open monolithic SELinux policy"; +bool LoadMonolithicPolicy() { + LOG(VERBOSE) << "Loading SELinux policy from monolithic file"; + if (selinux_android_load_policy() < 0) { + PLOG(ERROR) << "Failed to load monolithic SELinux policy"; return false; } - policy_file->path = kSepolicyFile; return true; } -void ReadPolicy(std::string* policy) { - PolicyFile policy_file; +bool LoadPolicy() { + return IsSplitPolicyDevice() ? LoadSplitPolicy() : LoadMonolithicPolicy(); +} - bool ok = IsSplitPolicyDevice() ? OpenSplitPolicy(&policy_file) - : OpenMonolithicPolicy(&policy_file); - if (!ok) { - LOG(FATAL) << "Unable to open SELinux policy"; +void SelinuxInitialize() { + LOG(INFO) << "Loading SELinux policy"; + if (!LoadPolicy()) { + LOG(FATAL) << "Unable to load SELinux policy"; } - if (!android::base::ReadFdToString(policy_file.fd, policy)) { - PLOG(FATAL) << "Failed to read policy file: " << policy_file.path; - } -} - -void SelinuxSetEnforcement() { bool kernel_enforcing = (security_getenforce() == 1); bool is_enforcing = IsEnforcing(); if (kernel_enforcing != is_enforcing) { @@ -551,7 +534,6 @@ void SelinuxRestoreContext() { selinux_android_restorecon("/dev/__properties__", 0); selinux_android_restorecon("/dev/block", SELINUX_ANDROID_RESTORECON_RECURSE); - selinux_android_restorecon("/dev/dm-user", SELINUX_ANDROID_RESTORECON_RECURSE); selinux_android_restorecon("/dev/device-mapper", 0); selinux_android_restorecon("/apex", 0); @@ -667,7 +649,7 @@ void MountMissingSystemPartitions() { extra_fstab.emplace_back(std::move(entry)); } - SkipMountingPartitions(&extra_fstab, true /* verbose */); + SkipMountingPartitions(&extra_fstab); if (extra_fstab.empty()) { return; } @@ -687,30 +669,6 @@ void MountMissingSystemPartitions() { } } -static void LoadSelinuxPolicy(std::string& policy) { - LOG(INFO) << "Loading SELinux policy"; - - set_selinuxmnt("/sys/fs/selinux"); - if (security_load_policy(policy.data(), policy.size()) < 0) { - PLOG(FATAL) << "SELinux: Could not load policy"; - } -} - -// The SELinux setup process is carefully orchestrated around snapuserd. Policy -// must be loaded off dynamic partitions, and during an OTA, those partitions -// cannot be read without snapuserd. But, with kernel-privileged snapuserd -// running, loading the policy will immediately trigger audits. -// -// We use a five-step process to address this: -// (1) Read the policy into a string, with snapuserd running. -// (2) Rewrite the snapshot device-mapper tables, to generate new dm-user -// devices and to flush I/O. -// (3) Kill snapuserd, which no longer has any dm-user devices to attach to. -// (4) Load the sepolicy and issue critical restorecons in /dev, carefully -// avoiding anything that would read from /system. -// (5) Re-launch snapuserd and attach it to the dm-user devices from step (2). -// -// After this sequence, it is safe to enable enforcing mode and continue booting. int SetupSelinux(char** argv) { SetStdioToDevNull(argv); InitKernelLogging(argv); @@ -723,31 +681,9 @@ int SetupSelinux(char** argv) { MountMissingSystemPartitions(); + // Set up SELinux, loading the SELinux policy. SelinuxSetupKernelLogging(); - - LOG(INFO) << "Opening SELinux policy"; - - // Read the policy before potentially killing snapuserd. - std::string policy; - ReadPolicy(&policy); - - auto snapuserd_helper = SnapuserdSelinuxHelper::CreateIfNeeded(); - if (snapuserd_helper) { - // Kill the old snapused to avoid audit messages. After this we cannot - // read from /system (or other dynamic partitions) until we call - // FinishTransition(). - snapuserd_helper->StartTransition(); - } - - LoadSelinuxPolicy(policy); - - if (snapuserd_helper) { - // Before enforcing, finish the pending snapuserd transition. - snapuserd_helper->FinishTransition(); - snapuserd_helper = nullptr; - } - - SelinuxSetEnforcement(); + SelinuxInitialize(); // We're in the kernel domain and want to transition to the init domain. File systems that // store SELabels in their xattrs, such as ext4 do not need an explicit restorecon here, |