Android的Sepolicy实际上是对SELinux安全策略的描述和设置。
1.什么是SELinux
安全增强型 Linux(Security-Enhanced Linux)简称 SELinux,它是 Linux 的一个安全子系统。SELinux 主要作用是最大限度地减小系统中服务进程可访问的资源(最小权限原则)。对资源的访问控制分为两类: DAC和MAC.
DAC
在未使用SELinux的系统上, 对资源的访问是通过权限位来确定, 比如一个文件对所属用户是否有读、写、执行权限, 其他用户的访问可由所属用户进行配置. 这种由所属用户自己决定是否将资源的访问权或部分访问权授予其他用户,这种控制方式是自主的,即自主访问控制(Discretionary Access Control, DAC).
ls -l可查看权限
> ls -l note -rw-rw-r-- 1 ifantsai ifantsai 37 6月 17 13:36 note
MAC
在使用了 SELinux 的系统上,对资源的访问除了通过权限位判定外,还需要判断每一类进程是否拥有对某一类资源的访问权限。这种方式对资源的访问控制, 称之为强制访问控制(Mandatory Access Control, MAC).只给每个进程开放所需要的资源, 将权限开放到最小, 当进程出现漏洞时也只会影响到该进程所涉及的资源, 这大大提升了安全性.
SELinux工作模式
SELinux 有三种工作模式,分别为:
- enforcing: 强制模式, 执行SELinux规则, 违反的行为会被阻止
- permissive: 宽容模式, 执行SELinux规则, 违反的行不会被阻止
- disabled: 关闭SELinux
通过执行getenforce
命令可以获取当前SELinux的工作模式。
SELinux与Android
SELinux是Google从android 5.0开始,强制引入的一套非常严格的权限管理机制,主要用于增强系统的安全性。然而,在Android系统开发中,我们经常会遇到由于SELinux造成的各种权限不足,即使拥有“万能的root权限”,也不能获取全部的权限。
为了澄清是否因为SELinux导致的问题,可以尝试将SELinux工作模式临时改为宽容模式看问题是否解决, 来判定是否是SELinux引起的问题.
# 修改工作模式为宽容模式
setenforce permissive
# 临时禁用SELinux
setenforce 0
# 获取当前sepolicy配置
getenforce
遇到权限问题时, 在log中会打印avc denied提示缺少什么权限, 可以通过dmesg | grep avc
过滤出所有avc denied.
如果问题消失了,基本可以确认是SELinux造成的权限问题,需要通过正规的方式来解决权限问题。
遇到权限问题,在logcat或者kernel的log中一定会打印avc denied提示缺少什么权限,可以通过命令过滤出所有的avc denied,再根据这些log各个击破:
cat /proc/kmsg | grep avc
或
dmesg | grep avc
例如:
audit(0.0:67): avc: denied { write } for path="/dev/block/vold/93:96" dev="tmpfs" ino=1263 scontext=u:r:kernel:s0 tcontext=u:object_r:block_device:s0 tclass=blk_file permissive=0
可以看到有avc denied,且最后有permissive=0,表示不允许。
2.解决avc denied
解决原则是:缺什么权限补什么,一步一步补到没有avc denied为止。
解决权限问题需要修改的权限文件如下位置,以.te结尾
A:Android/devicesoftwinner/astar-common/sepolicy/*.te
B:Android/external/sepolicy/*.te
其中,A是对B的overlay(覆盖),能在A修改的尽量在A修改,尽量避免修改B,修改B可能会导致CTS fail问题,修改A不会影响CTS测试。
下面给出四个案例:
案例1
audit(0.0:67): avc: denied { write } for path="/dev/block/vold/93:96" dev="tmpfs" ino=/1263 scontext=u:r:kernel:s0 tcontext=u:object_r:block_device:s0 tclass=blk_file permissive=0
分析过程:
缺少什么权限: {undefined write }权限,
谁缺少权限: scontext=u:r:kernel:s0
对哪个文件缺少权限:tcontext=u:object_r:block_device
什么类型的文件: tclass=blk_file
完整的意思: kernel进程对block_device类型的blk_file缺少write权限。
解决方法:在上文A位置,找到kernel.te这个文件,加入以下内容:
allow kernel block_device:blk_file write;
make installclean后重新编译,刷boot.img才会生效
万能公式
通过案例,我们可以总结出一般规律,以下面为例
audit(1441759284.810:5): avc: denied { read } for pid=1494 comm="sdcard" name="0" dev="nandk" ino=245281 scontext=u:r:sdcardd:s0 tcontext=u:object_r:system_data_file:s0 tclass=dir permissive=0
某个scontext对某个tclass类型的tcontext缺乏某个权限,我们需要允许这个权限:
我们的log重新排列一下,
scontext = u:r:sdcardd
tcontex t= u:object_r:system_data_file:s0
tclass = dir
avc: denied { read }
在scontext所指的.te文件(例如sdcardd.te)中加入类似如下allow内容:
allow <scontext> <tcontext>:<tclass> {<avc: denied>}
案例2
在RK Android9.0上进行操作, 权限文件以.te
为后缀, 涉及到需要修改的路径:
android/device/rockchip/common/sepolicy
android/device/rockchip/rk3399/sepolicy
Android自带的进程服务通过以上目录配置即可 , 自己添加的第三方进程需要添加到自定义的目录下
以如下所示的avc denied为例讲解
# avc: denied { 操作权限 } for pid=7201 comm=“进程名” scontext=u:r:源类型:s0 tcontext=u:r:目标类型:s0 tclass=访问类别 permissive=0 avc: denied { read } for pid=7517 comm="audio@2.0-servi" name="u:object_r:default_prop:s0" dev="tmpfs" ino=11426 scontext=u:r:hal_audio_default:s0 tcontext=u:object_r:default_prop:s0 tclass=file permissive=0
主要关注以下内容:
denied {read}
: 表示缺少read权限scontext=u:r:hal_audio_default:s0
: 表示hal_audio_default缺少了权限tcontext=u:object_r:default_prop:s0
: 表示是对default_prop缺少了权限tclass=file
: 表示缺少权限的资源类型为file
因此只要在hal_audio_default.te文件中加入下面内容即可
allow hal_audio_default tcontext:file read;
如果需要赋予read, open权限, 当有多个权限时用{}
包裹
allow hal_audio_default tcontext:file { read open };
或者参考android/system/sepolicy/public/global_macros
中赋予一个复合权限, 如r_file_perms
表示{ getattr open read ioctl lock map }
3.常用命令
$ adb shell setenforce 0 #把Selinux设为permissive模式,该模式下selinux被临时禁用,对应的文件不会有selinux的权限限制 #但同时,avc denied的log依旧会打印出来,此时虽然avc denied的log还有,但确实时permissive状态,不会被Selinux限制读写等权限。 #可以观察avc denied的log最后permissive的值来看状态,permissive=1时代表当前处在兼容模式 $ adb shell getenforce #查看selinux的状态 #enforcing代表selinux enable #permissive代表selinux 临时 disable
4.SELinux avc权限规则快速生成配置
step1: 打印avc denied的SELinux相关Log
$ adb logcat | grep avc > avc.log
step2: 在源代码中找到audit2allow,并用audit2allow生成selinux avc权限配置信息
$ find . -name "audit2allow" $ ./audit2allow -i avc.log > selinux_config_info.log #selinux_config_info.log中会是类似如下的信息(以system_server为例) allow system_server sysfs:file { read write };
step3: 在生成的selinux avc权限配置信息的基础上加上 open 和 attr 权限,因为一般都需要open和attr的权限,如果不加可能会继续报attr或者open权限的avc denied错误
allow system_server sysfs:file { read write open attr };
step4: 在源代码的对应位置加上sepolicy的权限
一般在 Android_Source/device/<vendor>/<device>/sepolicy目录下,file_context, system_server.te等文件中
tips:
决定image生成哪些binary/so,放在 device/<vendor>/<device>/device-common.mk等mk文件中,比如EGL的设置等等。
修改权限问题,除了device/<vendor>/<device>/sepolicy目录下的te文件,还可以修改external/sepolicy目录下的te文件,但是前者是device specify的,意味着可以覆盖后者,一般我们改前者。并且改后者可能会导致CTS fail,因为把google默认的规则改了
5.Nerverallow问题与SELinux权限自定义
nerverallow问题概述
添加了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.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};
这样就客制化好了一个节点的写权限
6.Sepolicy的file_contexts与*.te文件解析
前文提到“对哪个文件缺少权限:tcontext=u:object_r:block_device”,其中的block_device是一个标签,可以有很多文件/设备都属于这个标签
那么file_contexts的内容就是来描述哪些设备/文件属于哪个标签的,一般存在Android源码中device/<vendor-name>/<board-name>/sepolicy/file_contexts中
以google cuttlefish中的文件为例:google/cuttlefish/shared/virgl/sepolicy/file_contexts
/vendor/lib(64)?/libglapi.so u:object_r:same_process_hal_file:s0 /vendor/lib(64)?/dri/.* u:object_r:same_process_hal_file:s0 #这里意思是/vendor/lib和/vendor/lib64/里的libglapi.so和dri目录下的所有文件/设备都属于same_process_hal_file这个标签下
而app.te中有下列内容,即te文件描述allow等规则时是以标签为粒度的
... allow appdomain same_process_hal_file:file { read execute_no_trans }; ... #意思是所有应用都有对属于same_process_hal_file标签的文件/设备的读和执行权限
这样就串起来了,所有应用都有libglapi.so和dri下文件的读、执行权限。
总结起来,file_contexts负责描述标签,*.te文件负责描述规则
政策格式规则如下:
政策规则采用以下形式: allow domains types:classes permissions; 其中: Domain - 一个进程或一组进程的标签。也称为域类型,因为它只是指进程的类型。 Type - 一个对象(例如,文件、套接字)或一组对象的标签。 Class - 要访问的对象(例如,文件、套接字)的类型。 Permission - 要执行的操作(例如,读取、写入)。
使用政策规则时结构示例:
allow appdomain app_data_file:file rw_file_perms; #这表示所有应用域都可以读取和写入带有 app_data_file 标签的文件
参考链接:
SELinux之解决avc denied: https://www.caiyifan.cn/p/38175be7.html
Android SELinux avc dennied权限问题解决方法: https://blog.csdn.net/tung214/article/details/72734086
Android修改Selinux avc权限的方法: https://blog.csdn.net/jppipai/article/details/129093013
android selinux打开 安卓selinux开启: https://blog.51cto.com/u_14191/6601762
Android进阶-SELinux https://blog.csdn.net/qq_33750826/article/details/80831431
Selinux Android 官方链接:https://source.android.com/docs/security/features/selinux?hl=zh-cn