Android系统启动流程(二)——Selinux初始化(基于Android13)

1 简述

Selinux,全称为Security-Enhanced Linux,即安全增强型Linux,是Linux中的一种安全管控策略。传统的Linux访问控制通过DAC(Discretionary Access Control)控制,即给用户、用户组、其他用户授予不同的读写和可执行权限来控制文件的访问。Selinux基于MAC(Mandatory Access Control)控制,基于安全上下文和安全策略的安全机制,只有定义了允许访问的规则的才能访问,否则默认不能访问。
MAC在DAC的基础之上,首先进行DAC检查,然后才进行MAC检查,检查通过后才能进行访问。

2 Selinux基础

违背了selinux规则会打印日志,日志关键字为avc

LocTimerMsgTask: type=1400 audit(0.0:8): avc: denied {
    call } for scontext=u:r:location:s0 tcontext=u:r:qtidataservices_app:s0:c56,c256,c512,c768 tclass=binder permissive=0

上面就是一个典型的selinux问题,透露出了以下信息

scontext=u:r:location:s0 请求访问者的selinux标签,这个通常是进程
tcontext=u:r:qtidataservices_app:s0 被请求者的selinux标签,通常是文件或者服务等
tclass=binder 被请求的类型是binder
denied {
    call } 表示请求者对访问者请求被selinux拒绝的操作是call操作
permissive=0 selinux拒绝了这个访问

接下来一一解析上面的,首先是selinux标签

  • u:r:location:s0这个就是selinux的上下文信息,其中u指用户user,r指角色role,location是类型type,s0是range,表示安全级别。所以selinux的基本格式为user:role:type[:range]。在Android中,只有一个user,一个range。而所有的进程,都是role都是r,所有的文件,role都是object_r。
  • tclass表示的是所要访问的对象的类别,通常有file,binder,dir等
  • call是操作,针对不同的class,有不同的操作,比如对于file来说,有create,remove等文件的操作,对于binder来说,就有bind,call这种操作
  • permissive是selinux的模式,selinux可以设置两种模式,通常默认是enforcing模式,即强制模式,所有没有通过selinux检查的操作都被阻止,并打印日志,这里的permissive=0表示的就是enforcing模式。还有一种就是permissive宽容模式,违反selinux规则的操作并不会被阻止,二是允许其访问,但是会打印avc日志。

对于上面的avc报错,如果需要修改的话,需要添加如下:

allow location qtidataservices_app:binder {
   call};

这个就是对avc日志的修改,允许location这个type,对qtidataservices_app的binder进行call的操作。这一行通常添加到location.te文件中,因为很多模块都有自己的te文件进行管理。
通常在domain.te中定义了相关的selinux的neverallow规则,如果在添加allow规则时,编译出现neverallow规则,则需要进行相应的修改。

3 init初始化selinux

system/core/init/main.cpp

if (!strcmp(argv[1], "selinux_setup")) {
   
    return SetupSelinux(argv);
}

system/core/init/selinux.cpp

int SetupSelinux(char** argv) {
   
	...
	// Read the policy before potentially killing snapuserd.
    std::string policy;
    ReadPolicy(&policy); //读取策略文件
    CleanupApexSepolicy();
    ...
    LoadSelinuxPolicy(policy); //感觉像是写入内核,外部依赖的selinux开源项目代码
    ...
	SelinuxSetEnforcement(); //设置selinux模式为enforcing
	...
    const char* path = "/system/bin/init";
    const char* args[] = {
   path, "second_stage", nullptr};
    execv(path, const_cast<char**>(args)); //再次调用init可执行文件,参数为second_stage
    ...

3.1 读取selinux

首先看ReadPolicy

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

    bool ok = IsSplitPolicyDevice() ? OpenSplitPolicy(&policy_file)
                                    : OpenMonolithicPolicy(&policy_file);
    if (!ok) {
   
        LOG(FATAL) << "Unable to open SELinux policy";
    }

    if (!android::base::ReadFdToString(policy_file.fd, policy)) {
   
        PLOG(FATAL) << "Failed to read policy file: " << policy_file.path;
    }
}
bool IsSplitPolicyDevice() {
   
    return access(plat_policy_cil_file, R_OK) != -1;
}
constexpr const char plat_policy_cil_file[] = "/system/etc/selinux/plat_sepolicy.cil";

IsSplitPolicyDevice用于判断设备是不是分策略设备,android8之后将策略拆分为平台策略和供应商策略。所以目前的版本,这个函数返回为true,是根据手机中这个plat_sepolicy.cil文件是否存在来判断的。
在这里插入图片描述
很明显,这个文件是存在的。
所以将调用OpenSplitPolicy,盲猜这个就是实际添加策略的函数了,传入的参数是policy_file,首先看看PolicyFile是个啥

struct PolicyFile {
   
    unique_fd fd;
    std::string path;
};

PolicyFile是一个结构体,包含一个文件描述符和一个string字符串的path。

bool OpenSplitPolicy(PolicyFile* policy_file) {
   
    // IMPLEMENTATION NOTE: Split policy consists of three or more CIL files:
    // * platform -- policy needed due to logic contained in the system image,
    // * 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.
    // secilc is invoked to compile the above three policy files into a single monolithic policy
    // file. This file is then loaded into the kernel.
    //android8以后策略分离。分离的策略至少包含3部分以上,platform平台策略,vendor供应商策略,mapping用于向后兼容的策略。其他的策略则包括product,system_ext,odm或者是apex。secilc就是将上面的这些策略文件编译成一个单个的整体策略文件,然后加载到内核中。

    const auto userdebug_plat_sepolicy = GetUserdebugPlatformPolicyFile();
    const bool use_userdebug_policy = userdebug_plat_sepolicy.has_value();
    if (use_userdebug_policy) {
   
        LOG(INFO) << "Using userdebug system sepolicy " << *userdebug_plat_sepolicy;
    }

    // Load precompiled policy from vendor image, if a matching policy is found there. The policy
    // must match the platform policy on the system image.
    // 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;
            }
        } else {
   
            LOG(INFO) << res.error();
        }
    }
    // No suitable precompiled policy could be loaded

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

    // 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 < 
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值