基于Android13的系统启动流程分析(一)之SeLinux权限介绍

学习Android系统启动流程之前先学习一下SeLinux权限系统,步入正题

本章讲解的方向和你将收获的知识:

  • 什么是SeLinux系统,SeLinux的简介和介绍
  • SeLinux系统的主要作用和存在的意义,是基于哪个版本开始推行该方案的
  • 如果遇到了SeLinux权限问题该如何解决,有几种解决方案
  • SeLinux详细内容知识分解,你将了解宽松模式和严格模式

一. SeLinux介绍

  1. SeLinux 全称 Security-Enhanced Linux 即安全增强型 Linux,基于MAC机制的一种实现,它是一个 Linux 内核模块,也是 Linux 的一个安全子系统,这不是android特有的特性,而是由Linux衍生过来的SEAndroid。

  2. 在Android Q版本上就开始推行SeLinux机制且强行执行此机制(沙盒机制,只能获取到AOSP指定的对外的接口去获取),所以在之前很多应用在Android Q上会产生很多权限相关的问题。

  3. 如果在Android Q或以上的版本遇到权限问题,尝试命令:adb shell logcat | grep avc,如果有对应的avc log输出则大概率是受SeLinux权限影响

    avc: denied { read } for pid=1834 comm=“gps_location” name=“mmcblk0p17” dev=“tmpfs”
    scontext=u:r:gps_location:s0 tcontext=u:object_r:block_device:s0 tclass=blk_file permissive=0

  4. SeLinux关键字介绍
    MAC和DAC都隶属于访问控制类,分为自主和强制两种方式
    pic4.1 访问控制
    Linux 内核资源访问控制分为 DAC(Discretionary Access Control,自主访问控制)和 MAC(Mandatory
    Access Control,强制访问控制)两类
    4.2 DAC
    这是基于用户-用户组的读、写、执行的权限检查,该管理过于宽松,如果获得 root 权限,可以在 Linux 系统内做任何事情
    4.3 MAC
    SELinux是 MAC 机制的一种实现,基于安全上下文和安全策略的安全机制,用于补充 DAC 检查。访问系统资源时,会先进行 DAC 检查,DAC 检查通过,才能进行 MAC 检查,如果 MAC 检查通过,才能获得访问权限

二. SeLinux基础知识

SELinux 分为 Permissive 和 Enforcing 两种模式

  1. Permissive 宽容模式
    宽容模式,代表 SELinux 运作中,违反 SELinux 规则时只会有警告讯息(avc denied),而不会实际限制的访问.
    在 Permissive 模式中,SELinux 被启用,但安全策略规则并没有被强制执行。当安全策略规则应该拒绝访问时,访问仍然被允许。然而,此时会向日志文件发送一条消息,表示该访问应该被拒绝(avc).

  2. Enforcing 严格模式
    在 Enforcing 模式中, SELinux 被启动,并强制执行所有的安全策略规则,代表 SELinux 运作中,违反 SELinux 规则的行为将被阻止并记录到日志中(avc)

  3. 查看当前SELinux模式
    (1)通过adb命令行来查看:adb shell getenforce,输出结果—>Enforcing 或者 Permissive
    (2)通过adb logcat命令行来观察:adb logcat | grep avc

    avc:denied {write setter} for path=”/dev/…” dev=”tmpfs”
    scontext=u:r:system_server:s0 tcontext=u:object_r:file:s0 permissive=1
    (permissive=1,说明是 Permissive 模式。permissive=0,说明是 Enforcing 模式)

  4. avc权限语法介绍

    avc: denied { read } for pid=1834 comm=“system_server” name=“mmcblk0p17” dev=“tmpfs” ino=10268 [scontext=u:r:system_server:s0] [tcontext=u:object_r:block_device:s0] tclass=blk_file permissive=0

    (此 AVC log 说明 system_server(进程)缺少对标签为 block_device、类型为 blk_file 和名称为 mmcblk0p17 文件的 read 权限)
    (1). avc: denied:表示当前操作被拒绝。
    (2). { read }:表示被拒绝的操作,{ }中含有实际尝试的操作。
    (3). for pid=1834:当前发生 avc: denied 进程的 ID。
    (4). comm=“system_server”:当前发生 avc: denied 进程的名称,即主体进程名称。
    (5). name=“mmcblk0p17”:操作尝试的目标文件或目录的路径,即客体资源名称。
    (6). dev=“tmpfs”:含有这个文件系统的设备节点,客体资源在该文件系统中。
    (7). ino=10268:目标文件或目录的节点号。
    (8). scontext=u:r:system_server:s0:主体进程的安全上下文。
    (9). tcontext=u:object_r:block_device:s0:客体资源的安全上下文。
    (10). tclass=blk_file:访问资源所属类别。
    (11). permissive=0:当前是 Enforcing 模式,permissive=1 时为 Permissive 模式

