Android从4.4版本引入SEAndroid,SEAndroid是一套基于SELinux做的系统安全机制。SEAndroid有三种模式:Enforcing,Permissive,Disabled。Enforcing状态下,所有违反SEAndroid策略的操作都会被禁止执行并显示警告信息,Permissive状态下所有违反SEAndroid策略的操作不会被禁止执行,但会显示警告信息,Disabled则表示完全关闭状态。
今日遇到一些Permissive状态下开机显示SEAndroid警告的问题,现在写个文档记录一下。
下面是Log信息:
[ 6.606469] type=1400 audit(1167652801.729:3): avc: denied { entrypoint } for pid=1228 comm="init" path="/system/bin/tee-supplicant" dev="mmcblk0p9" ino=336 scontext=u:r:tee:s0 tcontext=u:object_r:system_file:s0 tclass=file permissive=1
从Log信息可以看到:源attribute(domain)为 tee( scontext项,根据此项我们首先要修改的文件为tee.te),目标attribute(file_type)为system_file(tcontext项),object_class为file(tclass项,在/external/sepolicy/security_classes中定义),目标进程文件为”/system/bin/tee-supplicant”(path项,该项可能没有,当策略冲突出现在进程对文件的访问时才会有,表示文件的位置),发生事件为denied(avc项,允许执行时为”granted”)。上面log的意思是:attribute为tee的进程对attribute为system_file的file的entrypoint操作是不被允许的。这里需要指出常用的两个atrribute,domain和file_type都是描述安全类型的attribute,只是进程的安全类型用domain描述,文件的安全类型用file_type描述,它们在/extrenal/sepolicy/attributes文件中均有定义:
# All types used for processes.
attribute domain;
...
# All types used for files that can exist on a labeled fs.
# Do not use for pseudo file types.
attribute file_type;
既然没有权限,就需要在SEAndroid的策略文件中授权。举例如下,表示允许attribute为netd对atrribute为proc的file进行write操作。值得注意的是,除了allow语句,还有neverallow语句,优先级比allow高,意思是在allow和neverallow同时设置了同一个操作的情况下,该操作仍是不被允许的。此外,没有设置allow的操作,就没有相应的权限,要想操作被允许,必须显式设置allow。
allow netd proc:file write
现在看看attribute为tee的进程信息,其会被写在tee.te中。可以看到新定义的tee和domain这个attribute通过type命令关联起来了,同理,新定义的tee_exec关联的是exec_type和file_type两个attribute。“关联”的意思即是两者可以被同等地对待,就像可以将tee当作domain了。
...
type tee, domain;
type tee_exec, exec_type, file_type;
type tee_device, dev_type;
type tee_data_file, file_type, data_file_type;
...
现在使用”grep -Hrn entrypoint ./”搜索当前路径下关于entrypoint操作的信息。可以看到,在domain.te中使用了如下语句:
neverallow domain { file_type -exec_type }:file entrypoint;
这句的意思是不允许属于domain这个attribute的进程去对属于file_type但不属于exec_type的file进行entrypoint操作。由Log的信息可以看到,报错的file_type为system_file,显然会被拒绝(可以用ps -Z看安进程安全上下文信息,ls -lZ看文件安全上下文信息)。可以留意到tee.te中,tee_exec关联到了exec_type,file_type。那么可以通过将对应文件的attribute改成tee_exec加上allow语句来通过策略。
./te_macros:14:allow $3 $2:file { entrypoint open read execute getattr };
./unconfined.te:72:}:{ chr_file file } ~{entrypoint execute_no_trans execmod execute relabelto};
./unconfined.te:73:allow unconfineddomain {dev_type -kmem_device}:{ chr_file file } ~{entrypoint execute_no_trans execmod execute relabelto};
./unconfined.te:83:}:{ chr_file file } ~{entrypoint execute_no_trans execmod execute relabelto};
./app.te:285:# Write to entrypoint executables.
./kernel.te:67:neverallow kernel { file_type fs_type -rootfs }:file { entrypoint execute_no_trans };
./init.te:119:neverallow init { file_type fs_type }:file entrypoint;
./domain.te:236:# Ensure that all entrypoint executables are in exec_type.
./domain.te:237:neverallow domain { file_type -exec_type }:file entrypoint;
./access_vectors:164: entrypoint
./access_vectors:182: entrypoint
./access_vectors:778: entrypoint
已知进程对应的文件为”/system/bin/tee-supplicant”,可以在file_contexts文件中找到这些系统进程对应文件及设备文件的domain。现在发现file_contexts没有”/system/bin/tee-supplicant”这一项。在原生的file_contexts中可以发现这样的一项:
/system(/.*)? u:object_r:system_file:s0
在没有显式声明的情况下,system目录下所有文件的attribute均为system_file,这也是我们看到的/system/bin/tee-supplicant属于system_file的原因。回到定制的file_contexts可以增加这一条:
/system/bin/tee-supplicant u:object_r:tee_exec:s0
现在tee-supplicant文件的file_type改成了tee_exec,其既关联exec_type又关联file_type,自然就不会被策略所拒绝。
如果我想定义一个全新的attribute呢?例如定义成sup_exec这个在策略文件中从没出现过的名字。由于策略只允许exec_type通过,现在需要将sup_exec与exec_type关联起来。打开tee.te文件,加上:
type sup_exec,exec_type,file_type;
便可以通过策略。
再来看一个例子。
[ 10.587032] type=1400 audit(1167652805.709:5): avc: denied { transfer } for pid=1245 comm="MiSetSystemINFO" scontext=u:r:misys:s0 tcontext=u:r:init:s0 tclass=binder permissive=1
可以看到attribute为misys的进程对attribute为init的binder的transfer操作被拒绝了。 grep -Hrn transfer ./看一下:
./unconfined.te:90:allow unconfineddomain { domain -init }:binder { call transfer set_context_mgr };
这里允许unconfineddomain这个attribute对所有除了init外关联domain的attribute的binder进行call,transfer, set_context_mgr操作。没有allow就是拒绝,这也对应了得到的Log。
可以猜想到misys这个domain被关联到unconfineddomain这个attribute了。打开misys.te看一下:
...
type misys, domain;
type misys_exec, exec_type, file_type;
unconfined_domain(misys)
typeattribute misys mlstrustedsubject;
init_daemon_domain(misys)
net_domain(misys)
...
在te_macros文件中。可以看到unconfined_domain函数的定义:
define(`unconfined_domain', `
typeattribute $1 mlstrustedsubject;
typeattribute $1 unconfineddomain;
')
typeattribute用法和type差不多,都是关联一个domain到attributes中,”typeattribute $1 unconfineddomain”表示将第一个参数关联到unconfineddomain中。那么misys这个domain确实关联到unconfineddomain中了,所以会被策略拒绝。
这个问题的根本原因是在没有显式声明的情况下,子进程会继承父进程的安全上下文。init进程是Android所有进程的父进程,fork出来的misys进程的安全上下文与init进程相同,都是init。这是由于开发者的疏忽导致。一种改法是在misys.te加入对应allow语句授予权限,另一种改法是使用ps -Z找到该进程对应的文件,在file_contexts改变这个文件的domain,只要不是init即可。
可以通过chcon命令来临时改变文件的安全上下文。用法:
chcon [-R] [-t type] [-u user] [-r role] 文件。
-t,-u.-r对应安全上下文的组成部分,-R表示递归地改变安全上下文。