Android P SELinux (二) 开机初始化与策略文件编译过程

本文详细介绍了Android系统中SELinux策略的加载和编译过程,从开机初始化的代码分析到策略文件如何转化为二进制文件加载到内核。重点讨论了`LoadSplitPolicy`函数,该函数首先尝试从预编译的策略文件加载,若找不到则通过`secilc`编译cil文件生成二进制策略。此外,文章还涵盖了策略编译的步骤,包括`m4`转换、`checkpolicy`检查和`secilc`编译。最后,文章阐述了策略文件如何通过`security_load_policy`加载到内核以及整个加载流程。

Android P SELinux (一) 基础概念
Android P SELinux (二) 开机初始化与策略文件编译过程
Android P SELinux (三) 权限检查原理与调试
Android P SELinux (四) CTS neverallow处理总结

本文主要围绕二进制策略文件的加载和编译过程

我们写的te规则,到底生成在哪里了?开机之后又是怎么加载使用的?

一、SELinux开机初始化

1. init.cpp

代码路径:android/system/core/init/init.cpp
main函数:

if (is_first_stage) {
   
     // 第一阶段初始化
    ...
 
    // Set up SELinux, loading the SELinux policy.
    SelinuxSetupKernelLogging();
    SelinuxInitialize();
 
    ...
}
 
 
// Now set up SELinux for second stage.
SelinuxSetupKernelLogging();
SelabelInitialize();
SelinuxRestoreContext();

2. selinux.cpp

代码路径:android/system/core/init/selinux.cpp

2.1 SelinuxSetupKernelLogging

设置log callback,可以调用selinux_log把log写入到kmsg里面

2.2 SelinuxInitialize

selinux 初始化,从二进制策略文件里面读取策略,加载到内核

void SelinuxInitialize() {
   
   
    Timer t;
 
    LOG(INFO) << "Loading SELinux policy";
    if (!LoadPolicy()) {
   
    // 加载SELinux策略
        LOG(FATAL) << "Unable to load SELinux policy";
    }
 
    bool kernel_enforcing = (security_getenforce() == 1); // 从kernel获取SELinux的状态,和getenforce的实现一样
    bool is_enforcing = IsEnforcing();  // 从bootargs里面获取
    if (kernel_enforcing != is_enforcing) {
   
   
        if (security_setenforce(is_enforcing)) {
   
    // 如果kernel里面的SELinux状态和bootargs的不一致,要设置成bootargs里面传过来的值
            PLOG(FATAL) << "security_setenforce(%s) failed" << (is_enforcing ? "true" : "false");
        }
    }
 
    if (auto result = WriteFile("/sys/fs/selinux/checkreqprot", "0"); !result) {
   
     // 由内核强制执行检查保护
        LOG(FATAL) << "Unable to write to /sys/fs/selinux/checkreqprot: " << result.error();
    }
 
    // init's first stage can't set properties, so pass the time to the second stage.
    setenv("INIT_SELINUX_TOOK", std::to_string(t.duration().count()).c_str(), 1);
}
 
bool LoadPolicy() {
   
    // 从Android8.0之后,因为Project Treble,system和vendor策略分离,所以Android P上走的是LoadSplitPolicy
    return IsSplitPolicyDevice() ? LoadSplitPolicy() : LoadMonolithicPolicy();
}

IsEnforcing 从bootargs里面的androidboot.selinux取值; security_getenforce 从kernel取值,这个值和getenforce拿到的是一样的

2.3 checkreqprot

设置"checkreqprot"标记的初始值。

  • "0"表示由内核强制执行检查保护(包括其中隐含的所有执行保护)
  • "1"表示由应用程序自己主动请求执行检查保护
    默认值由内核在编译时确定,也可以在运行时通过/sys/fs/selinux/checkreqprot修改

2.4 LoadPolicy

policy的分割是从Android 8.0之后开始的,具体可以看看这篇博客:
Android8.0 SELinux详解

Android低版本用的是LoadMonolithicPolicy,8.0之后调用的是LoadSplitPolicy

sepolicy分离

#public - policy exported on which non-platform policy developers may write
#additional policy. types and attributes are versioned and included in
#delivered non-platform policy, which is to be combined with platform policy. 导出的策略,非平台策略开发人员可以在其上编写附加策略
类型和属性被版本化并包含在交付的非平台策略中,该策略将与平台策略相结合
使用BOARD_PLAT_PUBLIC_SEPOLICY_DIR来添加拓展

types和attributes生成在vendor分区的会带上版本,比如bootanim_28_0,实现在cil_android_attributize

#private - platform-only policy required for platform functionality but which
#is not exported to vendor policy developers and as such may not be assumed
#to exist. 平台功能所需的纯平台策略,但不会导出到供应商策略开发人员,因此可能不存在。

