Android SELinux基础知识
一、概念
作为 Android 安全模型的一部分,Android 使用安全增强型 Linux (SELinux) 对所有进程强制执行强制访问控制 (MAC),其中包括以 Root/超级用户权限运行的进程(Linux 功能)。很多公司和组织都为 Android 的 SELinux 实现做出了贡献。借助 SELinux,Android 可以更好地保护和限制系统服务、控制对应用数据和系统日志的访问、降低恶意软件的影响,并保护用户免遭移动设备上的代码可能存在的缺陷的影响。
SELinux 按照默认拒绝的原则运行:任何未经明确允许的行为都会被拒绝。SELinux 可按两种全局模式运行:
- 宽容模式(permissive ):权限拒绝事件会被记录下来,但不会被强制执行。
- 强制模式(Enforcing):权限拒绝事件会被记录下来并强制执行。
二、如何切换SELinux模式及问题确认
2.1 Selinux不同模式切换
android KK 4.4 版本后,Google 默认启用了SELinux即(Enforcing mode),SELinux 强制模式可以在 userdebug 或 eng build 中通过 ADB 停用。为此,请先运行 adb root 以将 ADB 切换为 root 权限。然后,如需停用 SELinux 强制模式,请运行以下命令
adb root
adb shell setenforce 0 //设置成permissive 模式
adb shell setenforce 1 //设置成enforce 模式
adb shell getenforce
Permissive
Enforcing
2.2 问题如何确认是Selinux 约束引起
userdebug或eng软件,通过adb命令(adb shell setenforce 0)临时关闭SElinux(使进入Permissive模式) 使请求都能被执行,重启失效。
如果问题消失,这样就能确认是SELinux 权限导致的问题。如果问题依然在,则是其他原因而非SELinux权限导致。
2.3 终端中查看安全上下文的方法
- 查看文件上下文命令:
adb shell ls -lZ
- 查看属性安全上下文:
adb shell getprop -Z
- 查看进程安全上下文:
adb shell ps -AZ
三、权限问题分析与修改
3.1 avc denied
adb logcat | grep “avc: denied” 过滤出报错log如下:
I auditd : type=1400 audit(0.0:276): avc: denied { write } for comm="eeme.backscreen" name="tiny_lcd" dev="tmpfs" ino=12463 scontext=u:r:backscreen_app:s0:c512,c768 tcontext=u:object_r:device:s0 tclass=chr_file permissive=0
格式如下:avc: denied { 操作权限 } for comm=“进程名” scontext=u:r:源类型:s0 tcontext=u:r:目标类型:s0 tclass=访问类型 permissive=0
源类型:授予访问的类型,通常是进程的域类型
目标类型:客体的类型,它被授权可以访问的类型
访问类型
操作权限:表示主体对客体访问时允许的操作类型(也叫做访问向量)
3.2 分析过程如下:
问题点 | 寻找点 | 结果 |
---|---|---|
缺什么权限 | denied | { write } |
谁缺少权限 | scontext=u:r: | backscreen_app |
对哪个文件缺少权限 | tcontext=u:object_r: | device |
什么类型的文件 | tclass= | chr_file |
3.3 解决方法:
套用公式: allow
backscreen_app.te
allow scontext tcontext:tclass 操作权限
allow backscreen_app device:chr_file write ;
编译image,此时会出现如下报错:
neverallow check failed at out/soong/.intermediates/system/sepolicy/recovery_sepolicy.cil/android_common/recovery_sepolicy.cil:12400 from system/sepolicy/public/domain.te:471
(neverallow domain device (chr_file (read write open)))
<root>
allow at out/soong/.intermediates/system/sepolicy/recovery_sepolicy.cil/android_common/recovery_sepolicy.cil:35615
(allow backscreen_app device (chr_file (ioctl read write getattr lock append map open watch watch_reads)))
因为直接按照LOG 转换出SELinux Policy: allow backscreen_app device:chr_file write ; 就会放开backscreen_app 读写所有device 的权限. 而Google 为了防止这样的情况, 使用了neverallow 语句来约束, 这样你编译sepolicy 时就无法编译通过
对于neverallow错误的解决方法,可以去另外百度,有直接修改neverallow规则,或者去提示violate的策略文件中加上我们需要的例外服务,但这些方法都违反了Google定义的强制规则,并可能导致CTS报错,所以在这里我只分享一种比较安全但是稍显复杂的方法:
为了规避这种权限放大情况, 我们需要细化访问目标(Object) 的SELinux Label, 做到按需申请. 通常会由三步构成
- 定义相关的SELinux type.
比如上述案例, 在 device/mediatek/common/sepolicy/device.te 添加
type oled_device, dev_type, mlstrustedobject; - 绑定文件与SELinux type.
比如上述案例, 在 device/mediatek/common/sepolicy/file_contexts 添加
/dev/tiny_lcd u:object_r:oled_device:s0
/dev/tiny_lcd是一个实际存在的节点
- 添加对应process/domain 的访问权限.
比如上述案例, 在 device/mediatek/common/sepolicy/backscreen_app.te 添加
allow backscreen_app oled_device:chr_file rw_file_perms;
完成后,编译刷机,此问题解决。
补充说明:
- 有时候avc denied的log不是一次性显示所有问题,要等你解决一个权限问题之后,才会提示另外一个权限问题。比如提示确实某个目录的read权限,你加入read之后,再显示缺少write权限,要你一次次一次试,一次一次加。这时你可以简单粗暴写个rw_dir_perms,这个权限包含了{open search write …}等等很多权限。
- 要加入的权限很多时,可以用中括号,比如:
allow system_app ota_package_file:file { write rename create unlink setattr };
3.4 那么哪些访问对象通常会遇到此类呢?
-
device
– 类型定义: external/sepolicy/device.te;
– 类型绑定: external/sepolicy/file_contexts; -
File 类型:
– 类型定义: external/sepolicy/file.te;
– 绑定类型: external/sepolicy/file_contexts; -
虚拟File 类型:
– 类型定义: external/sepolicy/file.te;
– 绑定类型: external/sepolicy/genfs_contexts; -
Service 类型:
– 类型定义: external/sepolicy/service.te;
– 绑定类型:external/sepolicyservice_contexts; -
Property 类型:
– 类型定义: external/sepolicy/property.te;
– 绑定类型: external/sepolicy/property_contexts;
通常我们强烈反对更新google default 的policy, 大家可以更新芯片厂商下面的相关的policy或者自己补充添加的policy