三. 解决SeLinux权限问题

1.命令行修改权限模式

adb root;   
adb shell setenforce 0 --->设置为Permissive宽松模式,临时关闭SELinux,机器重启以后还是会恢复的

2.AndroidManifest.xml配置开关
需要注意targetSdkVersion 需要小于 30

android:requestLegacyExternalStorage="true"

3.修改init启动过程中的代码
这种方式比较简单—>修改 system/core/init/selinux.cpp 文件里的 IsEnforcing()函数,直接让它返回false即可

bool IsEnforcing() {
    
    
    // 直接返回false
    return false;
    if (ALLOW_PERMISSIVE_SELINUX) {
    
    
        return StatusFromProperty() == SELINUX_ENFORCING;
    }
    return true;
}

void SelinuxSetEnforcement() {
    
    
    // getenforce获取Enforcing模式
    bool kernel_enforcing = (security_getenforce() == 1);
    // 直接让is_enforcing返回false即关闭严格模式
    bool is_enforcing = IsEnforcing();
    if (kernel_enforcing != is_enforcing) {
    
    
        // 调用security_setenforce(false)设置为Permissive宽容模式
        if (security_setenforce(is_enforcing)) {
    
    
            PLOG(FATAL) << "security_setenforce(" << (is_enforcing ? "true" : "false")
                        << ") failed";
        }
    }

    if (auto result = WriteFile("/sys/fs/selinux/checkreqprot", "0"); !result.ok()) {
    
    
        LOG(FATAL) << "Unable to write to /sys/fs/selinux/checkreqprot: " << result.error();
    }
}

4.根据报错的权限添加SELinux权限
由于缺少 SELinux 权限导致如下“avc: denied”,需要根据 AVC log 信息添加相应权限。

avc: denied {
    
     read write } for pid=3483 comm=“batteryTest” name=“rtc0” 
dev=“tmpfs” scontext=u:r:system_server:s0 tcontext=u:object_r:custom_battery_device:s0 
tclass=chr_file permissive=0

system_server(进程)缺少对标签为 custom_battery_device、类型为 chr_file 和名称为 rtc0 文件节点的读写权限。
根据上面缺少的avc添加所需的读写权限:
(1). 打开/system/sepolicy/private/{scontext.te}(system_server.te)文件
(2). 添加如下代码:和上面的标签对应上:{allow scontext tcontext:tclass permission }
(3). 例如allow system_server custom_battery_device:chr_file {read write} ,编译源码并进行烧录即可

四. Neverallow问题

添加了selinux权限后,代码进行编译时,编译失败并报 neverallow 错误,例如添加`allow system_app sysfs:file {
     
     write};`权限后编译报错

原因是 Google 不允许应用进程写 sysfs 类型的文件,这是Google规范,安全考虑,部分权限不允许给,
当然也可以修改domain.te来修改Google的规范,虽然可以解决问题,但是这是不被允许的,在送测的时候会导致GMS测试失败。

例如domain.te拥有以下代码:
neverallow {
    
     appdomain -bluetooth -nfc }sysfs:dir_file_class_set write
那么则不允许给dir_file_class_set标签赋予write权限。

那又必须要这个权限怎么办?那就可以客制化SELiunx权限。

五. SELinux权限客制化(自定义权限)