#vendor - vendor-only policy required for vendor functionality. This policy can
#reference the public policy but cannot reference the private policy. This
#policy is for components which are produced from the core/non-vendor tree and
#placed into a vendor partition. 供应商功能所需的供应商专用策略。此策略可以引用公共策略,但不能引用私有策略。此策略适用于从核心/非供应商树生成并放置到供应商分区中的组件。

#mapping - This contains policy statements which map the attributes
#exposed in the public policy of previous versions to the concrete types used
#in this policy to ensure that policy targeting attributes from public
#policy from an older platform version continues to work. 它包含策略语句,这些语句将以前版本的公共策略中公开的属性映射到此策略中使用的具体类型,以确保来自较旧平台版本的公共策略的策略目标属性继续工作。

2.5 LoadSplitPolicy

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,
    // * mapping -- mapping policy which helps preserve forward-compatibility of non-platform policy
    //   with newer versions of platform policy.
    //
    // secilc is invoked to compile the above three policy files into a single monolithic policy
    // file. This file is then loaded into the kernel.
 
    // 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;
    if (FindPrecompiledSplitPolicy(&precompiled_sepolicy_file)) {
   
    // 先找odm分区,再找vendor分区
        unique_fd fd(open(precompiled_sepolicy_file.c_str(), O_RDONLY | O_CLOEXEC | O_BINARY));  // open file
        if (fd != -1) {
   
   
            if (selinux_android_load_policy_from_fd(fd, precompiled_sepolicy_file.c_str()) < 0) {
   
    // 加载到kernel
                LOG(ERROR) << "Failed to load SELinux policy from " << precompiled_sepolicy_file;
                return false;
            }
            return true;
        }
    }
    // No suitable precompiled policy could be loaded
 
    LOG(INFO) << "Compiling SELinux policy";
 
    // Determine the highest policy language version supported by the kernel
    set_selinuxmnt("/sys/fs/selinux");
    int max_policy_version = security_policyvers();
    if (max_policy_version == -1) {
   
   
        PLOG(ERROR) << "Failed to determine highest policy version supported by kernel";
        return false;
    }
 
    // We store the output of the compilation on /dev because this is the most convenient tmpfs
    // storage mount available this early in the boot sequence.
    char compiled_sepolicy[] = "/dev/sepolicy.XXXXXX";
    unique_fd compiled_sepolicy_fd(mkostemp(compiled_sepolicy, O_CLOEXEC));
    if (compiled_sepolicy_fd < 0) {
   
   
        PLOG(ERROR) << "Failed to create temporary file " << compiled_sepolicy;
        return false;
    }
 
    // Determine which mapping file to include
    std::string vend_plat_vers;
    if (!GetVendorMappingVersion(&vend_plat_vers)) {
   
   
        return false;
    }
    std::string mapping_file("/system/etc/selinux/mapping/" + vend_plat_vers + ".cil");
 
    // vendor_sepolicy.cil and plat_pub_versioned.cil are the new design to replace
    // nonplat_sepolicy.cil.
    std::string plat_pub_versioned_cil_file("/vendor/etc/selinux/plat_pub_versioned.cil");
    std::string vendor_policy_cil_file("/vendor/etc/selinux/vendor_sepolicy.cil");
 
    if (access(vendor_policy_cil_file.c_str(), F_OK) == -1) {
   
   
        // For backward compatibility.
        // TODO: remove this after no device is using nonplat_sepolicy.cil.
        vendor_policy_cil_file = "/vendor/etc/selinux/nonplat_sepolicy.cil";
        plat_pub_versioned_cil_file.clear();
    } else if (access(plat_pub_versioned_cil_file.c_str(), F_OK) == -1) {
   
   
        LOG(ERROR) << "Missing " << plat_pub_versioned_cil_file;
        return false;
    }
 
    // odm_sepolicy.cil is default but optional.
    std::string odm_policy_cil_file("/odm/etc/selinux/odm_sepolicy.cil");
    if (access(odm_policy_cil_file.c_str(), F_OK) == -1) {
   
   
        odm_policy_cil_file.clear();
    }
    const std::string version_as_string = std::to_string(max_policy_version);
 
    // clang-format off
    std::vector<const char*> compile_args {
   
   
        "/system/bin/secilc",
        plat_policy_cil_file,
        "-m", "-M", "true", "-G", "-N",
        // Target the highest policy language version supported by the kernel
        "-c", version_as_string.c_str(),
        mapping_file.c_str(),
        "-o", compiled_sepolicy,
        // We don't care about file_contexts output by the compiler
        "-f", "/sys/fs/selinux/null",  // /dev/null is not yet available
    };
    // clang-format on
 
    if (!plat_pub_versioned_cil_file.empty()) {
   
   
        compile_args.push_back(plat_pub_versioned_cil_file.c_str
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值