笔者在工作中多次遇到和SELinux相关的问题,初次遇到时一头雾水,走了很多弯路,也耗费了很多时间精力。后来看了不少资料和博客,也研究了相关代码,对SELinux有了些认识。所以用本文来做个总结,加深理解。
本文将从下面五个方面来逐步认识和理解Android 下SELinux。
# for userspace object managers
class security
class process
class system
class capability
# file-related classes
class filesystem
class file
class dir
class fd
class lnk_file
class chr_file
class blk_file
class sock_file
common file
{
ioctl
read
write
create
getattr
setattr
lock
relabelfrom
relabelto
append
unlink
link
rename
execute
swapon
quotaon
mounton
}class dir
inherits file
{
add_name
remove_name
reparent
search
rmdir
open
audit_access
execmod
}
external/sepolicy/global_macros全局 宏变量定义文件,也就是用一个宏变量同时表示几个属性。如下例子
define(`x_file_perms', `{ getattr execute execute_no_trans }')
define(`r_file_perms', `{ getattr open read ioctl lock }')
define(`w_file_perms', `{ open append write }')
define(`rx_file_perms', `{ r_file_perms x_file_perms }')
define(`ra_file_perms', `{ r_file_perms append }')
define(`rw_file_perms', `{ r_file_perms w_file_perms }')
define(`rwx_file_perms', `{ rw_file_perms x_file_perms }')
define(`link_file_perms', `{ getattr link unlink rename }')
define(`create_file_perms', `{ create setattr rw_file_perms link_file_perms }')
external/sepolicy/te_macros 宏规则变量定义文件,也就是用一个变量来表示连续执行几条规则。如下例子
define(`domain_trans', `
# Old domain may exec the file and transition to the new domain.
allow $1 $2:file { getattr open read execute };
allow $1 $3:process transition;
# New domain is entered by executing the file.
allow $3 $2:file { entrypoint open read execute getattr };
# New domain can send SIGCHLD to its caller.
allow $3 $1:process sigchld;
# Enable AT_SECURE, i.e. libc secure mode.
dontaudit $1 $3:process noatsecure;
# XXX dontaudit candidate but requires further study.
allow $1 $3:process { siginh rlimitinh };
')
external/sepolicy/file_contexts 文件安全上下文定义文件。就是说对于一个文件或者目录,它的安全上下文应该在这里定义,如果没有定义,那么默认和父目录的安全上下文一样。
比如说我们在data目录下建了一个log目录存放系统运行时的log,那么我们可能希望这个log目录的的安全上下文是u:object_r:data_log_file:s0那么就应该在 file_contexts文件里做如下定义:/data/log(/.*)?u:object_r:data_log_file:s0
external/sepolicy/property_contexts 属性安全上下文定义文件。比如说新加了一个persist.log. * 开头的属性,那么就需要给它添加对应的安全上下文,如果不添加或者添加的上下文不对,那么在的某个进程里写入时就写不进去。
比如做了如下定义,那么是具有 system_prop域安全上下文,只能在system进程里修改值,在shell里是不能访问的。persist.log. u:object_r:system_prop:s0
如果希望在shell里能修改值,就应该这么写persist.log. u:object_r:shell_prop:s0 ,当然如果通过修改相关*.te 文件也能实现,但是不是很规范。
external/sepolicy/property.te 文件允许我们定义自己的prop 类型域,还有seapp_contexts、service_contexts 文件等,都是类似的,都可以根据需要修改。
下面几个是常用到的规则操作allow:赋予某项权限。
allowaudit:audit含义就是记录某项操作。默认情况下是SELinux只记录那些权限检查失败的操作。allowaudit则使得权限检查成功的操作也被记录。注意,allowaudit只是允许记录,它和赋予权限没关系。赋予权限必须且只能使用allow语句。
dontaudit:对那些权限检查失败的操作不做记录。
neverallow:前面讲过,用来检查安全策略文件中是否有违反该项规则的allow语句
规则命令格式:规则名 主体域 客体域:类型 权限属性 ; 或者 规则名 主体域 客体域:类型 {权限属性集合} ;比如:allow domain system_data_file:dir { search getattr };
allow domain system_file:file r_file_perms;
规则都定义在以te为后缀的文件里面,比如见到的init.te installd.te system_app.te platform_app.te shell.te 等等。init.te 是针对init进程定义的规则,system_app.te 针对的是app进程是system的应用定义的规则,shell.te 针对的是shell进程定义的规则,以此类推。如果我们对某种类型的进程定义了新的规则,那么我们应该把新规则添加到它对应的te文件里面。 比如我们在data目录下加了一个log目录,并且它的DAC权限为777,安全上下文是 u:object_r:data_log_file:s0, 我们希望系统运行过程通过shell命令查看log,那么我们就可以往shell.te 文件添加如下策略:allow shell data_log_file:dir r_dir_perms;
allow shell data_log_file:file r_file_perms;
如果我们新启了一个进程,并且希望它运行在自己定义的安全上下文里面,那么我们应该给它添加对应的te规则文件。
五、 曾经遇到过的问题和解决办法
1. 在init.rc 文件里启动一个log服务,一直属于restarting 状态,无法启动。系统输出如下warning log。
W/init ( 9795): type=1400 audit(0.0:284): avc: denied { execute_no_trans } for path="/system/bin/start_log.sh" dev="dm-0" ino=430 scontext=u:r:init:s0 tcontext=u:object_r:logsvc_exec:s0 tclass=file permissive=0
主体是:init进程,对应域是u:r:init:s0
客体是:system/bin/start_log.sh 对应域是u:object_r:logsvc_exec:s0
这个log的意思就是init进程在执行system/bin/start_log.sh 时没有 execute_no_trans权限。那我们就给它init加权限。
找到init.te 文件添加如下到末尾:
allow init logsvc_exec:file execute_no_trans;
2. 在开发一个自动压力测试程序,其中有一项是测试休眠唤醒相关的,会通过读取/d/suspend_stats 设备节点来判断是否有休眠失败情况。但是什么也读取不到这个值。打log发现如下warning。
09-07 12:22:52.480 W/AutoStressTools(32594): type=1400 audit(0.0:11): avc: denied { read } for name="suspend_stats" dev="debugfs" ino=10246 scontext=u:r:system_app:s0 tcontext=u:object_r:debugfs:s0 tclass=file permissive=0
主体是:AutoStressTools应用,属于system app进程,对应域是u:r:system_app:s0
客体是:/d/suspend_stats 对应域是u:object_r:debugfs:s0
这个log的意思就是system读取 /d/suspend_stats 时没有 read权限。那我们就给它system app加权限。
找到system_app.te 文件添加如下到末尾:
allow system_app debugfs:file r_file_perms;
3. CTS认证相关的,在init.rc 添加一个服务后,CTS 测试failed掉。
-- testInitDomain |
junit.framework.AssertionFailedError: Expected 1 process in SELinux domain "u:r:init:s0" Found "[pid: "1" proctitle: "/init" label: "u:r:init:s0" vsize: 4161536, pid: "211" proctitle: "/system/bin/memsicp" label: "u:r:init:s0" vsize: 3977216]" expected:<1> but was:<2> at junit.framework.Assert.fail(Assert.java:50) |
那么我们就另外给这个服务添加域就可以,并且添加对应的规则te文件。