一、安全属性(格式):
在SELinux世界里,每种东西都会被赋予一个安全属性(官方说法叫Security Context,后文简称SContext
)。
Linux中的所有东西,包括<u>活的进程</u>、<u>死的资源</u>都有自己对应的SContext
安全属性,因此可以把SContext分
为进程SContext和资源SContext,无论哪种安全属性,他们的本质都是一个字符串,其格式如下:
user:role:type[:range]
#user表示用户:SEAndroid中仅定义了一个SELinux用户,用u表示
#role表示角色:可以暂时理解为在SELinux用户可以有多个不同的role,不同role所具有的权限也不一样
#type表示类型:不同的type所具有的权限也不一样,但跟role有本质的不同
#range表示安全级别:通常为s0,不用怎么关注
# 例如: u:object_r:sysfs_video:s0
/sys/class/resource_mgr/res_report u:object_r:sysfs_video:s0
/vendor/bin/gpio_pin_control\.sh u:object_r:auto-shell-sh_exec:s0
1.1、进程SContext
console:/ # ps -Z -A
LABEL USER PID PPID VSZ RSS WCHAN ADDR S NAME
u:r:init:s0 root 1 0 10944672 11988 do_epoll_wait 0 S init
u:r:kernel:s0 root 2 0 0 0 kthreadd 0 S [kthreadd]
u:r:kernel:s0 root 3 2 0 0 rescuer_thread 0 I [rcu_gp]
u:r:kernel:s0 root 4 2 0 0 rescuer_thread 0 I [slub_flushwq]
u:r:kernel:s0 root 5 2 0 0 rescuer_thread 0 I [netns]
u:r:kernel:s0 root 7 2 0 0 worker_thread 0 I [kworker/0:0H-kblockd]
u:r:kernel:s0 root 9 2 0 0 rescuer_thread 0 I [mm_percpu_wq]
u:r:kernel:s0 root 11 2 0 0 rcu_tasks_kthread 0 S [rcu_tasks_kthre]
u:r:kernel:s0 root 12 2 0 0 rcu_tasks_kthread 0 S [rcu_tasks_trace]
u:r:kernel:s0 root 13 2 0 0 smpboot_thread_fn 0 S [ksoftirqd/0]
u:r:kernel:s0 root 14 2 0 0 rcu_gp_fqs_loop 0 I [rcu_preempt]
输入命令ps -Z -A能够查询当前所有进程的安全属性,最右边列表示进程名称,最左边列表示该进程对应的安全属性。大多数的进程的SContext的user值为u表示SEAndroid用户;role值为r表示进程;range值为s0。
1.2、资源SContext
#查看根目录的安全属性
console:/ # ls -l -Z
total 92
drwxr-xr-x 2 root root u:object_r:cgroup:s0 4096 2009-01-01 00:00 acct
drwxr-xr-x 27 root root u:object_r:apex_mnt_dir:s0 560 1970-01-01 00:00 apex
lrw-r--r-- 1 root root u:object_r:rootfs:s0 11 2009-01-01 00:00 bin -> /system/bin
lrw-r--r-- 1 root root u:object_r:cache_file:s0 11 2009-01-01 00:00 cache -> /data/cache
drwxr-xr-x 3 root root u:object_r:configfs:s0 0 1970-01-01 00:00 config
lrw-r--r-- 1 root root u:object_r:rootfs:s0 17 2009-01-01 00:00 d -> /sys/kernel/debug
drwxrwx--x 50 system system u:object_r:system_data_root_file:s0 4096 2024-10-21 12:22 data
drwx------ 8 root system u:object_r:mirror_data_file:s0 160 1970-01-01 00:00 data_mirror
drwxr-xr-x 2 root root u:object_r:tmpfs:s0 4096 2009-01-01 00:00 debug_ramdisk
drwxr-xr-x 24 root root u:object_r:device:s0 5120 1970-01-01 00:00 dev
在Android系统根目录执行ls -Z命令,能够查看根目录文件的SContext。大多数资源的SContext的user值为u代表SEAndroid用户;role值为object_r代表文件;range值为s0。
1.3、TE术语:
这里细分为 主体、客体、客体类别、访问许可.
- AV规则:既然已经有了主体和客体,那么就有对应访问规则,又叫AV规则。属于SElinux策略语言的控制部分,即te文件中每一条AV规则语句都需要能够描述清楚:主体 对 客体 是否具有 访问许可。其实就是te文件里面的allow、neverallow、dontaudit、auditallow等语句。
#一条完整的te安全控制语句格式应该为: AV规则 主体 客体:客体类别 许可
#示例1:
allow netd proc:file write
#allow为AV规则,表示允许
#netd为主体,一般为进程SContext的type
#proc为客体,一般为资源SContext的type
#file为客体类别,表示proc是一个文件
#write为许可,表示写权限
#综上可以解读为:允许域名为netd的所有进程具有对资源文件proc的write权限
#示例2:
allow zygote init:process sigchld;
#allow为AV规则,表示允许
#zygote为主体
#init为客体,一般为资源SContext的type
#process为客体类别,表示init是一个进程
#sigchld为许可,表示发送信号
#综上可以解读为:允许zygote进程向init进程发送信号
-
allow
、neverallow
、dontaudit
和auditallow
策略或指令的含义如下:
allow:允许特定操作或访问。这意味着指定的主体(如用户或进程)可以执行某些动作。
neverallow:明确禁止特定操作或访问。这是一个强制性的限制,确保指定的主体无论如何都无法执行某些动作。
dontaudit:表示不记录特定操作的审计日志。即使该操作被拒绝或允许,也不会生成审计记录。
auditallow:允许特定操作并记录审计日志。这意味着如果该操作被执行,会生成相应的审计记录,以便后续检查和监控。
allow
在实际开发中通常用的最多的还是allow语句,该语句格式如下:
allow 主体type 客体type 客体类别: {权限}
当SELinux拒绝访问权限的时候,将会打印如下日志:
avc:denied { map } for path="/data/local/data/mute.png" dev="mmcblk0p37" ino=5338 scontext=u:r:bootvideo:s0 tcontext=u:object_r:system_data_file:s0 tclass=file permissive=0
像这类日志都是以关键字avc开头,需要注意其中几个很重要的字段:
- denied { 权限 }:找到denied字段,后面通常跟随大括号,里面的内部就表示被拒绝的操作,即什么操作权限被拒绝,例如读写查等权限
- scontext=主体上下文:找到scontext字段,后面跟随的为主体安全上下文SContext,即代表主体进程
- tcontext=客体上下文:找到tcontext字段,后面跟随的为客体安全上下文SContext,即代表被访问单个资源或一系列资源的上下文
- tclass=客体类别:找到tclass字段,后面为客体类别
通过上面几个关键字,我们就可以为其配置专门的AV规则,安装allow语句的规则可以在对应主体进程的te文件下添加allow语句:
allow bootvideo system_data_file:file { map }
二、策略文件(.te):
android默认的策略配置都基本上写好了,这些策略配置基本上不需要我们去更改。但是厂商定制策略文件的sepolicy目录下。这里介绍该目录下几个比较重要的文件。
2.1、sepolicy/vendor/service.te
service.te文件主要定义了一些service(init.rc服务对应的进程)的type。即定义了init.rc中的service对应进程的SContext的type字段。这些type通常会继承于service_manager_type。如下:
type hitv_service, service_manager_type;
type bootvideo_service, service_manager_type;
type quickplay_service, service_manager_type;
type shen_service, service_manager_type;
2.2、sepolicy/system/private/service_contexts
前文定义了一些type,但是这些type并没有得到使用,service_contexts文件就是用来配置Service(四大组件之一)系统服务的安全属性SContext,其中使用service.te里面定义的type(进程的type也叫域名Domain)。如下:
TVService u:object_r:tvservice_service:s0
hitvservice u:object_r:tvservice_service:s0
hirmservice u:object_r:tvservice_service:s0
bootvideo u:object_r:bootvideo_service:s0
quickplay u:object_r:quickplay_service:s0
shen u:object_r:shen_service:s0
2.3、sepolicy/vendor/shen.te
前文已经为init.rc将启动的进程做了安全属性的配置,那么可以在为每个进程做具体的安全策略,通常每个自定义进程就会对应一个te文件,编译系统打包过程中会自动将sepolicy目录下的配置拼接到原生SEAndroid中。这里拿自定义进程shen来举例,那么就需要创建一个shen.te文件来配置具体安全策略。如下:
type shen,coredomain,domain;
#定义shen,继承于coredomain和domain,将代表某一组进程的域名
type shen_exec,exec_type,file_type;
#定义shen_exec,继承于exec_type和file_type,将代表某一资源,继承了执行权限
init_domain_domain(shen)
#具体安全策略
allow 语句
neverallow 语句
2.4、sepolicy/vendor/file_contexts
前文配置了进程SContext,那么这里还需要配置资源SContext,如下:
#Devices
/dev/sockect/vold0 u:object_r:vold_sockect:s0
/dev/sockect/vold1 u:object_r:vold_sockect:s0
#System files
/system/bin/bootvideo u:object_r:bootvideo_exec:s0
/system/bin/quickplay u:object_r:quickplay_exec:s0
/system/bin/shen u:object_r:shen_exec:s0
如上配置文件/system/bin/shen的type为shen_exec,前文继承于exec_type,表示该资源为一个可执行文件。
三、配置新进程的SEAndroid
service shen /system/bin/shen
user root
class core
现如今就可以在init.rc中配置如上代码,让init进程fock创建一个进程shen,并执行了/system/bin/shen里面的代码。其中新进程名叫shen,执行的资源文件是/system/bin/shen,但是仅仅这样,系统开机启动后并没有启动进程shen,原来是因为init进程在启动rc文件中的service之前初始化了selinux相关的东西,因此selinux校验不过,进程shen无法被启动,因此需要作如下步骤配置:
3.1、配置进程shen的安全策略
创建sepolicy/vendor/shen.te文件:
type shen,coredomain,domain;
#定义shen,继承于coredomain和domain,将代表某一组进程的域名
type shen_exec,exec_type,file_type;
#定义shen_exec,继承于exec_type和file_type,将代表某一资源,继承了执行权限
init_domain_domain(shen)
binder_use(shen)
binder_service(shen)
binder_call(shen,system_server)
hwbinder_use(shen)
set_prop(shen,exported_system_prop)
set_prop(shen,exported2_system_prop)
allow shen mediaserver_service:service_manager { find };
allow shen mediaserver:binder { call};
allow shen surfaceflinger_service:service_manager { find };
allow shen surfaceflinger:binder { call };
allow shen surfaceflinger:fd { call };
allow shen system_file:dir { read open };
allow shen system_prop:property_service { set };
allow shen storage_file:link_file {read open };
allow shen system_data_file:file {read open map };
上面的策略文件,先定义了两个type,还记得type是什么吗?没错,是Linux任何东西(包括进程和资源)都具有的SContext安全属性中的type。上面的shen.te文件前两句就定义了两个type:shen和shen_exec,其中shen继承于domain,shen_exec继承于file_type。
1)、domain
深入原生sepolicy代码中的定义就会知道,domain专门用来修饰进程SContext的type,表示一个域(一组进程),我们的进程叫shen,因此用字符串shen来表示这组进程,在后面的allow语句中都是allow shen xxxx来实现安全策略。
2)、exec_type
file_type是用来修饰资源SContext的type,这里定义了一个shen_exec,并继承于exec_type/file_type,其中exec_type表示可执行文件,因此只要有某资源安全属性的type字段就表示它能够被执行。在后文中我们会把shen_exec赋予/system/bin/shen文件。
3)、init_domain_domain
init_domain_domain(shen)是一个宏,声明当一个domain为init的进程创建一个子进程执行一个type为study_exec的文件时,将该子进程的domain设置为shen,而不是继承父进程(它的父进程就是init进程)的domain。
3.2、赋予二进制文件SContext
rc文件中指定进程shen执行/system/bin/shen这个二进制文件,但到目前为止,这个文件就是一个普普通通的文件,shen进程读取该文件的代码时候将会被selinux权限校验的时候拒绝,因此我们还需要对该文件赋予可被执行的type。
sepolicy/vendor/file_contexts专门用来配置所有资源的SContext,因此我们只需要在该文件中进行如下配置即可:
#sepolicy/vendor/file_contexts
/system/bin/shen u:object_r:shen_exec:s0
3.3、赋予binder通信功能
经过上面两个步骤init可以成功启动进程shen,但是此时的shen并不能在系统生态中使用binder进程间通信功能。
sepolicy/vendor/service.te中定义继承于service_manager_type的type,如下:
type shen_service, service_manager_type;
将拥有service_manager_type的type赋予给进程shen,如下:
shen u:object_r:shen_service:s0
四、问题思路
4.1、如何定位Selinux权限问题?
4.1.1、Sepolicy 相关问题确认
烧⼊user版本的镜像后如果发现有相关功能⽆法正常⼯作了,但是烧⼊userdebug版本的镜像后发现可以正常⼯作,有可能是权限没有添加成功导致。
-
如果出现了selinux相关的权限拒绝,则在kernel log 或者android log中都有对应的”avc:denied”,当然也可能和selinux的模式有关系,我们需要⾸先要确认当时SELinux 的模式, 是enforcing mode 还是 permissve mode。如果问题容易复现,我们可以先将SELinux 模式调整到Permissive mode,然后再测试确认是否与SELinux 约束相关。
adb shell setenforce 0 setenforce 0 设置SELinux 成为permissive模式 临时关闭selinux setenforce 1 临时打开selinux
-
另外当出现selinux相关问题,我们可以通过下⾯的命令来抓取对应的log:
adb shell logcat | grep avc 或 ad b shell dmesg | grep avc
4.1.2、Sepolicy Rule 添加及验证
1.根据log推导出需要添加的权限 :
//例1
audit(1444651438.800:8): avc: denied { search } for pid=158 comm="setmacaddr"
name="/" dev="nandi" ino=1 scontext=u:r:engsetmacaddr:s0
tcontext=u:object_r:vfat:s0 tclass=dir permissive=0
//缺少什么权限: 缺少 search权限
//谁缺少权限: engsetmacaddr
//对哪个节点缺少权限: vfat
//什么类型的⽂件: dir
//最后输⼊的命令:
allow engsetmacaddr vfat:dir { search };
//例2
auditd ( 627): avc: denied { write } for pid=15848 comm=“system_server”
name=“enable” dev=“sysfs” ino=9381 scontext=u:r:zygote:s0
tcontext=u:object_r:sysfs:s0 tclass=file permissive=1
//缺少什么权限: 缺少 write 权限
//谁缺少权限: scontext=u:r:zygote:s0
//对哪个⽂件缺少权限: tcontext=u:object_r:sysfs:s0
//什么类型的⽂件: tclass=file
//最后输⼊的命令:
allow zygote sysfs:file write
//例3
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
//缺少什么权限: 缺少 read 权限
//谁缺少权限: sdcardd
//对哪个⽂件缺少权限: system_data_file
//什么类型的⽂件: dir
//最后输⼊的命令
allow sdcardd system_data_file:dir read
//例4 -- 针对ioctl的特殊说明
(avc: denied { ioctl } for path="/dev/cmx_ddlsw" dev="tmpfs" ino=10422
ioctlcmd=4d02 scontext=u:r:system_server:s0
tcontext=u:object_r:cmx_ddlsw_device:s0 tclass=chr_file permissive=0)
//在rules中添加ioctl后,还需要声明具体的iocmd,必须结合源代码添加,例如:
allow system_server cmx_ddlsw_device:file { ioctl };
allowxperm system_server cmx_ddlsw_device:file ioctl { 0x4d02 };
- 注意
有时候avc denied的log不是⼀次性显⽰所有问题,要等解决⼀个权限问题之后,才会提⽰另外⼀个权限问题,⽐如提⽰缺失某个⽬录的read权限,你加⼊read之后,再显⽰缺少write权限,要你⼀次⼀次试,⼀次⼀次加。所以建议在permissive模式下进⾏debug和添加权限,否则会⽆限放⼤⼯作量。
要加⼊的权限很多时,可以⽤中括号或者使⽤宏(强烈建议使⽤宏,兼容性更好),⽐如allowengsetmacaddr vfat:dir { search write add_name create};
4.1.3、利⽤audit2allow 简化⽅法添加log
-
从 logcat 或串⼝中提取相应的 avc-denied log,下⾯的语句为提取所有的 avc- denied log
$ adb shell "cat /proc/kmsg | grep avc" > avc_log.txt
-
使⽤ audit2allow ⼯具⽣成对应的 policy 规则audio2allow 使⽤必须先 source build/envsetup.sh,导⼊环境变量,可能还需要执行lunch。
audit2allow -i avc_log.txt
将对应的policy 添加到 te ⽂件中⼀般添加在 /device/<company>/common/sepolicy 或者/device/<company>/$DEVICE/sepolicy ⽬录下。
4.1.4 使修改⽣效
根据上述log进⼊$SDK/device/rockchip/common,将相应的权限语句添加到相应的⽂件中,重新编译烧写进⾏验证。
4.2、违反neverallow规则解决思路
遇到neverallow规则问题,千万别急着去注释/剔除里面原有的规则(原生的尽量别动它!)。增加allow规则是常见的解决办法,但是随着 android 版本的升级,系统对 selinux 的管控越来越严,增加了大量的 neverallow。一般情况下,向默认标签授予权限的做法是错误的。其中许多权限都是 neverallow 规则所不允许的。按照上面方法去添加 selinux 可能会违反了 neverallow规则,编译时候会报 neverallow 相关的错误。
4.2.1、访问 default property 超过权限
这是比较常见的 neverallow 错误,比如在 systemserver 进程中去对新加的属性设置属性,运行时就会报 default propert 的错误。
遇到这种类型的错误,首先需要给新增的属性添加标签。在 property.te 中定义 property
type demo_prop, property_type;
在 Android 12 中,由于新的 neverallow,如果对 file 进行操作将会编译不过
neverallow domain {
property_type
-system_property_type
-product_property_type
-vendor_property_type
}:file no_rw_file_perms;
新的 property type 需要指出具体是什么 property type,比如用于 vendor 就改成如下
type demo_prop, property_type, vendor_property_type;
但是如果这个属性还需要在 coredomain 中使用,比如 shell,那么上面写法还是会报一样的 neverallow,需要额外添加 vendor_restricted_property_type 或者是 vendor_restricted_prop(demo_prop)
在 proerty_contexts 中匹配所需要访问的 property
prop u:object_r:demo_prop:s0
然后再根据提示添加政策,比如例子是在systemserver中操作,所以在system_server.te中添加如下政策:
allow system_server demo_prop:property_service set;
4.2.2、为新服务添加标签并解决拒绝事件
比如新添加了一个 java 服务,那么需要在 service.te 中新增对应 type: type new_service, service_manager_type; 然后在 service_contexts 中匹配servicename u:object_r: new_service:s0
最后添加根据报错添加相关政策
native 的服务一般是通过 rc 文件定义的可执行程序拉起并注册进 ServiceManager 的,所以要对可执行程序做如下权限配置:
创建一个新域 newdomain.te
type newdomain, domain;
type newdomain _exec, exec_type, file_type;
init_daemon_domain(newdomain)
在 file_contexts 添加对应标签
Service 地址 u:object_r:newdomain _exec:s0
最后添加相关的政策
payjoy移植过程中遇到过这个问题,同时注意一定要在项目文件中进行配置(不要去配Google那边的了)。部分配置如下:
./service_context.te
# Start of PayJoy changes
payjoy_access_service u:object_r:payjoy_access_service:s0
# End of PayJoy changes
./service.te
# Start of PayJoy changes
type payjoy_access_service, system_server_service, service_manager_type;
# End of PayJoy changes
不要少了任何一步,否则都有可能不成功。
4.2.3、新设备节点增加访问权限
bsp 创建了一个新的设备节点,即使权限是 777,android 层也是没有访问权限的。
同样首先在 device.te 中添加定义
type new_device, dev_type;
然后在 file_context 中添加对应标签
device 路径 u:object_r:md_block_device:s0
最后添加相关的政策
总结:对于neverallow 需要缩小权限,明确具体需要对什么添加权限。然后根据需要的类型添加对应的 type 和 context 标签, 最后添加对应的政策。
具体应用可以移步:
AOSP添加新的开机服务的权限 和 AOSP 8.0+添加一个HAL服务