理论知识
在学习如何分析问题前,我们首先对其基本概念进行了解以及解析
概念
什么是SELinux,SELinux 即Security-Enhanced Linux,由美国国家安全局(NSA)发起,Secure Computing Corporation (SCC) 和 MITRE 直接参与开发,以及很多研究机构(如犹他大学)一起参与的强制性安全审查机制。该系统最初是作为一款通用访问软件,发布于 2000 年 12 月(代码采用 GPL 许可发布)。在Linux Kernel 2.6 版本后,直接整合进入SELinux,搭建在Linux Security Module(LSM)基础上。目前已经成为最受欢迎、使用最广泛的安全方案。
SELinux 是典型的MAC-Mandatory Access Controls实现,对系统中每个对象都生成一个安全上下文(Security Context),每一个对象访问系统的资源都要进行安全上下文审查。审查的规则包括类型强制检测(type enforcement),多层安全审查(Multi-Level Security),以及基于角色的访问控制(RBAC: Role Based Access Control)。
SELinux 搭建在Linux Security Module(LSM)基础上,关于 LSM 架构的详细描述请参见文章 “Linux Security Modules: General Security Support for the Linux Kernel”,该文章在 2002 年的 USENIX Security 会议上发表。有完整的实现LSM 的所有hook function。
SElinux?SEAndroid?
Android 是建立在标准的Linux Kernel 基础上的,自然也可以开启SELinux。在通用移动平台上,通常很少开启这样的安全服务,Google 为了进一步增强Android 的安全性,经过长期的准备,目前已经在Android 5.0(L) 上有完整地开启SELinux,并对SELinux 进行深入整合形成了SEAndroid。
SELinux在Android的更新过程
K 4.4 针对netd, installd, zygote, vold 四个原本具有root 权限的process,以及它们fork 出的子进程启用Enforce 模式。
- L 版本普遍性开启SELinux Enforce mode。
- Permissive 模式,只打印audit 异常LOG,不拒绝请求;Enforce 模式,既打印audit 异常LOG,也拒绝请求。
从Android N 版本开始的后续版本,Google都会要求强制性开启SELinux,以保证手机安全。
从Android O 版本后,Google大幅度地增强了SELinux的限制,特别是限制System/Vendor之间的交叉使用,如VNDK的权限控制就主要由selinux来控制。
总体来说,Selinux 在android上的使用过程是一个从具体启用到整体启用、从粗粒度限制到精细控制的过程。
SELinux 给Android 带来的影响
- 严格限制了ROOT 权限,以往ROOT “无法无天” 的情况将得到极大的改善。
- 通过SELinux保护,降低系统关键进程受攻击的风险,普通进程将没有权限直接连接到系统关键进程。
- 进一步强化APP的沙箱机制,确保APP难以做出异常行为或者攻击行为。
- 将改变APP一旦安装权限就已经定死的历史,APP权限动态调整将成为可能。
SELinux mode
SELinux分为两种模式,即分别为Permissve Mode(宽容模式)和Enfocing Mode(强制模式)
- Permissive Mode:只通过Kernel Audit System 记录LOG,但不真正拦截访问。
- Enfocing Mode:不止会记录日志,也会真正进行拦截访问
一般在调试时候使用Permissive Mode,然后在最后修正时使用Enfocing Mode一次修正,在量产阶段保护系统。
如何在boot阶段改变SELinux模式
步骤1:
在
(使用lk的平台:) /vendor/mediatek/proprietary/bootable/bootloader/lk/platform/mtxxxx/rules.mk
或
(使用lk2的平台:)/vendor/mediatek/proprietary/bootable/bootloader/lk2/platform/mediatek/mtxxxx/rules.mk
中,将:
choose one of following value -> 1: disabled/ 2: permissive /3: enforcing
SELINUX_STATUS := 3
修改为:
choose one of following value -> 1: disabled/ 2: permissive /3: enforcing
SELINUX_STATUS := 2
步骤2(只在 user load 中需要这一步操作):
/system/core/init/selinux.cpp
bool IsEnforcing() {
return false; //强制将 SELinux 切到 permissive 模式。
if (ALLOW_PERMISSIVE_SELINUX) {
return StatusFromCmdline() == SELINUX_ENFORCING;
}
return true;
}
步骤3(只在 Android R 及后续版本的 user load 中需要这一步操作) :
对于kernel-4.1x版本:
在alps/kernel-x.xx/arch/armxx/configs/xxxx_defconfig (注意不是 xxxx_debug_defconfig,debug_defconfig 是eng load 的)中,设置:
CONFIG_SECURITY_SELINUX_DEVELOP=y
例如:
alps/kernel-4.14/arch/arm64/configs/k69v1_64_tee_cts_defconfig
对于Android S及后续版本中的kernel-5.10及后续版本的GKI2.0平台(如:MT6983/MT6895/MT6879)的mgk defconfig中,设置:
CONFIG_SECURITY_SELINUX_DEVELOP=y
例如:
alps/kernel-5.10/arch/arm64/configs/mgk_64_k510_defconfig
步骤4(只在 Android S及后续版本中的kernel-5.10及后续版本的GKI2.0平台(如:MT6983/MT6895/MT6879)中需要这一步操作):
禁用prebuilt boot.img替换:
在device/mediatek/vendor/common/BoardConfig-vext.mk中,将
MTK_USE_PREBUILT_BOOTIMAGE := yes
修改为:
MTK_USE_PREBUILT_BOOTIMAGE := no
DAC and MAC
DAC 即 Discretionary Access control,自主访问控制,即系统只提供基本的验证,完整的访问控制由开发者自己控制。
MAC 即 Mandatory Access control,强制性访问控制,即系统针对每一项访问都进行严格限制,具体的限制策略由开发者给出。
Linux DAC
Linux DAC 采用了一种非常简单的策略,将资源访问者分成三类,分别是Owner, Group, Other; 资源针对这三类访问者设置不同的访问权限。而访问权限又分成 read, write, execute。
访问者通常是进程,有自己的uid/gid,通过uid/gid 和 文件权限匹配,来确定是否可以访问。
将Root权限根据不同的应用场景划分成许多的Root Capabilities,其中如果有CAP_DAC_OVERRIDE 这项的话,可以直接绕过Linux DAC 限制。
Linux DAC 有明显的不足,其中一个重要点就是Root 权限 “无法无天”,几乎可以做任意事情,一旦入侵者拿到root 权限,就已经完全掌控了系统。另外,每一个进程默认都拿到对应这个用户的所有权限,可以改动/删除这个用户的所有文件资源。很明显,这个难以防止恶意软件。
Linux MAC
Linux MAC 针对DAC 的不足,要求系统对每一项访问进行检查,每访问一个文件资源都需要进行针对性的验证。而这个针对性的验证,是根据已经定义好了的策略进行的。在Linux Kernel中,所有的MAC机制都搭建在Linux Security Modules (LSM) 基础上,包括:SELinux、Apparmor、Smack 和 TOMOYO Linux等。目前SELinux已经成了事实上的行业标准。
相对于Linux DAC,MAC 可以明显弥补DAC 的缺陷。一方面,限制了Root 权限,即使你有root 权限,如果无法通过MAC 验证,那么一样地无法真正执行相关的操作。另外,对每一项权限进行了更加完整的细化,可限制用户对资源的访问行为。
.查看Security Context
使用ps -A -Z 或者cat proc/PID/attr/current,查看当前进程可用ID
查看文件的Security Context 用:ls -Z
SELinux Policy limited by Google
从一开始,Google 就已经通过neverallow 语法以及 CTS 测试对selinux policy 进行了初步限制。
-
维持system/sepolicy 与Google AOSP一致,尽量不要修改。特别是不要去除任何的neverallow 语句。
-
严禁修改untrusted_app.te,放开untrusted_app 的权限。
-
系统关键进程启动长时间运行的process,必须进行domain 切换。比如netd, installd, vold, zygote 等。
-
Native thread 严禁使用kernel 标签,而kernel thread 除init 外,都应当是kernel 标签。
-
在user build 中不能存在如su, recovery, init_shell 标签的进程。
-
在user build 中所有的process 都必须是Enforce Mode。
在后续的版本更新中,Google 导入了大批量的限制性 neverallow 语法,特别是从O 版本开始针对System/Vendor 的分离,进行了大量针对性的限制,具体可以参考Google 在/system/sepolicy 中所设定的neverallow 语句。
问题的发现以及快速分析
确认与SELinux相关
要分析问题,我们首先需要找到一个方向。那么我们如何快速找到问题的发生点确认问题是否与SELInux相关呢?主要是两个方法
-
将SELinux 调整到Permissive 模式测试
将SELinux 模式调整到Permissive 模式,然后再测试确认是否与SELinux 约束相关。
ENG 版本:
adb shell setenforce 0
如果还能复现问题,则基本可以确定当前root cause与SELinux 无关。如果原本很容易复现,而Permissive mode 不能再复现,那么就可能关系比较大。 -
查看LOG 中是否有标准的SELinux Policy Exception
在Kernel LOG / Main Log 中查询关键字 “avc:” 看看是否有SELinux Policy Exception,并进一步确认这个异常是否与当时的逻辑相关。
如何设置与确认SELinux 模式?
如何确认SELinux 模式
在ENG/USER 版本中,都可以使用getenforce 命令进行查询,如:
root@mt6589_phone_720pv2:/ # getenforce
getenforce
Enforcing
如何设置SELinux 模式
在ENG/USERDEBUG 版本中,可以使用setenforce 命令进行设置:
adb shell setenforce 0 //设置成permissive 模式
adb shell setenforce 1 //设置成enforce 模式
在USER 版本中无法使用setenforce 命令进行设置。
如何分析SELinux Policy Exception
我们来看一下,一份非常典型的SELinux问题报错。
<5>[ 27.706805] (3)[304:logd.auditd]type=1400 audit(1420041991.220:17): avc: denied { execute } for pid=2182 comm=“app_process” path=“/data/dalvik-cache/arm64/system@framework@boot.oat” dev=“mmcblk0p18” ino=15109 scontext=u:r:root_channel:s0 tcontext=u:object_r:dalvikcache_data_file:s0 tclass=file permissive=0
现在我们来分解一下这段日志每一小节的含义
- <5>:kernel的日志等级
- [ 27.706805]:发生的时间
- (3):CPU数
- [304:logd.auditd]:问题发生在audit
- type=1400 : SYSCALL
type=AVC - for kernel events
type=USER_AVC - USER版本 - audit(1420041991.220:17) : audit(时间:序列号)
- avc: denied { execute }:字段取决于正在审核的事件类型。
- pid=2182 comm=“app_process” :如果是任务,则记录进程ID以及执行文件的名称
- path=“/data/dalvik-cache/arm64/system@framework@boot.oat” dev=“mmcblk0p18” ino=15109:目标信息
- permissive: permissive (1) or enforcing (0)
分解出来后,我们就可以简单翻译成:在enforcing模式下,auditd执行app_process的execute事件发生权限错误,PID是2182。
基本分析流程
- 将SELinux 调到 permissive mode,然后一次性抓取出所有的"avc:" 的LOG。
- 如果问题与开机流程无关,并且eng 版本能够复现。
adb shell setenforce 0 - 如果问题与开机流程相关,或者只有user build 能够复现。参考?“”“”设置permissive mode
- 确认访问是否是必须的?是否是违法恶意访问?
- 如果是正常访问,确认访问的目标类型是否太过广泛,如果太过广泛,则需要细化具体的文件目标。
- 添加对应的SELinux Policy 到对应的Policy 文件。
如何处理与Google 定义neverallow 冲突
其实对于这一点只要记住一个关键点,谷歌禁止的绝对不要在原文件进行修改,如果一定要修改其权限请重新创建一个process domain进行对节点的申请
配置路径
- 对于系统本身SEpolicy,并且在所有版本中都可以获取的,我们放在device/mediatek/sepolicy/base
- 对于debug版本申请的SEpolicy,我们放在device/mediatek/sepolicy/debug
- 对于三方vendor申请的SEpolicy,放在device/mediatek/sepolicy/third_party