AOSP安全策略SElinux_android以及问题排查思路

一、安全属性(格式):

在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进程发送信号
  • allowneverallowdontauditauditallow 策略或指令的含义如下:
  1. allow:允许特定操作或访问。这意味着指定的主体(如用户或进程)可以执行某些动作。

  2. neverallow:明确禁止特定操作或访问。这是一个强制性的限制,确保指定的主体无论如何都无法执行某些动作。

  3. dontaudit:表示不记录特定操作的审计日志。即使该操作被拒绝或允许,也不会生成审计记录。

  4. 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 相关问题确认
  1. 烧⼊user版本的镜像后如果发现有相关功能⽆法正常⼯作了,但是烧⼊userdebug版本的镜像后发现可以正常⼯作,有可能是权限没有添加成功导致。

  2. 如果出现了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
    
  3. 另外当出现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 };
  • 注意
  1. 有时候avc denied的log不是⼀次性显⽰所有问题,要等解决⼀个权限问题之后,才会提⽰另外⼀个权限问题,⽐如提⽰缺失某个⽬录的read权限,你加⼊read之后,再显⽰缺少write权限,要你⼀次⼀次试,⼀次⼀次加。所以建议在permissive模式下进⾏debug和添加权限,否则会⽆限放⼤⼯作量。

  2. 要加⼊的权限很多时,可以⽤中括号或者使⽤宏(强烈建议使⽤宏,兼容性更好),⽐如allowengsetmacaddr vfat:dir { search write add_name create};

4.1.3、利⽤audit2allow 简化⽅法添加log
  1. 从 logcat 或串⼝中提取相应的 avc-denied log,下⾯的语句为提取所有的 avc- denied log

    $ adb shell "cat /proc/kmsg | grep avc" > avc_log.txt
    
  2. 使⽤ audit2allow ⼯具⽣成对应的 policy 规则audio2allow 使⽤必须先 source build/envsetup.sh,导⼊环境变量,可能还需要执行lunch。

    audit2allow -i avc_log.txt
    
  3. 将对应的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服务

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,163评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,301评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,089评论 0 352
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,093评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,110评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,079评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,005评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,840评论 0 273
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,278评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,497评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,667评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,394评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,980评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,628评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,796评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,649评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,548评论 2 352

推荐阅读更多精彩内容