SEAndroid是在Android系统中基于SELinux推出的强制访问控制模型,来完善自主访问模型中只要取得root权限就可以为所欲为的情况。
SELinux是一种基于域-类型(domain-type)模型的强制访问控制(MAC)安全系统,其原则是任何进程想在SELinux系统中干任何事,都必须先在安全策略的配置文件中赋予权限。凡是没有在安全策略中配置的权限,进程就没有该项操作的权限。在SELinux出现之前,Linux的安全模型是DAC(DiscretionaryAccess Control),译为自主访问控制。其核心思想是进程理论上所拥有的权限与运行它的用户权限相同。比如,以root用户启动shell,那么shell就有root用户的权限,在Linux系统上能干任何事。这种管理显然比较松散。在SELinux中,如果需要访问资源,系统会先进行DAC检查,不通过则访问失败,然后再进行MAC权限检查。
Linux中有两种东西,一种死的(Inactive),一种活的(Active)。死的东西就是文件(Linux哲学,万物皆文件。注意,万不可狭义解释为File),而活的东西就是进程。此处的“死”和“活”是一种比喻,映射到软件层面的意思是:进程能发起动作,例如它能打开文件并操作它。而文件只能被进程操作。
为了方便调试,我们可以手动开启或关闭selinux:
使用setenforce 命令进行设置:
adb shell setenforce 0 //设置成permissive 模式
adb shell setenforce 1 //设置成enforce 模式
注意此方法重启后失效
SELinux(或SEAndroid)将app划分为主要三种类型(根据user不同,也有其他的domain类型):
1.untrusted_app 第三方app,没有Android平台签名,没有system权限
2.platform_app 有android平台签名,没有system权限
3.system_app 有android平台签名和system权限
4.untrusted_app_25 第三方app,没有Android平台签名,没有system权限,其定义如下This file defines the rules for untrusted apps running with targetSdkVersion <= 25.
从上面划分,权限等级,理论上:untrusted_app < platform_app < system_app按照这个进行排序
system/sepolicy/seapp_contexts数据文件
isSystemServer=true domain=system_server
user=system seinfo=platform domain=system_app type=system_app_data_file
user=bluetooth seinfo=platform domain=bluetooth type=bluetooth_data_file
user=nfc seinfo=platform domain=nfc type=nfc_data_file
user=radio seinfo=platform domain=radio type=radio_data_file
user=shared_relro domain=shared_relro
user=shell seinfo=platform domain=shell type=shell_data_file
user=_isolated domain=isolated_app levelFrom=user
user=_app seinfo=platform domain=platform_app type=app_data_file levelFrom=user
user=_app domain=untrusted_app type=app_data_file levelFrom=user
从上面可以看出,domain和type由user和seinfo两个参数决定。
比如:
user=system seinfo=platform,domain才是system_app
user=_app,可以是untrusted_app或platform_app,如果seinfo=platform,则是platform_app。
首先看user,user可以理解为UID,例如ps -Z结果如下:
u:r:system_app:s0 system 2414 1172 com.android.keychain
u:r:platform_app:s0 u0_a6 2439 1172 com.android.managedprovisioning
u:r:untrusted_app:s0 u0_a8 2460 1172 com.android.onetimeinitializer
u:r:system_app:s0 system 2480 1172 com.android.tv.settings
u:r:untrusted_app:s0 u0_a27 2504 1172 com.android.email
u:r:untrusted_app:s0 u0_a28 2523 1172 com.android.exchange
u:r:untrusted_app:s0 u0_a7 2567 1172 com.android.musicfx
第一列是SContext,第二列是UID,只要UID是system的基本都是system_app,反过来一样。
其他的U0_XXX要么属于platform_app或untrusted_app
seinfo由system/sepolicy/private/mac_permissions.xml决定,内容如下:
<!-- Platform dev key in AOSP -->
<signer signature="@PLATFORM" >
<seinfo value="platform" />
</signer>
<!-- All other keys -->
<default>
<seinfo value="default" />
</default>
即如果签名是platform,seinfo就是platform,其他的比如shared等,seinfo是default。
比如上面ps -Z的结果里面,OneTimeInitializer.apk是untrusted_app,ManagedProvisioning.apk是platform_app。
分别查看这两个app的Android.mk
packages\apps\OneTimeInitializer\Android.mk 没有定义LOCAL_CERTIFICATE,默认是shared
packages\apps\ManagedProvisioning\Android.mk 有定义LOCAL_CERTIFICATE := platform
因为ManagedProvisioning.apk有platform签名,所以seinfo是platform。
所有资源可以分为两类: 主体和客体
主体是活的,可以主动对其他资源进行某些操作的,在linux中指进程。
客体则是死的,被动的。除了主体之外的资源(file,socket,device等)。
所有subject和object都有自己的标签,比如进程,目录,文件,设备,网终端口,主机名等资源都有自己的标签。
通过编写规则来控制某个subject标签对某个object标签的访问,这就是所谓管理政策。
如下面 u:object_r:system_file:s0 表示 用户,角色,类型,安全级别 , 这些字符串都是标签
-rwxr-xr-x root shell u:object_r:system_file:s0 dexdump
对象都有一个安全上下文(Security Context),它是一串字符串,通示标签构建。
标准格式:user:role:type:mls_level,可通过ps -Z命令查看
比如u:object_r:system_file:s0
用于角色访问控制,user不是linux uid,是MAC专用的定义,一个user可以属于多个role,不同的role具有不同的权限, android只定义一个用户u和一个subject角色r,一个object角色object_r,即不使用角色访问控制功能,这里不作详述。
主体(subject)和客体(object)都关联有一个安全级别,安全级别较高的主体可以读取安全级别较低的客体,而安全级别较低的主体可以写入安全级别较高的客体,通过这种规则,可以允许数据从安全级别较低的主体流向安全级别较高的主体,而限制数据从安全级别较高的主体流向安全级别较低的主体.Android只定义一种安全级别S0,即不使用安全级别。
type: 标注资源类型。
domain:和type一样,但是只限于标注subject,即进程的类型。对进程来说,Type就是Domain。
class:客体(object)的类别,本质上是具有相同操作权限集合的资源类型 , 比如file,它有open,read,write等操作,就定义一个file的class,包含所有相关操作权限。
策略格式:
rule subject_type target_type : class perm_set
rule:控制类型,比如allow neverallow.
subject_type:指domain
target_type:请求资源的类型
class perm_set: 对资源的操作
//init进程 创建/data/property and files
allow init property_data_file:dir create_dir_perms;
allow init property_data_file:file create_file_perms;
我们直接来看几个实例:
//SEAndroid中的安全策略文件policy.conf
#允许zygote域中的进程向init type的进程(Object Class为process)发送sigchld信号
allow zygote init:process sigchld;
Domain Transitions
一个进程fork 另外一个进程并执行(exec) 一个执行档时,为了避免新进程权限和源进程一样大,需要进行domain 切换
Object Transitions
process创建文件时, 默认是沿用父目录的Security Context, 如果要设置成特定的Label, 就必须进行Object切换。
讲解SEAndroid策略文件前,先了解一些基本语法,主要有下面三类
type
class
allow
type用于定义资源类型 即把资源类型和属性关联到一起
属性在后面有详细介绍
type type_id [alias alias_id,] [attribute_id]
将type_id(别名为alias)关联到attribute. 这样的话,方便用attribute来管理不同的type中包含相同的属性的部分。
例子:
type init, domain;
将init关联到domain,即将domain设置为init类型的属性
每一个资源对应一个type, 每一个type 对应有一个或几个Attribute.
用于定义资源类别的权限集合
class class_name [ inherits common_name ] { permission_name … }
inherits表示继承了common定义的权限,然后自己额外实现了permission_name的权限
例子:
class dir inherits file
{
add_name
remove_name
reparent
search
rmdir
open
audit_access
execmod
}
dir继承 file的操作权限集合并增加自己的操作
allow:赋予某项权限。
allowaudit:audit含义就是记录某项操作。默认情况下是SELinux只记录那些权限检查失败的操作
dontaudit:对那些权限检查失败的操作不做记录。
neverallow:用来检查安全策略文件中是否有违反该项规则的allow语句。
例子:
allow init unlabeled:filesystem mount;
允许init类型对unlabeled类型的filesystem进行mount的操作
neverallow { appdomain -unconfineddomain } kmem_device:chr_file { read write };
绝对不允许app(除了有unconfineddomain属性的app)对kmem_device类型的字符设备进行读写的操作
现在再来看type的定义,和type相关的命令主要有三个,如下面的例子所示:
type命令的完整格式为:type type_id [alias alias_id,] [attribute_id]
其中,方括号中的内容为可选。alias指定了type的别名,可以指定多个别名。
#下面这个例子定义了一个名为shell的type,它和一个名为domain的属性(attribute)关联
type shell, domain; #本例来自shell.te,注意,可以关联多个attribute
属性由attribute关键字定义,如attributes文件中定义的SEAndroid使用的属性有:
attribute domain
attribute file_type
可以在定义type的时候,直接将其和某个attribute关联,也可以单独通过
typeattribue将某个type和某个或多个attribute关联起来,如下面这个例子
将前面定义的system类型和mlstrustedsubject属性关联了起来
typeattribute system mlstrustedsubject
使用attribute有什么好处呢?一般而言,系统会定义数十或数百个Type,每个Type都需要通过allow语句来设置相应的权限,这样我们的安全策略文件编起来就会非常麻烦。有了attribute之后呢,我们可以将这些Type与某个attribute关联起来,然后用一个allow语句,直接将source_type设置为这个attribute就可以了:
#定义两个type,分别是A_t和B_t,它们都管理到attribute_test
type A_t attribute_test;
type B_t attribute_test;
#写一个allow语句,直接针对attribute_test
allow attribute_test C_t:file {read write};
#上面这个allow语句在编译后的安全策略文件中会被如下两条语句替代:
allow A_t C_t:file {read write};
allow B_t C_t:file {read write};
SEAndroid中,init进程的SContext为u:r:init:s0,而init创建的子进程显然不会也不可能拥有和init进程一样的SContext(否则根据TE,这些子进程也就在MAC层面上有了和init一样的权限)。那么这些子进程的SContext是怎么被打上和其父进程不一样的SContext呢?
ELinux中,上述问题被称为Domain Transtition,即某个进程的Domain切换到一个更合适的Domain中去。Domain Transition也是需要我们在安全策略文件中来配置的,而且有相关的关键词,来看例子。
#先要使用type_transition语句告诉SELinux
#type_transition的完整格式为:
#type_transition source_type target_type : class default_type;
#对Domain Transition而言有如下例子:
type_transition init_t apache_exec_t : process apache_t;
上面这个例子的解释如下,请读者务必仔细:
#首先,你得让init_t域中的进程能够执行type为apache_exec_t的文件
allow init_t apache_exec_t : file execute;
#然后,你还得告诉SELiux,允许init_t做DT切换以进入apache_t域
allow init_t apache_t : process transition;
#最后,你还得告诉SELinux,切换入口(对应为entrypoint权限)为执行apache_exec_t类型的文件
allow apache_t apache_exec_t : file entrypoint;
为什么会需要上述多达三个权限呢?这是因为在Kernel中,从fork到execv一共设置了三处Security检查点,所以需要三个权限。
提示:读者不必纠结这个了,按照规范做就完了。不过…,这导致我们写TE文件时候会比较麻烦啊!
确实比较麻烦,不过SELinux支持宏,这样我们可以定义一个宏语句把上述4个步骤全部包含进来。在SEAndroid中,系统定义的宏全在te_macros文件中,其中和DT相关的宏定义如下:
#定义domain_trans宏。$1,$2等等代表宏的第一个,第二个....参数
define(`domain_trans', `
# SEAndroid在上述三个最小权限上,还添加了自己的一些权限
allow $1 $2:file { getattr open read execute };
allow $1 $3:process transition;
allow $3 $2:file { entrypoint read execute };
allow $3 $1:process sigchld;
dontaudit $1 $3:process noatsecure;
allow $1 $3:process { siginh rlimitinh };
')
#定义domain_auto_trans宏,这个宏才是我们在te中直接使用的
#以例子7而言,该宏的用法是:
#domain_auto_trans(init_t, apache_exec_t, apache_t)
define(`domain_auto_trans', `
# 先allow相关权限
domain_trans($1,$2,$3)
# 然后设置type_transition
type_transition $1 $2:process $3;
')
除了DT外,还有针对Type的Transition。举个例子,假设目录A的SContext为u:r:dir_a,那么默认情况下在该目录下创建的文件都具有u:r:dir_a这个SContext。所以我们也要针对死得东西进行打标签。
# 定义file_type_auto_trans(domain, dir_type, file_type)宏
#该宏的含义是:当domain域中的进程在某个Type为dir_type的目录中创建文件时,该文件的SContext应该是file_type
在SEAndroid的app.te中,有如下TT设置:
./app.te:86:file_type_auto_trans(appdomain, download_file, download_file)