根据上面所说,Google规则需遵守,部分权限不允许通过,所以需要自定义权限规则.

  • type定义
    type分为了property.tefile.tesystem.te,有很多类型,不止这三种,使用哪个文件取决于avc权限中的tclass属性,目前只用file.te举例,打开file.te添加如下type

    /system/sepolicy/private/file.te
    # {parameter1,parameter2,parameter3}
    # type固定格式,custom_battery_file自定义的名称,file_type定义为文件类型
    # 逗号分割,后面可以继续跟类型 例如:data_file_type
    type custom_battery_file, file_type;
    
  • 配置安全上下文
    安全上下文分为了 genfs_contextsfile_contextsproperty_contexts ,当然不止这几种,例举了一些常用的
    打开 /system/sepolicy/private/file_contexts 文件,打开哪个文件取决于avc权限中的tclass属性
    (三个xxx_contexts,需要自行判断缺少的权限的客体资源是目录还是文件 或是 属性值):

    /system/sepolicy/private/file_contexts
    # 添加如下代码
    # 第一个参数写文件节点(读取这个文件节点没权限,就添加这个节点)
    # 第二个参数固定写法u:object_r:custom_battery_file(这里写file.te里定义的类型):s0(这些参数不做详细解释)
    /vendor/custom/product/battery	u:object_r:custom_battery_file:s0
    

    /xxx/xxx (目标文件路径或文件) u:object_r:file.te里面自定义的名称:s0

  • 配置原进程访问权限
    根据avc log中的scontext(主体进程)的值来决定在哪个文件下添加访问权限
    例如 scontext=u:r:system_server:s0,那则打开system_server.te即可

    /system/sepolicy/private/system_server.te
    # 1.allow固定格式 
    # 2.system_server固定格式取决于scontext的值  
    # 3.custom_battery_file(file.te里自定义的类型)
    # 4.冒号后面的file,取决于avc log中的tclass类型是什么(例如tclass=file)
    # 5.{要给的权限类型}
    allow system_server custom_battery_file:file {
          
          read open};
    

    这样就客制化好了一个节点的写权限

六. 补充知识(mls规则)

终端中查看安全上下文的方法
在终端中,可通过如下指令查看文件安全上下文:ls -lZ
在终端中,可通过如下指令查看属性安全上下文:getprop -Z
在终端中,可通过如下指令查看进程安全上下文:ps -Z

如果 te 文件已经添加 SELinux 权限,但没有生效,查看 AVC log 信息出现“s0:c512,c768”字眼,则可判
断是由于 mls 规则导致。说明主体和客体安全级别不同,
举例:已经在 platform_app.te 中添加了 SELinux 权限,但 log 中依然有如下报错:
avc: denied { write } for pid=2002 comm=“lightness”
scontext=u:r:platform_app:s0(主体):c512,c768 tcontext=u:r:custom_device_lightness:s0(客体资源) tclass=char_file(file类型,读取的文件节点属于file类型) permissive=0

主体: scontext=u:r:【platform_app】
客体: tcontext=u:r:【custom_device_lightness】
这是因为 Google 在文件 system/sepolicy/private/mls 中进行了安全级的限制,代码如下:
【mlsconstrain cahr_file { write } (l1 eq l2 or t1 == mlstrustedsubject or t2 == mlstrustedsubject);】
l1需要l2相等,或者l1等于mlstrustedsubject l2等于mlstrustedsubject ,才能允许SELiunx权限。

这种情况需要主体进程或者客体进程中的一个是 mlstrustedsubject,这里 platform_app(主体) 最好不要修改,所以要修改客体 slogmodem(客体)。

具体修改方法如下:

/system/sepolicy/private/platform_app.te(取决于主体进程)文件
添加如下代码:
type custom_device_lightness, domain, mlstrustedsubject;

七. 代码修改后烧录

1.如果只是修改 selinux 相关文件–通过编译命令:
(1)lunch 项目
(2)cd system/sepolicy
(3)mma
将编译产物 out/target/product/xxx/system/etc/selinux 和 out/target/product/xxx/vendor/etc/selinux 拷贝出来,推入手机查看是否生效,执行指令如下:
(1)cd out/target/product/xxx
(2)adb root
(3)adb remount
(4)adb push system/etc/selinux /system/etc/
(5)adb push vendor/etc/selinux /vendor/etc/
如果手机不能root && remount,可以考虑通过编译烧录的方式去验证

总结

至此SELinux权限系统基本上介绍完毕,如何解决权限问题,如何关闭权限和切换权限,自定义权限,配置权限,皆已教学完毕。不为别的,为了留下工作上的脚印

下一篇进入基于Android13的系统启动流程分析(一)之文件系统介绍(待补充)

猜你喜欢

转载自blog.csdn.net/q1210249579/article/details/128637218