Selinux简介
SELinux是安全增强型 Linux(Security-Enhanced Linux)简称 SELinux。它是一个 Linux 内核模块,也是 Linux 的一个安全子系统。
SELinux for Android在架构和机制上与SELinux完全一样,考虑到移动设备的特点,所以移植到Android上的只是SELinux的一个子集。SELinux for Android的安全检查几乎覆盖了所有重要的系统资源,包括域转换,类型转换,进程、内核、文件、目录、设备,App,网络及IPC相关的操作。
Android分为宽容模式(仅记录但不强制执行 SELinux 安全政策 )和强制模式(强制执行并记录安全政策。如果失败,则显示为 EPERM 错误。 );在选择强制执行级别时只能二择其一。
Selinux模式:
Enforce模式:打印异常log,拒绝请求。没有权限的请求被拒绝后 执行就会有问题。
Permissive模式:打印异常log,不拒绝请求。请求没被拒绝,执行不会出现问题。
设置SELinux:
adb shell setenforce 0 //设置后即Permissive
adb shell setenforce 1 //设置后即Enforcing
这种设置,在某些版本下需要root权限。
Selinux实际报错日志
这次遇到的问题,就是软件的串口通讯问题。这个软件在debug版系统上,运行的挺好的,切换到user系统上,通讯就会失败,会遇到权限问题导致通讯失败。日志如下:
05-14 10:24:00.144 2773 2773 I test.app: type=1400 audit(0.0:1079): avc: denied { ioctl } for path="socket:[43620]" dev ="sockfs" ino=43620 ioctlcmd=0x8933 scontext=u:r:platform_app:s0:c512,c768 tcontext=u:r:platform_app:s0:c512,c768 tclass=socket permissive=0
分析
avc: denied { 操作权限 } for pid=7201 comm=“进程名” scontext=u:r:源类型:s0 tcontext=u:r:目标类型:s0 tclass=访问类别 permissive=0
添加规则
allow scontext tcontext:tclass denied
添加权限
在device/mediatek/sepolicy/bsp/non_plat/platform_app.te(备注:本项目是9.0MTK6765,请依据自己项目具体定位添加位置,一般处于system/sepolicy/
或device/qcom/sepolicy/
)中添加:
allow platform_app platform_app:socket ioctl;
实际情况
正常情况按照上面那么加权限就行啦,但是。。。
在 Android P上面由于谷歌收紧了 Android SElinux控制,增加了许多neverallow规则,导致调试权限十分不便
而ioctl就是谷歌在9.0上重点限制的部分权限。这部分就是串口和通讯相关权限。
如果直接加上面的权限,会直接报错。相关修改见此博客,讲的挺好:
Android P系统编译报错SELinux违反Neverallow
最终处理方案
别的博客里的处理方案修改代码过多,而且容易连带改出其他BUG。因为我司设备有加密芯片,限制严格,我就准备从宽容模式入手,将USER版系统的Selinux模式改为宽容模式,允许通过这部分权限,来解决问题。
源码分析
system/core/init/selinux.cpp
system/core/init/main.cpp
核心代码在俩个文件内,大概流程如下:
①:main.cpp中的main函数调用selinux.cpp中的SetupSelinux:
int main(int argc, char** argv) {
...省略
if (!strcmp(argv[1], "selinux_setup")) {
return SetupSelinux(argv);
}
...省略
}
②:selinux.cpp中SetupSelinux函数实现如下:
int SetupSelinux(char** argv) {
...省略
SelinuxInitialize();
...省略
return 1;
}
③:SetupSelinux调用了SelinuxInitialize方法。SelinuxInitialize方法代码如下:
void SelinuxInitialize() {
...省略
bool kernel_enforcing = (security_getenforce() == 1);
//判断是否强制模式
bool is_enforcing = IsEnforcing();
if (kernel_enforcing != is_enforcing) {
//调用security_setenforce函数,和setenforce原理一样
if (security_setenforce(is_enforcing)) {
PLOG(FATAL) << "security_setenforce(%s) failed" << (is_enforcing ? "true" : "false");
}
}
...省略
}
④:IsEnforcing方法实现如下:
bool IsEnforcing() {
if (ALLOW_PERMISSIVE_SELINUX) {
return StatusFromCmdline() == SELINUX_ENFORCING;
}
return true;
}
⑤:从IsEnforcing中可以知道,如果一直返回false,那么将会关闭selinux。
最终修改,即全局强制关闭selinux:
bool IsEnforcing() {
if(1 > 0) {
return false;
}
if (ALLOW_PERMISSIVE_SELINUX) {
return StatusFromCmdline() == SELINUX_ENFORCING;
}
return true;
}
价格1>0的判断是因为直接return false会导致系统编译不通过,不信邪的朋友可以尝试一下。
结语
自此,这个BUG算是处理完了,又学到了很多新知识,关于Selinux,关于安卓的安全防御机制。
如果你能从这篇博客中获取到有用的东西,点个赞。