Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 50f03fd5 authored by Thiébaud Weksteen's avatar Thiébaud Weksteen
Browse files

Revert "Use Apex sepolicy if it's available"

This reverts commit baeece6d.

Test: boot aosp_cf_x86_64_phone-userdebug
Bug: 297794885
Change-Id: I0515bc30eba42589c407deb587684b4da011aead
parent 62051237
Loading
Loading
Loading
Loading
+0 −3
Original line number Diff line number Diff line
@@ -174,7 +174,6 @@ libinit_cc_defaults {
        "libprotobuf-cpp-lite",
        "libpropertyinfoserializer",
        "libpropertyinfoparser",
        "libsigningutils",
        "libsnapshot_cow",
        "libsnapshot_init",
        "libxml2",
@@ -183,7 +182,6 @@ libinit_cc_defaults {
    ],
    shared_libs: [
        "libbase",
        "libcrypto",
        "libcutils",
        "libdl",
        "libext4_utils",
@@ -199,7 +197,6 @@ libinit_cc_defaults {
        "libselinux",
        "libunwindstack",
        "libutils",
        "libziparchive",
    ],
    header_libs: ["bionic_libc_platform_headers"],
    bootstrap: true,
+0 −4
Original line number Diff line number Diff line
@@ -1247,9 +1247,6 @@ void CreateSerializedPropertyInfo() {
        // Don't check for failure here, since we don't always have all of these partitions.
        // E.g. In case of recovery, the vendor partition will not have mounted and we
        // still need the system / platform properties to function.
        if (access("/dev/selinux/apex_property_contexts", R_OK) != -1) {
            LoadPropertyInfoFromFile("/dev/selinux/apex_property_contexts", &property_infos);
        }
        if (access("/system_ext/etc/selinux/system_ext_property_contexts", R_OK) != -1) {
            LoadPropertyInfoFromFile("/system_ext/etc/selinux/system_ext_property_contexts",
                                     &property_infos);
@@ -1273,7 +1270,6 @@ void CreateSerializedPropertyInfo() {
        LoadPropertyInfoFromFile("/vendor_property_contexts", &property_infos);
        LoadPropertyInfoFromFile("/product_property_contexts", &property_infos);
        LoadPropertyInfoFromFile("/odm_property_contexts", &property_infos);
        LoadPropertyInfoFromFile("/dev/selinux/apex_property_contexts", &property_infos);
    }

    auto serialized_contexts = std::string();
+16 −231
Original line number Diff line number Diff line
@@ -26,29 +26,26 @@
// The monolithic policy variant is for legacy non-treble devices that contain a single SEPolicy
// file located at /sepolicy and is directly loaded into the kernel SELinux subsystem.

// The split policy is for supporting treble devices and updateable apexes.  It splits the SEPolicy
// across files on /system/etc/selinux (the 'plat' portion of the policy), /vendor/etc/selinux
// (the 'vendor' portion of the policy), /system_ext/etc/selinux, /product/etc/selinux,
// /odm/etc/selinux, and /dev/selinux (the apex portion of policy).  This is necessary to allow
// images to be updated independently of the vendor image, while maintaining contributions from
// multiple partitions in the SEPolicy.  This is especially important for VTS testing, where the
// SEPolicy on the Google System Image may not be identical to the system image shipped on a
// vendor's device.
// The split policy is for supporting treble devices.  It splits the SEPolicy across files on
// /system/etc/selinux (the 'plat' portion of the policy) and /vendor/etc/selinux (the 'vendor'
// portion of the policy).  This is necessary to allow the system image to be updated independently
// of the vendor image, while maintaining contributions from both partitions in the SEPolicy.  This
// is especially important for VTS testing, where the SEPolicy on the Google System Image may not be
// identical to the system image shipped on a vendor's device.

// The split SEPolicy is loaded as described below:
// 1) There is a precompiled SEPolicy located at either /vendor/etc/selinux/precompiled_sepolicy or
//    /odm/etc/selinux/precompiled_sepolicy if odm parition is present.  Stored along with this file
//    are the sha256 hashes of the parts of the SEPolicy on /system, /system_ext, /product, and apex
//    that were used to compile this precompiled policy.  The system partition contains a similar
//    sha256 of the parts of the SEPolicy that it currently contains. Symmetrically, system_ext,
//    product, and apex contain sha256 hashes of their SEPolicy. Init loads this
//    are the sha256 hashes of the parts of the SEPolicy on /system, /system_ext and /product that
//    were used to compile this precompiled policy.  The system partition contains a similar sha256
//    of the parts of the SEPolicy that it currently contains.  Symmetrically, system_ext and
//    product paritition contain sha256 hashes of their SEPolicy.  The init loads this
//    precompiled_sepolicy directly if and only if the hashes along with the precompiled SEPolicy on
//    /vendor or /odm match the hashes for system, system_ext, product, and apex SEPolicy,
//    respectively.
// 2) If these hashes do not match, then either /system or /system_ext /product, or apex (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.
//    /vendor or /odm match the hashes for system, system_ext and product SEPolicy, respectively.
// 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.
//    That function contains even more documentation with the specific implementation details of how
//    the SEPolicy is compiled if needed.

@@ -61,15 +58,12 @@
#include <stdlib.h>
#include <sys/wait.h>
#include <unistd.h>
#include <fstream>

#include <CertUtils.h>
#include <android-base/chrono_utils.h>
#include <android-base/file.h>
#include <android-base/logging.h>
#include <android-base/parseint.h>
#include <android-base/result.h>
#include <android-base/scopeguard.h>
#include <android-base/strings.h>
#include <android-base/unique_fd.h>
#include <fs_avb/fs_avb.h>
@@ -77,7 +71,6 @@
#include <libgsi/libgsi.h>
#include <libsnapshot/snapshot.h>
#include <selinux/android.h>
#include <ziparchive/zip_archive.h>

#include "block_dev_initializer.h"
#include "debug_ramdisk.h"
@@ -245,7 +238,6 @@ Result<std::string> FindPrecompiledSplitPolicy() {
             precompiled_sepolicy + ".system_ext_sepolicy_and_mapping.sha256"},
            {"/product/etc/selinux/product_sepolicy_and_mapping.sha256",
             precompiled_sepolicy + ".product_sepolicy_and_mapping.sha256"},
            {"/dev/selinux/apex_sepolicy.sha256", precompiled_sepolicy + ".apex_sepolicy.sha256"},
    };

    for (const auto& [actual_id_path, precompiled_id_path] : sepolicy_hashes) {
@@ -324,7 +316,7 @@ bool OpenSplitPolicy(PolicyFile* policy_file) {
    // * vendor -- policy needed due to logic contained in the vendor image,
    // * mapping -- mapping policy which helps preserve forward-compatibility of non-platform policy
    //   with newer versions of platform policy.
    // * (optional) policy needed due to logic on product, system_ext, odm, or apex.
    // * (optional) policy needed due to logic on product, system_ext, or odm images.
    // secilc is invoked to compile the above three policy files into a single monolithic policy
    // file. This file is then loaded into the kernel.

@@ -420,12 +412,6 @@ bool OpenSplitPolicy(PolicyFile* policy_file) {
    if (access(odm_policy_cil_file.c_str(), F_OK) == -1) {
        odm_policy_cil_file.clear();
    }

    // apex_sepolicy.cil is default but optional.
    std::string apex_policy_cil_file("/dev/selinux/apex_sepolicy.cil");
    if (access(apex_policy_cil_file.c_str(), F_OK) == -1) {
        apex_policy_cil_file.clear();
    }
    const std::string version_as_string = std::to_string(SEPOLICY_VERSION);

    // clang-format off
@@ -468,9 +454,6 @@ bool OpenSplitPolicy(PolicyFile* policy_file) {
    if (!odm_policy_cil_file.empty()) {
        compile_args.push_back(odm_policy_cil_file.c_str());
    }
    if (!apex_policy_cil_file.empty()) {
        compile_args.push_back(apex_policy_cil_file.c_str());
    }
    compile_args.push_back(nullptr);

    if (!ForkExecveAndWaitForCompletion(compile_args[0], (char**)compile_args.data())) {
@@ -497,194 +480,6 @@ bool OpenMonolithicPolicy(PolicyFile* policy_file) {
    return true;
}

constexpr const char* kSigningCertRelease =
        "/system/etc/selinux/com.android.sepolicy.cert-release.der";
const std::string kSepolicyApexMetadataDir = "/metadata/sepolicy/";
const std::string kSepolicyApexSystemDir = "/system/etc/selinux/apex/";
const std::string kSepolicyZip = "SEPolicy.zip";
const std::string kSepolicySignature = "SEPolicy.zip.sig";

const std::string kTmpfsDir = "/dev/selinux/";

// Files that are deleted after policy is compiled/loaded.
const std::vector<std::string> kApexSepolicyTmp{"apex_sepolicy.cil", "apex_sepolicy.sha256"};
// Files that need to persist because they are used by userspace processes.
const std::vector<std::string> kApexSepolicy{"apex_file_contexts", "apex_property_contexts",
                                             "apex_service_contexts", "apex_seapp_contexts",
                                             "apex_test"};

Result<void> CreateTmpfsDir() {
    mode_t mode = 0744;
    struct stat stat_data;
    if (stat(kTmpfsDir.c_str(), &stat_data) != 0) {
        if (errno != ENOENT) {
            return ErrnoError() << "Could not stat " << kTmpfsDir;
        }
        if (mkdir(kTmpfsDir.c_str(), mode) != 0) {
            return ErrnoError() << "Could not mkdir " << kTmpfsDir;
        }
    } else {
        if (!S_ISDIR(stat_data.st_mode)) {
            return Error() << kTmpfsDir << " exists and is not a directory.";
        }
        LOG(WARNING) << "Directory " << kTmpfsDir << " already exists";
    }

    // Need to manually call chmod because mkdir will create a folder with
    // permissions mode & ~umask.
    if (chmod(kTmpfsDir.c_str(), mode) != 0) {
        return ErrnoError() << "Could not chmod " << kTmpfsDir;
    }

    return {};
}

Result<void> PutFileInTmpfs(ZipArchiveHandle archive, const std::string& fileName) {
    ZipEntry entry;
    std::string dstPath = kTmpfsDir + fileName;

    int ret = FindEntry(archive, fileName, &entry);
    if (ret != 0) {
        // All files are optional. If a file doesn't exist, return without error.
        return {};
    }

    unique_fd fd(TEMP_FAILURE_RETRY(
            open(dstPath.c_str(), O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, S_IRUSR | S_IWUSR)));
    if (fd == -1) {
        return ErrnoError() << "Failed to open " << dstPath;
    }

    ret = ExtractEntryToFile(archive, &entry, fd.get());
    if (ret != 0) {
        return Error() << "Failed to extract entry \"" << fileName << "\" ("
                       << entry.uncompressed_length << " bytes) to \"" << dstPath
                       << "\": " << ErrorCodeString(ret);
    }

    return {};
}

Result<void> GetPolicyFromApex(const std::string& dir) {
    LOG(INFO) << "Loading APEX Sepolicy from " << dir + kSepolicyZip;
    unique_fd fd(open((dir + kSepolicyZip).c_str(), O_RDONLY | O_BINARY | O_CLOEXEC));
    if (fd < 0) {
        return ErrnoError() << "Failed to open package " << dir + kSepolicyZip;
    }

    ZipArchiveHandle handle;
    int ret = OpenArchiveFd(fd.get(), (dir + kSepolicyZip).c_str(), &handle,
                            /*assume_ownership=*/false);
    if (ret < 0) {
        return Error() << "Failed to open package " << dir + kSepolicyZip << ": "
                       << ErrorCodeString(ret);
    }

    auto handle_guard = android::base::make_scope_guard([&handle] { CloseArchive(handle); });

    auto create = CreateTmpfsDir();
    if (!create.ok()) {
        return create.error();
    }

    for (const auto& file : kApexSepolicy) {
        auto extract = PutFileInTmpfs(handle, file);
        if (!extract.ok()) {
            return extract.error();
        }
    }
    for (const auto& file : kApexSepolicyTmp) {
        auto extract = PutFileInTmpfs(handle, file);
        if (!extract.ok()) {
            return extract.error();
        }
    }
    return {};
}

Result<void> SepolicyCheckSignature(const std::string& dir) {
    std::string signature;
    if (!android::base::ReadFileToString(dir + kSepolicySignature, &signature)) {
        return ErrnoError() << "Failed to read " << kSepolicySignature;
    }

    std::fstream sepolicyZip(dir + kSepolicyZip, std::ios::in | std::ios::binary);
    if (!sepolicyZip) {
        return Error() << "Failed to open " << kSepolicyZip;
    }
    sepolicyZip.seekg(0);
    std::string sepolicyStr((std::istreambuf_iterator<char>(sepolicyZip)),
                            std::istreambuf_iterator<char>());

    auto releaseKey = extractPublicKeyFromX509(kSigningCertRelease);
    if (!releaseKey.ok()) {
        return releaseKey.error();
    }

    return verifySignature(sepolicyStr, signature, *releaseKey);
}

Result<void> SepolicyVerify(const std::string& dir) {
    auto sepolicySignature = SepolicyCheckSignature(dir);
    if (!sepolicySignature.ok()) {
        return Error() << "Apex SEPolicy failed signature check";
    }
    return {};
}

void CleanupApexSepolicy() {
    for (const auto& file : kApexSepolicyTmp) {
        std::string path = kTmpfsDir + file;
        unlink(path.c_str());
    }
}

// Updatable sepolicy is shipped within an zip within an APEX. Because
// it needs to be available before Apexes are mounted, apexd copies
// the zip from the APEX and stores it in /metadata/sepolicy. If there is
// no updatable sepolicy in /metadata/sepolicy, then the updatable policy is
// loaded from /system/etc/selinux/apex. Init performs the following
// steps on boot:
//
// 1. Validates the zip by checking its signature against a public key that is
// stored in /system/etc/selinux.
// 2. Extracts files from zip and stores them in /dev/selinux.
// 3. Checks if the apex_sepolicy.sha256 matches the sha256 of precompiled_sepolicy.
// if so, the precompiled sepolicy is used. Otherwise, an on-device compile of the policy
// is used. This is the same flow as on-device compilation of policy for Treble.
// 4. Cleans up files in /dev/selinux which are no longer needed.
// 5. Restorecons the remaining files in /dev/selinux.
// 6. Sets selinux into enforcing mode and continues normal booting.
//
void PrepareApexSepolicy() {
    // If apex sepolicy zip exists in /metadata/sepolicy, use that, otherwise use version on
    // /system. If neither exists, do nothing.
    std::string dir;
    if (access((kSepolicyApexMetadataDir + kSepolicyZip).c_str(), F_OK) == 0) {
        dir = kSepolicyApexMetadataDir;
    } else if (access((kSepolicyApexSystemDir + kSepolicyZip).c_str(), F_OK) == 0) {
        dir = kSepolicyApexSystemDir;
    } else {
        LOG(INFO) << "APEX Sepolicy not found";
        return;
    }

    auto sepolicyVerify = SepolicyVerify(dir);
    if (!sepolicyVerify.ok()) {
        LOG(INFO) << "Error: " << sepolicyVerify.error();
        // If signature verification fails, fall back to version on /system.
        // This file doesn't need to be verified because it lives on the system partition which
        // is signed and protected by verified boot.
        dir = kSepolicyApexSystemDir;
    }

    auto apex = GetPolicyFromApex(dir);
    if (!apex.ok()) {
        // TODO(b/199914227) Make failure fatal. For now continue booting with non-apex sepolicy.
        LOG(ERROR) << apex.error();
    }
}

void ReadPolicy(std::string* policy) {
    PolicyFile policy_file;

@@ -961,12 +756,9 @@ void LoadSelinuxPolicyAndroid() {

    LOG(INFO) << "Opening SELinux policy";

    PrepareApexSepolicy();

    // Read the policy before potentially killing snapuserd.
    std::string policy;
    ReadPolicy(&policy);
    CleanupApexSepolicy();

    auto snapuserd_helper = SnapuserdSelinuxHelper::CreateIfNeeded();
    if (snapuserd_helper) {
@@ -982,13 +774,6 @@ void LoadSelinuxPolicyAndroid() {
        snapuserd_helper->FinishTransition();
        snapuserd_helper = nullptr;
    }

    // This restorecon is intentionally done before SelinuxSetEnforcement because the permissions
    // needed to transition files from tmpfs to *_contexts_file context should not be granted to
    // any process after selinux is set into enforcing mode.
    if (selinux_android_restorecon("/dev/selinux/", SELINUX_ANDROID_RESTORECON_RECURSE) == -1) {
        PLOG(FATAL) << "restorecon failed of /dev/selinux failed";
    }
}

int SetupSelinux(char** argv) {