学习Android系统启动流程之前先学习一下SeLinux权限系统,步入正题
本章讲解的方向和你将收获的知识:
SeLinux 全称 Security-Enhanced Linux 即安全增强型 Linux,基于MAC机制的一种实现,它是一个 Linux 内核模块,也是 Linux 的一个安全子系统,这不是android特有的特性,而是由Linux衍生过来的SEAndroid。
在Android Q版本上就开始推行SeLinux机制且强行执行此机制(沙盒机制,只能获取到AOSP指定的对外的接口去获取),所以在之前很多应用在Android Q上会产生很多权限相关的问题。
如果在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
SeLinux关键字介绍
MAC和DAC都隶属于访问控制类,分为自主和强制两种方式
4.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 分为 Permissive 和 Enforcing 两种模式
Permissive 宽容模式
宽容模式,代表 SELinux 运作中,违反 SELinux 规则时只会有警告讯息(avc denied),而不会实际限制的访问.
在 Permissive 模式中,SELinux 被启用,但安全策略规则并没有被强制执行。当安全策略规则应该拒绝访问时,访问仍然被允许。然而,此时会向日志文件发送一条消息,表示该访问应该被拒绝(avc).
Enforcing 严格模式
在 Enforcing 模式中, SELinux 被启动,并强制执行所有的安全策略规则,代表 SELinux 运作中,违反 SELinux 规则的行为将被阻止并记录到日志中(avc)
查看当前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 模式)
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 模式
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}
,编译源码并进行烧录即可
添加了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权限。
根据上面所说,Google规则需遵守,部分权限不允许通过,所以需要自定义权限规则.
type定义
type分为了property.te
、file.te
、system.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_contexts
、file_contexts
、property_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};
这样就客制化好了一个节点的写权限
终端中查看安全上下文的方法
在终端中,可通过如下指令查看文件安全上下文: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的系统启动流程分析(一)之文件系统介绍(待补充)