SEAndroid是Google在Android4.4上正式推出的一套以SELinux为核心的系统安全机制。而SELinux则是由NSA(美国国安局)在Linux社区的帮助下设计的一个针对Linux的安全强化系统。
NSA最初设计的安全模型叫FLASK,全称为FluxAdvanced Security Kernel(由Uta大学和美国国防部开发,后来由NSA将其开源),当时这套模型针对DTOS系统。后来NSA觉得Linux更具发展和普及前景,所以就在Linux系统上重新实现了FLASK,并称之为SELinux。
SELinux是一种基于域-类型(domain-type)模型的强制访问控制(MAC)安全系统,在LinuxKernel中,SELinux通过LSM(LinuxSecurity Modules)实现。在2.6以前的版本,SELinux通过patch方式发布。从2.6开始,SELinux正式进入内核,成为标配。
由于Linux有多种发行版本,所以各家的SELinux表现形式也略有区别,具体到Android平台,Google对其进行了一定的修改,从而得到SEAndroid。
SEAndroid在架构和机制上与SELinux完全一样,考虑到移动设备的特点,所以移植到Android上的只是SELinux的一个子集。SEAndroid的安全检查几乎覆盖了所有重要的系统资源,包括域转换,类型转换,进程、内核、文件、目录、设备,App,网络及IPC相关的操作。
接下来,我们就来看一下SEAndroid安全机制的整体框架,如下所示:图1.SEAndroid安全机制框架
从上面的架构图可以看出来,SEAndroid安全机制包含用户空间和内核空间两部分,内核空间主要涉及LSM内核安全模块,用户空间包括SecurityContext,Security Policy等模块。这些模块的作用和交互如下:
1)LSM提供了一种通用的安全框架,允许将安全模型以模块方式载入内核,除了selinux外,linux还支持tomoyo,yama等安全模型;
2)AVC是一个策略缓存,当进程试图访问系统资源的时候,kernel中的安全策略服务将会先在AVC中查找策略,如果没有命中,则会到安全服务器中查找,找到了,则权限被缓存,允许访问,如果没找到,则拒绝访问;
3)SecurityPolicy描述系统资源的安全访问策略,系统启动时init进程负责把策略文件加载到内核的LSM模块中;
4)SecurityContext描述系统资源的安全上下文,SELinux的安全访问策略就是在安全上下文的基础上实现的;
5)libselinux为用户空间提供了SELinux文件系统访问接口;
SELinux出现之前,Linux的安全模型叫DAC,全称是DiscretionaryAccess Control,翻译为自主访问控制。DAC的核心思想很简单,就是:
进程理论上所拥有的权限与运行它的用户权限相同。比如,以root用户启动shell,那么shell就有root用户的权限,在Linux系统上能干任何事。
显然DAC的管理太宽松了,所以各路高手都想方设法要在Android上搞到root权限。事实上su就是通过修改uid(0)和gid(0)的方式来取得root权限的。那么SELinux又是如何解决这个问题的呢?原来,它在DAC之外,设计了一个新的安全模型,叫做MAC(MandatoryAccess Control),翻译为强制访问控制。MAC的处世哲学非常简单,即:
任何进程想在SELinux系统中干任何事,都必须先在安全策略的配置文件中赋予权限。凡是没有在安全策略中配置的权限,进程就没有该项操作的权限。来看一个SEAndroid中设置权限的例子:
[external/sepolicy/netd.te]
allow netd sysfs:file write;
如果没有在netd.te中使用上例中的权限配置语句,那么netd就无法往sys目录下的任何文件中写入数据,即使netd具有root权限。 上面这条表示允许(Allow)netd域(Domain)中的进程写(Write)sysfs类型(Type)的文件;
显然,MAC比DAC在权限管理方面要复杂,严格和细致得多。
那么,关于DAC和MAC,两者是什么关系呢?
1)Linux系统先做DAC检查。如果没有通过DAC权限检查,则操作直接失败。通过DAC检查之后,再做MAC权限检查;
2)SELinux中也有用户的概念,但它和Linux中原有的user概念不是同一个东西。什么意思呢?比如,Linux中的超级用户root在SELinux中可能就是一个没权限,没地位,打打酱油的“路人甲”。当然,这一切都是由SELinux安全策略来决定的。
通过上面内容的学习,各位应该能够感觉到,在SELinux中,安全策略文件是最重要的,确实如此,学习SELinux的终极目标应该是:
1)看懂现有的安全策略文件;
2)编写符合项目需求的安全策略文件;
SELinux有自己的一套规则来编写安全策略文件,这套规则被称为SELinux安全策略语言,它是掌握SELinux的重点;
Linux中有两种东西,一种死的(Inactive),一种活的(Active)。死的东西就是指文件(Linux哲学,万物皆文件),而活的东西就是进程。此处的“死”和“活”是一种比喻,映射到软件层面的意思就是:进程能发起动作,例如它能打开并操作文件,而文件只能被进程操作。
SELinux中,每种东西都会被赋予一个安全属性,它就是SecurityContext,Security Context(以下简称SContext,安全上下文或安全属性)是一个字符串,主要由三部分组成。例如在SEAndroid中,进程的SContext可以通过PS-Z命令查看,如下:
u:r:init:s0 root 464 1 /system/bin/sh
u:r:healthd:s0 root 240 1 /sbin/healthd
u:r:lmkd:s0 root 241 1 /system/bin/lmkd
u:r:servicemanager:s0 system 242 1 /system/bin/servicemanager
u:r:vold:s0 root 243 1 /system/bin/vold
u:r:surfaceflinger:s0 system 244 1 /system/bin/surfaceflinger
1)u为user的意思:SEAndroid中定义了一个SELinux用户,值为u; 最左边那一列就是进程的SContext,以第一个进程/system/bin/sh的SContex为例,其值为u:r:init:s0:
2)r为role的意思:role是角色之意,它是SELinux中一种比较高层次,更方便的权限管理思路,即RoleBased Access Control(基于角色的访问控制,简称RBAC)。简单点说,一个user可以属于多个role,不同的role具有不同的权限。
3)init代表进程在init域(Doamin)中。MAC的基础管理思路其实不是上面的RBAC,而是所谓的TypeEnforcement Access Control(简称TEAC,一般用TE表示)。对进程来说Type就是Domain,比如init这个Domain有什么权限,都需要在策略文件(init.te)中定义。
4)s0和SELinux为了满足军用和教育行业而设计的Multi-LevelSecurity(MLS)机制有关。简单点说,MLS将系统的进程和文件进行了分级,不同级别的资源需要对应级别的进程才能访问。
再来看看文件的SContext,这里我们用ls -Z来查看,如下:
drwx------ root root u:object_r:rootfs:s0 root
-rw-r--r-- root root u:object_r:rootfs:s0 service_contexts
drwxr-x--x root sdcard_r u:object_r:rootfs:s0 storage
dr-xr-xr-x root root u:object_r:sysfs:s0 sys
drwxr-xr-x root root u:object_r:system_file:s0 system
倒数第二列就是文件和目录的SContext信息,以第一行root目录为例,其值为u:object_r:rootfs:s0: 再来看看文件的SContext,这里我们用ls-Z来查看,如下:
1)u还是user的意思;
2)object_r:文件是死的东西,它没办法扮演任何角色,所以在SELinux中,所有死的东西都用object_r来表示它的role;
3)rootfs代表文件的类型(Type),和进程的Domain其实是一个意思。它表示root目录对应的Type是rootfs;
4)s0:MLS的级别;
根据SELinux规范,完整的SContext字符串为:
user:role:type[:range]
注意:方括号中的内容表示可选项。S0属于range中的一部分。
SContext的核心其实是前三个部分:user:role:type
前面说了,MAC的管理核心是TEAC,然后是高一级的RBAC。RBAC是基于TE的,而TE也是SELinux中最主要的部分,下面我们就来看看TE。
我们再来看看前面提到的权限设置语句:
allow netd sysfs:file write;
1)allow:表示授权。除了allow之外,还有allowaudit、dontaudit、neverallow等; 这条语句的语法为:
2)netd:sourcetype,也叫Domain,Subject。
3)sysfs:targettype,它代表其后的file所对应的Type。
4)file:objectclass,它代表能够给Domain操作的对象。例如file、dir、socket等,Android中SecurityClass的定义在security_classes中。在Android系统中,有一些特殊的Class,如property_service,binder等。
5)write:在该类objectclass中所定义的操作,例如file类支持ioctl,read,write等操作。access_vectors中定义了所有objectclass支持的操作。
根据SELinux规范,完整的allow语句格式为:
rule_name source_type target_type:class perm_set;
1)如果有多个source_type,target_type,class或perm_set,可以用”{}”括起来; 注意:使用allow语句的时候,可以使用下面的一些小技巧来简化命令书写;
2)”~”号,表示除了”~”以外;
3)”-”号,表示去除某项内容;
4)”*”号,表示所有内容
下面就让我们先看几个例子吧:
Example 1:
allow appdomain zygote_tmpfs:file read;
允许appdomain域的进程对zygote_tmpfs类型的文件(file)执行read操作;
rule_name:allow;
soruce_type:appdomain;
target_type:zygote_temfs;
object_class:file;
access_vector:read;
Example 2:
allow zygote appdomain:process { getpgid setpgid };
允许zygote域的进程对appdomain类型的进程执行getpgid和setpgid操作;
rule_name:allow;
soruce_type:zygote;
target_type:appdomain;
objectclass:process;
accessvector:getpgid setpgid;
“{}”中的内容表示一组type或perm_set,使用”{}”可以简化allow语句的书写,如果不用”{}”,上例就需要两条allow语句来分别设置setpgid和getpgid操作,类似的例子还有很多:
Example 3:
# 允许bluetooth域的进程对tun_device, uhid_device, hci_attach_dev类型的
# 字符文件(chr_file)执行读写操作;
allow bluetooth { tun_device uhid_device hci_attach_dev }:chr_file { read write }
# 允许appdomain域的进程对app_data_file类型的文件(file)和链接(lnk_file)
# 执行create操作;
allow appdomain app_data_file:{ file lnk_file } { create }
Example 4:
allow init {
shell_data_file
app_data_file
}:{ chr_file file } ~{entrypoint execute_no_trans execmod execute relabelto};
Example4中使用了”~”符号,表示允许init域的进程对shell_data_file,app_data_file类型的字符文件(chr_file),普通文件(file)执行除了entrypointexecute_no_trans execmod execute relabelto以外的操作;
Example 5:
allow init { file_type -system_file }:dir relabelto;
”-”符号表示去除某项,即允许init域的进程对file_type类型中除了system_file类型外的目录执行relabelto操作;
file_type其实是一个类型的集合,所有文件相关的类型都可以包含在这个集合中,如system_file,system_data_file,apk_data_file等,SELinux之所以有类型集合的概念也是为了简化安全策略语言的书写,如上例所示,所有对file_type类型设置的权限策略,都适用于包含在file_type集合中的所有类型,也就是说init域的进程可以对包含在file_type集合中的所有类型(system_data_file,apk_data_file等)的目录执行relabelto操作,system_file类型除外。 ”-”符号表示去除某项,即允许init域的进程对file_type类型中除了system_file类型外的目录执行relabelto操作;
Example 6:
allow system_server self:netlink_selinux_socket *;
”*”符号表示所有内容,即system_server域的进程能够对system_server类型执行所有netlink_selinux_socket类相关的操作;
self表示targettype与source type相同,这时就不用再重复写一遍sourcetype了,用self代替就可以了;
好,下面来个稍微复杂点的:
Example 7:
neverallow {
domain -keystore
} keystore_data_file:dir ~{ open create read getattr setattr search relabelto };
简单来说就是不允许除keystore域外的其它域对keystore_data_file类型的目录执行open,create等操作;特别注意,前面提到权限必须显示声明,没有声明的话默认就没有权限。那么neverallow语句就没有存在的必要了,因为“无权限”是不需要声明的。确实如此,neverallow语句的作用只是在生成安全策略文件时进行检查,判断是否有违反neverallow语句的策略存在。
下面我们为init_shell添加对keystore_data_file类型目录访问的权限,显然这是违反Example7中neverallow规则的,那么,在生成安全策略文件时就检测到了冲突,如下:
out/host/linux-x86/bin/checkpolicy: loading policy configuration from out/target/product/xxx/obj/ETC/sepolicy_intermediates/policy.conf libsepol.check_assertion_helper: neverallow on line 24 of external/sepolicy/keystore.te (or line 7122 of policy.conf) violated by allow init_shell keystore_data_file:dir { open };
Error while expanding policy
make: *** [out/target/product/xxx/obj/ETC/sepolicy.recovery_intermediates/sepolicy.recovery] Error 1
在编译policy.conf时,检测到有违反keystore.te第24行neverallow规则的allow语句存在;从编译日志也能看出来,安全策略文件保存在obj/ETC/sepolicy_intermediates目录下;
看了上面几个例子,是不是觉得安全策略语言很简单啊,下面让我们再深入一点,看看allow语句各个字段的含义吧。
1.Rule Name
rule_name一共有四种,定义如下:
1)allow:赋予某项权限;
2)allowaudit:audit含义就是记录某项操作,默认情况下SELinux只记录那些权限检查失败的操作。allowaudit则使得权限检查成功的操作也被记录。注意:allowaudit只是允许记录,它和赋予权限没有关系。赋予权限必须且只能使用allow语句。
3)dontaudit:对那些权限检查失败的操作不做记录;
4)neverallow:检查安全策略文件中是否有违反该项操作的allow语句;
2.Type
SELinux是基于Domain-Type的强制类型访问模型,Domain其实也是Type,它只是对进程类型的习惯称呼,和Type相关的命令主要由三个:
1)type:类型声明,type命令的完整格式为:
type type_id [attribute_id][attribute_id] …;
其中方括号中的内容为可选项,attribute_id表示与type_id相关联的属性;
下面的例子定义了一个名为shell的type,它和一个名为domain的属性(attribute)关联:
type shell, domain;
type sysfs, fs_type, sysfs_type;
注意:一个type可以关联多个attribute;
2)attribute:属性由attribute关键字声明,属性其实是一个特殊的type,可以把属性看成是type的集合,为属性设置的策略,适用于所有与该属性相关联的type,如下:
attribute fs_type; # 声明fs_type属性
attribute sysfs_type; # 声明sysfs_type属性
# 允许init进程对sysfs_type类型集合的所有目录,文件执行relabelto操作
allow init sysfs_type:{ dir file lnk_file } relabelto;
注意:attributes文件中定义了SEAndroid中使用的所有属性;
3)typeattribute:可以在定义type的时候,直接将其和某个attribute关联起来,也可以单独通过typeattribute命令将某个type和某个或多个attribute关联起来,如下:
Typeattribute system mlstrustedsubject;
特别注意:对于初学者而言,attribute和type的关系最难理解,因为”attribute”这个关键字实在没取好名字,很容易产生误解:
a)实际上,type和attribute位于同一个命名空间,即不能用type命令和attribute命令定义相同名字的东西;
b)其实,attribute真正的意思应该是类似typegroup这样的概念。比如将type A和attributeB关联起来,就是说type A属于attributeB中的一员;
使用attribute有什么好处呢?一般而言,系统会定义数十或数百个type,每个type都需要通过allow语句来设置相应的权限,这样我们的安全策略文件编写起来就会非常麻烦。有了attribute之后呢,我们可以将这些type与某个attribute关联起来,把这些type共有的权限,通过一条allow语句来设置:
# 定义两个type,分别是t_a和t_b,它们都需要设置对t_c类型文件的读操作;
# 首先,把t_a和t_b关联到attr_test
type t_a, attr_test;
type t_b, attr_test;
# 通过一条allow语句为attr_test设置对t_c类型文件的读权限
allow attr_test t_c:file read;
好了,现在t_a和t_b域的进程拥有了对t_c类型文件的读权限
3.Object Class
ObjectClass声明了进程要操作的对象类,security_classes文件定义了SEAndroid中用到的所有class;
class关键字用于声明objectclass类型:
# file-related classes
class file # 文件
class dir # 目录
class fd # 文件描述符
class lnk_file # 链接文件
class chr_file # 字符设备文件
class blk_file # 块设备文件
… …
# network-related classes
class socket
class tcp_socket
class udp_socket
… …
class binder # Android平台特有的binder
class zygote # Android平台特有的zygote
… …
class property_service # Android平台特有的属性服务
4.Perm Set
perm_set指的是某种ObjectClass所拥有的操作,以file类而言,就包括open,read, write等操作;和Object Class一样,SEAndroid所支持的permset的声明在access_vectors文件中;
SELinux规范中,定义permset有两种方式:
1)common:其命令的格式为:
commoncommon_name { permission_name … }
以下是file类对应的权限(permset),大部分各位都能猜出来是干什么的:
common file {
ioctl read write create getattr setattr lock relabelfrom relabelto
append unlink link rename execute swapon quotaon mounton }
2)class:除了common外,还有一种class命令也可以设置permset,class命令可以继承common定义的permset;
class命令的完整格式为:
class class_name [inherits common_name] {permission_name … }
inherits表示继承某个common定义的权限,如下:
class dir inherits file {
add_name remove_name reparent search rmdir open audit_access
execmod }
绝大多数情况下,SELinux的安全策略需要我们编写各种各样的te文件。由前文可知,.te文件内部应该包含了各种allow, type等语句。这些都是TEAC,属于SELinux MAC中的核心组成部分
在TEAC之上,SELinux还有一种基于角色的安全策略,也就是RBAC。RBAC到底是如何实施相关权限控制的呢?我们先来看看SEAndroid中Role和User的定义。
Android中只定义了一个role,就是r,把r和domain类型关联起来:
role r;
role r types domain;
再来看user的定义:
user u roles { r } level s0 range s0 - mls_systemhigh;
声明useru,并且和role r关联起来,level表示u的安全级别,s0为最低级,也就是默认的级别。mls_systemhigh表示u所能获得的最高安全级别;
那么,Role和User之间有什么样的权限控制呢?
在RBAC中,权限与角色相关联,用户通过成为适当角色的成员而得到这些角色的权限。这就极大地简化了权限的管理。在一个组织中,角色是为了完成各种工作而创造,用户则依据它的责任和资格来被指派相应的角色,用户可以很容易地从一个角色被指派到另一个角色。角色可依新的需求和系统的合并而赋予新的权限,而权限也可根据需要而从某角色中回收。
前面陆陆续续的讲了些SELinux中最常见的东西,不过细心的人可能会问这样一个问题:这些安全属性(SContext)最开始是怎么赋给这些死的和活的东西呢?
在SELinux中,设置或分配SContext给进程或文件的工作叫做SecurityLabeling,土语叫打标签。
Android系统启动后,init进程会调用selinux_android_load_policy接口,将一个编译完的安全策略文件传递给内核,内核可根据其中的相关信息来初始化SELinux相关模块;接着init进程会调用selinux_android_restorecon接口,根据context文件中的信息为系统打标签;
context文件中显示声明了系统中各种资源的安全属性,下面就来看看SEAndroid中用到的几种context文件:
1.为文件或文件系统打标签
1)file_contexts:为系统中的各种文件,目录设置初始的SContext,如下:
/dev(/.*)? u:object_r:device:s0
/dev/akm8973.* u:object_r:sensors_device:s0
/dev/accelerometer u:object_r:sensors_device:s0
/dev/adf[0-9]* u:object_r:graphics_device:s0
/dev/alarm u:object_r:alarm_device:s0
/dev/android_adb.* u:object_r:adb_device:s0
/dev/ashmem u:object_r:ashmem_device:s0
/dev/audio.* u:object_r:audio_device:s0
… …
/system(/.*)? u:object_r:system_file:s0
/system/bin/sh -- u:object_r:shell_exec:s0
/system/bin/run-as -- u:object_r:runas_exec:s0
/system/bin/bootanimation u:object_r:bootanim_exec:s0
2)fs_use:描述SELinux的Labeling信息在不同文件系统时的处理方式。对于常规文件系统,SContext信息保存在文件节点(inode)的属性中,系统可以通过getattr函数从inode中读取到文件的安全属性。
fs_use_xattr关键字表示SContext是永远性的保存在文件系统中:
# Label inodes via getxattr.
fs_use_xattr yaffs2 u:object_r:labeledfs:s0;
fs_use_xattr jffs2 u:object_r:labeledfs:s0;
fs_use_xattr ext2 u:object_r:labeledfs:s0;
fs_use_xattr ext3 u:object_r:labeledfs:s0;
fs_use_xattr ext4 u:object_r:labeledfs:s0;
3)genfs_contexts:文件系统还可以使用genfscon关键字来打标签,如下:
# Label inodes with the fs label.
genfscon rootfs / u:object_r:rootfs:s0
… …
genfscon proc / u:object_r:proc:s0
genfscon proc /net u:object_r:proc_net:s0
… …
genfscon sysfs / u:object_r:sysfs:s0
genfscon vfat / u:object_r:vfat:s0
genfscon debugfs / u:object_r:debugfs:s0
genfscon fuse / u:object_r:fuse:s0
2.为property打标签
SEAndroid还增加为property打标签的功能,如下property_contexts文件:
ril. u:object_r:radio_prop:s0
gsm. u:object_r:radio_prop:s0
… …
sys. u:object_r:system_prop:s0
sys.powerctl u:object_r:powerctl_prop:s0
… …
* u:object_r:default_prop:s0
3.为service打标签
SEAndroid还支持为service打标签,使用”getprop| grep init.svc”命令,可以查看系统中所有服务的运行状态,系统服务的安全上下文,详见service_context;
4.为app打标签
为app打标签,mac_permissions.xml根据apk签名设置app的seinfo,AndroidL只识别platform签名,其它都是default:
接下来seapp_contexts根据seinfo和user来为app打标签;
user=system domain=system_app type=system_app_data_file
user=bluetooth domain=bluetooth type=bluetooth_data_file
… …
# 三方的apk,如果有platform签名,则运行在platform_app域
user=_app seinfo=platform domain=platform_app type=app_data_file
# 三方的apk,如果没有platform签名,则运行在untrusted_app域
user=_app domain=untrusted_app type=app_data_file
SEAndroid中,init进程的SContext为u:r:init:s0(在init.rc中使用” setcon u:r:init:s0”命令设置),而init创建的子进程显然不会也不可能拥有和init进程一样的SContext(否则根据TE,这些子进程也就在MAC层面上有了和init进程一样的权限)。那么这些子进程是怎么被打上和父进程不一样的SContex呢?
在SELinux中,上述问题被称为DomainTransition,即某个进程的Domain切换到一个更合适的Domain中去。DomainTransition也是在安全策略文件中配置的,而且有相关的关键字。
这个关键字就是type_transition,表示类型转换,其完整格式如下:
type_transition source_type target_type : class default_type
表示source_type域的进程在对target_type类型的文件进行class定义的操作时,进程会切换到default_type域中,下面我们看个域转换的例子:
type_transition init shell_exec:process init_shell
这个例子表示:当init域的进程执行(process)shell_exec类型的可执行文件时,进程会从init域切换到init_shell域。那么,哪个文件是shell_exec类型呢?从file_contexts文件能看到,/system/bin/sh的安全属性是u:object_r:shell_exec:s0,也就是说init域的进程如果运行shell脚本的话,进程所在的域就会切换到init_shell域,这就是DomainTransition(简称DT)。
请注意,DT也是SELinux安全策略的一部分,type_transition不过只是开了一个头而已,要真正实施成功这个DT,还至少需要下面三条权限设置语句:
# 首先,你得让init域的进程要能够执行shell_exec类型的文件
allow init shell_exec:file execute;
# 然后,你需要告诉SELinux,允许init域的进程切换到init_shell域
allow init init_shell:process transition;
# 最后,你还得告诉SELinux,域切换的入口(entrypoint)是执行shell_exec类型的文件
allow init_shell shell_exec:file entrypoint;
看起来挺麻烦,不过SELinux支持宏定义,我们可以定义一个宏,把上面4个步骤全部包含进来。在SEAndroid中,所有的宏都定义在te_macros文件中,其中和DT相关的宏定义如下:
# 定义domain_trans宏,$1,$2,$3代表宏的第一个,第二个…参数
define(`domain_trans', `
allow $1 $2:file { getattr open read execute };
allow $1 $3:process transition;
allow $3 $2:file { entrypoint open read execute getattr };
… …
')
# 定义domain_auto_trans宏,这个宏才是我们在te中直接使用的
define(`domain_auto_trans', `
# Allow the necessary permissions.
domain_trans($1,$2,$3)
# Make the transition occur by default.
type_transition $1 $2:process $3;
')
呵呵,是不是简单多了,domain_trnas宏在DT需要的最小权限的基础上还增加了一些权限,在te文件中可以直接用domain_auto_trans宏来显示声明域转换,如下:
domain_auto_trans(init, shell_exec, init_shell)
除了DT外,还有针对Type的Transition(简称TT)。举个例子,假设目录A的SContext为u:r:dir_a,那么默认情况下,在该目录下创建的文件的SContext就是u:r:dir_a,如果想让它的SContext发生变化,那么就需要TypeTransition。
和DT类似,TT的关键字也是type_transition,而且要顺利完成Transition,也需要申请相关权限,废话不多说了,直接看te_macros是怎么定义TT所需的宏:
# 定义file_type_trans宏,为Type Transition申请相关权限
define(`file_type_trans', `
allow $1 $2:dir ra_dir_perms;
allow $1 $3:notdevfile_class_set create_file_perms;
allow $1 $3:dir create_dir_perms;
')
# 定义file_type_auto_trans(domain, dir_type, file_type)宏
# 该宏的意思就是当domain域的进程在dir_type类型的目录创建文件时,该文件的SContext
# 应该是file_type类型
define(`file_type_auto_trans', `
# Allow the necessary permissions.
file_type_trans($1, $2, $3)
# Make the transition occur by default.
type_transition $1 $2:dir $3;
type_transition $1 $2:notdevfile_class_set $3;
')
SELinux支持Disabled,Permissive,Enforce三种模式;
Disabled就不用说了,此时SELinux的权限检查机制处于关闭状态;
Permissive模式就是SELinux有效,但是即使你违反了它的安全策略,它让你继续运行,但是会把你违反的内容记录下来。在策略开发的时候非常有用,相当于Debug模式;
Enforce模式就是你违反了安全策略的话,就无法继续操作下去。
在Eng版本使用setenforce命令,可以在Permissive模式和Enforce模式之间切换。
SELinux是经过安全强化的Linux操作系统,一些原有的命令都进行了扩展,另外还增加了一些新的命令,下面让我们看看经常用到的几个命令:
1)ls -Z命令
查看文件,目录的安全属性:
root@xxx:/ # ls –Z
drwxr-x--x root sdcard_r u:object_r:rootfs:s0 storage
dr-xr-xr-x root root u:object_r:sysfs:s0 sys
drwxr-xr-x root root u:object_r:system_file:s0 system
… …
2)ps -Z命令
查看进程的安全属性:
root@xxx:/ # ls -Z
u:r:rild:s0 radio 272 1 /system/bin/rild
u:r:drmserver:s0 drm 273 1 /system/bin/drmserver
u:r:mediaserver:s0 media 274 1 /system/bin/mediaserver
u:r:installd:s0 install 283 1 /system/bin/installd
3)chcon命令
更改文件的安全属性
root@xxx:/ # ls -Z /dev/tfa9890 crw-rw---- media media u:object_r:audio_device:s0 tfa9890 root@xxx:/ # chcon u:object_r:device:s0 /dev/tfa9890 root@xxx:/ # ls -Z /dev/tfa9890 crw-rw---- media media u:object_r: device:s0 tfa9890 |
4)restorecon命令
当文件的安全属性在安全策略配置文件里面有定义时,使用restorecon命令,可以恢复原来的安全属性
root@xxx:/ # restorecon /dev/tfa9890 SELinux: Loaded file_contexts from /file_contexts root@xxx:/ # ls -Z /dev/tfa9890 crw-rw---- media media u:object_r:audio_device:s0 tfa9890 |
5)id命令
使用id命令,能确认自己的SecurityContext
root@xxx:/ # id uid=0(root) gid=0(root) groups=1004(input),1007(log),1011(adb), 1015(sdcard_rw),1028(sdcard_r),3001(net_bt_admin),3002(net_bt), 3003(inet),3006(net_bw_stats) context=u:r:su:s0 |
6)getenforce命令
得到当前SELinux的模式值
root@xxx:/ # getenforce Enforcing |
7)setenforce命令
更改当前的SELINUX的模式值,后面可以跟 enforcing,permissive 或者1,0。
通过上面的讲解,各位应该对SELinux安全策略有了一定的了解,接下来我们看看在具体项目开发中,我们应该如何应对安全策略相关的问题。
SELinux的安全检查覆盖了所有重要的系统资源,每次MAC访问失败都会记录在内核中,如下:
<6>[82.950769] type=1400 audit(1882976.149:5): avc: denied { write } for pid=3194 comm="BluetoothAdapte" name="aplog" dev="mmcblk0p22" ino=88 scontext=u:r:bluetooth:s0 tcontext=u:object_r:system_data_file:s0 tclass=dir |
上面的log记录了一条违反安全策略的访问信息,即BluetoothAdapte进程试图在data分区写目录失败。
scontext表示进程的SContext,u:r:bluetooth:s0,属于bluetooth域;
tcontext表示目标的SContext,u:r:system_data_file:s0,属于system_data_file类型;
tclass表示进程要操作的ObjectClass,dir表示目录;
mmcblk0p22是userdata分区,write表示写操作。
连起来就是bluetooth域的进程(BluetoothAdapte),对system_data_file类型的dir执行write操作失败。明确了失败原因,我们就可以在安全策略配置文件中定制我们自己的策略了:
[external/sepolicy/bluetooth.te] allow bluetooth system_data_file:dir w_dir_perms; |
w_dir_perms是一个宏,其定义在global_macros中,包含了write相关操作:
[external/sepolicy/global_macros] define(`w_dir_perms', `{ open search write add_name remove_name }') |
在项目开发中,我们在/dev目录下建立了一个新的设备文件tfa98xx,这是一个音频相关的设备文件,但是在集成framework层的代码后,总是出现下面的访问错误,应该如何处理呢?
<6>[12.435524] type=1400 audit(3635791.670:21): avc: denied { read write } for pid=273 comm="mediaserver" name="tfa98xx" dev="tmpfs" ino=9770 scontext=u:r:mediaserver:s0 tcontext=u:object_r:device:s0 tclass=chr_file |
首先,我们先看一下访问失败的原因:从log看,应该是mediaserver域的进程没有权限读写device类型的字符设备文件。那么我们能不能在mediaserver.te中加入访问权限呢?
在domain.te中有如下定义:
[external/sepolicy/domain.te] neverallow { domain -unconfineddomain -ueventd } device:chr_file { open read write }; |
也就是说除了unconfineddomain和uevented域外,所有在domain域中的进程都不能对device类型的字符设备文件执行open,read,write操作。
mediaserver也属于domain域,所以肯定不能通过添加策略来设置访问权限,怎么办呢?
在mediaserver.te中,我们发现mediaserver域是可以对audio_device类型的字符设备执行读写的:
[external/sepolicy/mediaserver.te] allow mediaserver audio_device:chr_file rw_file_perms; |
那么,能不能通过打标签的方法,把/dev/tfa98xx设置为audio_device类型呢?答案是肯定的。
在file_context中设置/dev/tfa98xx的安全属性,问题解决了:
[external/sepolicy/file_context] /dev/tfa9890 u:object_r:audio_device:s0 |
Android5.0的CTS测试中已经包含了安全策略相关的测试项:
runcts -c android.security.cts.SELinuxTest -m testSELinuxPolicyFile
测试中出现了下面的错误信息:
android.security.cts.SELinuxTest#testSELinuxPolicyFile FAIL |
查看device_logcat,能看到
System.out: SELinux avc rule neverallow58 failed 2 out of 100 checks. |
neverallow58测试出现2个失败,那么这个neverallow58是个啥呢?
先看一下测试用的apk,在android-cts/repository/testcases下有个CtsSecurityTestCases.apk,解压缩后asset目录下有个selinux_policy.xml文件,这个文件里面记录了所有CTS测试项。
… …
|
看来neverallow58就是检查souce定义的这些域中的进程对target类型,也就是security_type类型的目录能否执行create和setattr操作,当然这是neverallow的,也就是不能执行操作才能pass。
在external/sepolicy目录看看我们最近的修改,发现为了让init_shell域的进程能够删除/data/security目录,修改了domain.te中的neverallow规则:
[external/sepolicy/domain.te] neverallow { domain -init -system_server -init_shell } security_file:dir { create setattr }; |
看来neverallow的规则,还是不能随便修改的,否则就可能导致CTS测试失败,去掉init_shell,CTS测试PASS;
[external/sepolicy/domain.te] neverallow { domain -init -system_server } security_file:dir { create setattr }; |
SEAndroid-for-share.pdf
SEAndroid策略:http://blog.csdn.net/xbalien29/article/details/19505575
SEAndroid安全机制框架分析:http://blog.csdn.net/luoshengyang/article/details/37613135
SELinux策略配置语言:http://blog.csdn.net/miroku_it/article/details/3685334
SELinux:百度百科