转载:http://blog.csdn.net/innost/article/details/19299937
SEAndroid是Google在Android 4.4上正式推出的一套以SELinux为基础于核心的系统安全机制。而SELinux则是由美国NSA(国安局)和一些公司(RedHat、Tresys)设计的一个针对Linux的安全加强系统。
NSA最初设计的安全模型叫FLASK,全称为Flux Advanced Security Kernel(由Uta大学和美国国防部开发,后来由NSA将其开源),当时这套模型针对DTOS系统。后来,NSA觉得Linux更具发展和普及前景,所以就在Linux系统上重新实现了FLASK,称之为SELinux。
Linux Kernel中,SELinux通过Linux Security Modules实现。在2.6之前,SElinux通过Patch方式发布。从2.6开始,SELinux正式入驻内核,成为标配。
思考:
1 同样是政府部门,差别咋这么大?
2 同样涉及操作系统和安全相关,NSA为何敢用Linux,为什么想方设法要开源?
由于Linux有多种发行版本,所以各家的SELinux表现形式也略有区别。具体到Android平台,Google对其进行了一定得修改,从而得到SEAndroid。
本文将先对SELinux相关知识进行介绍,然后看看Android是如何实现SELinux的(咱们只看用户空间)。
要求:最好能下到Android 4.4源码,可http://blog.csdn.net/innost/article/details/14002899
目标:学完本文,读者应该可以轻松修改相关安全策略文件,以进一步在安全方面定制自己的Android系统。
一 SELinux背景知识
1. DAC和MAC
SELinux出现之前,Linux上的安全模型叫DAC,全称是Discretionary Access Control,翻译为自主访问控制。DAC的核心思想很简单,就是:
进程理论上所拥有的权限与执行它的用户的权限相同。比如,以root用户启动Browser,那么Browser就有root用户的权限,在Linux系统上能干任何事情。
显然,DAC太过宽松了,所以各路高手想方设法都要在Android系统上搞到root权限。那么SELinux如何解决这个问题呢?原来,它在DAC之外,设计了一个新的安全模型,叫MAC(Mandatory Access Control),翻译为强制访问控制。MAC的处世哲学非常简单:即任何进程想在SELinux系统中干任何事情,都必须先在安全策略配置文件中赋予权限。凡是没有出现在安全策略配置文件中的权限,进程就没有该权限。来看一个SEAndroid中设置权限的例子:
/* from external/sepolicy/netd.te 下面这条SELinux语句表示 允许(allow )netd域(domain)中的进程 ”写(write)“ 类型为proc的文件 注意,SELinux中安全策略文件有自己的一套语法格式,下文我们将详细介绍它 */ allow netd proc:file write如果没有在netd.te中使用上例中的权限配置allow语句,则netd就无法往/proc目录下得任何文件中写数据,即使netd具有root权限。
图1中最左边的那一列是进程的SContext,以第一个进程/system/bin/logwrapper的SContext为例,其值为u:r:init:s0,其中:
u为user的意思。SEAndroid中定义了一个SELinux用户,值为u。
r为role的意思。role是角色之意,它是SELinux中一种比较高层次,更方便的权限管理思路,即Role Based Access Control(基于角色的访问控制,简称为RBAC)。简单点说,一个u可以属于多个role,不同的role具有不同的权限。RBAC我们到最后再讨论。
init,代表该进程所属的Domain为init。MAC的基础管理思路其实不是针对上面的RBAC,而是所谓的Type Enforcement Accesc Control(简称TEAC,一般用TE表示)。对进程来说,Type就是Domain。比如init这个Domain有什么权限,都需要通过[例子1]中allow语句来说明。
S0和SELinux为了满足军用和教育行业而设计的Multi-Level Security(MLS)机制有关。简单点说,MLS将系统的进程和文件进行了分级,不同级别的资源需要对应级别的进程才能访问。后文还将详细介绍MLS。
再来看文件的SContext,读者可通过ls -Z来查看,如图2所示:
图2中,倒数第二列所示为Nexus 7根目录下几个文件和目录的SContext信息,以第一行root目录为例,其信息为u:object_r:rootfs:s0:
u:同样是user之意,它代表创建这个文件的SELinux user。
object_r:文件是死的东西,它没法扮演角色,所以在SELinux中,死的东西都用object_r来表示它的role。
rootfs:死的东西的Type,和进程的Domain其实是一个意思。它表示root目录对应的Type是rootfs。
s0:MLS的级别。
根据SELinux规范,完整的SContext字符串为:
user:role:type[:range]
注意,方括号中的内容表示可选项。s0属于range中的一部分。下文再详细介绍range所代表的Security Level相关的知识。
看,SContext的核心其实是前三个部分:user:role:type。
刚才说了,MAC基本管理单位是TEAC(Type Enforcement Accesc Control),然后是高一级别的Role Based Accesc Control。RBAC是基于TE的,而TE也是SELinux中最主要的部分。
下面来看看TE。
2.1 TE介绍
在例子1中,大家已经见过TE的allow语句了,再来细致研究下它:
[例子2]
allow netd proc:file write这条语句的语法为:
rule_name source_type target_type : class perm_set我们直接来看几个实例:
//SEAndroid中的安全策略文件policy.conf #允许zygote域中的进程向init type的进程(Object Class为process)发送sigchld信号 allow zygote init:process sigchld; #允许zygote域中的进程search或getattr类型为appdomain的目录。注意,多个perm_set #可用{}括起来 allow zygote appdomain:dir { getattr search }; #来个复杂点的: #source_type为unconfineddomain target_type为一组type,由 #{ fs_type dev_type file_type }构成。object_class也包含两个,为{ chr_file file } #perm_set语法比较奇特,前面有一个~号。它表示除了{entrypoint relabelto}之外,{chr_file #file}这两个object_class所拥有的其他操作 allow unconfineddomain {fs_type dev_type file_type}:{ chr_file file } \ ~{entrypoint relabelto}; #特殊符号除了~外,还有-号和*号,其中: # 1):-号表示去除某项内容。 # 2):*号表示所有内容。 #下面这条语句中,source_type为属于appdomain,但不属于unconfinedomain的进程。 #而 *表示所有和capability2相关的权限 #neverallow:表示绝不允许。 neverallow { appdomain -unconfineddomain } self:capability2 *;特别注意,前面曾提到说权限必须显示声明,没有声明的话默认就没有权限。那neverallow语句就没必要存在了。因为”无权限“是不需要声明的。确实如此,neverallow语句的作用只是在生成安全策略文件时进行检查,判断是否有违反neverallow语句的allow语句。例如,笔者修改shell.te中一个语句后,生成安全策略文件时就检测到了冲突,如图3所示:
如图3所示,笔者修改shell.te后,意外导致了一条allow语句与neverallow语句冲突,从而生成安全策略文件失败。
下面我们来看上述allow语句中所涉及到的object class和perm set。
(1) Object class和Perm Set
Object class很难用语言说清楚它到底是怎么定义的,所以笔者也不废话,直接告诉大家常见的Object class有哪些。见下面的SEPolicy示例:
[external/sepolicy/security_classes示例]
....... #此文件定义了Android平台中支持的Object class #根据SELinux规范,Object Class类型由class关键字申明 # file-related classes class filesystem class file #代表普通文件 class dir #代表目录 class fd #代表文件描述符 class lnk_file #代表链接文件 class chr_file #代表字符设备文件 ...... # network-related classes class socket #socket class tcp_socket class udp_socket ...... class binder #Android平台特有的binder class zygote #Android平台特有的zygote #Android平台特有的属性服务。注意其后的userspace这个词 class property_service # userspace和用户空间中的SELinux权限检查有关,下文再解释上述示例展示了SEAndroid中Object Class的定义,其中:
#SELinux规范中,定义perm set有两种方式,一种是使用下面的common命令 #其格式为:common common_name { permission_name ... } common定义的perm set能 #被另外一种perm set命令class所继承 #以下是Android平台中,file对应的权限(perm set)。其大部分权限读者能猜出是干什么的。 #有一些权限需要结合文后的参考文献来学习 common file { ioctl read write create getattr setattr lock relabelfrom relabelto append unlink link rename execute swapon quotaon mounton } #除了common外,还有一种class命令也可定义perm set,如下面的例子: #class命令的完整格式是: #class class_name [ inherits common_name ] { permission_name ... } #inherits表示继承了某个common定义的权限 注意,class命令定义的权限其实针对得就是 #某个object class。它不能被其他class继承 class dir inherits file { add_name remove_name reparent search rmdir open audit_access execmod } #来看SEAndroid中的binder和property_service这两个Object class定义了哪些操作权限 class binder { impersonate call set_context_mgr transfer } class property_service { set }提示:Object class和Perm set的具体内容(SELinux中其实叫Access Vector)都和Linux系统/Android系统密切相关。所以,从知识链的角度来看,Linux编程基础很重要。
#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的关系最难理解,因为“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}; 前面讲过,TE的完整格式为: rule_name source_type target_type : class perm_set所以,attribute可以出现在source_type中,也可以出现在target_type中。
#来自external/sepolicy/netd.te文件 #永远不允许netd域中的进程 读写 dev_type类型的 块设备文件(Object class为blk_file) neverallow netd dev_type:blk_file { read write }(3) RBAC和constrain
#Android中只定义了一个role,名字就是r role r; #将上面定义的r和attribute domain关联起来 role r types domain;再来看user的定义。
#支持MLS的user定义格式为: #user seuser_id roles role_id level mls_level range mls_range; #不支持MLS user定义格式为: #user seuser_id roles role_id; #SEAndroid使用了支持MLS的格式。下面定义的这个user u,将和role r关联。 #注意,一个user可以和多个role关联。 #level之后的是该user具有的安全级别。s0为最低级,也就是默认的级别,mls_systemHigh #为u所能获得的最高安全级别(security level)。此处暂且不表MLS user u roles { r } level s0 range s0 - mls_systemhigh;那么,Roles和User中有什么样的权限控制呢?
#注意,关键字也是allow,但它和前面TE中的allow实际上不是一种东西 #下面这个allow允许from_role_id切换到to_role_id allow from_role_id to_role_id;2) 角色之间的关系。SELinux中,Role和Role之间的关系和公司中的管理人员的层级关系类似,例如:
#dominance语句属于deprecated语句,MLS中有新的定义层级相关的语句。不过此处要介绍的是 #selinux中的层级关系 #下面这句话表示super_r dominate(统治,关键词dom) sysadm_r和secadm_r这两个角色 #反过来说,sysadm_r和secadm_r dominate by (被统治,关键词 domby) super_r #从type的角度来看,super_r将自动继承sysadm_r和secadm_r所关联的type(或attribute) dominance { role super_r {role sysadm_r; role secadm_r; }3)其他内容,由于SEAndroid没有使用,此处不表。读者可阅读后面的参考文献。
#constrain标准格式为: # constrain object_class_set perm_set expression ; #下面这句话表示只有source和target的user相同,并且role也相同,才允许 #write object_class为file的东东 constrain file write (u1 == u2 and r1 == r2) ;前面已经介绍过object_class和perm_set了,此处就不再赘述。constrain中最关键的是experssion,它包含如下关键词:
#先看initial_sids sid kernel #sid是关键词,用于定义一个sid sid security sid unlabeled sid fs sid file sid file_labels sid init ...... #再来看initial_sid_context sid kernel u:r:kernel:s0 #将initial_sids中定义的sid和初始的SContext关联起来 sid security u:object_r:kernel:s0 sid unlabeled u:object_r:unlabeled:s0 sid fs u:object_r:labeledfs:s0 sid file u:object_r:unlabeled:s0 sid file_labels u:object_r:unlabeled:s0 sid init u:object_r:unlabeled:s0提示:sid的细节需要查看LSM的实现。此处不拟深究它。另外,这两个文件也是和Kernel紧密相关的,所以一般不用修改它们。
#先要使用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检查点,所以需要三个权限。
#定义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; ')在external/sepolicy/init_shell.te中就有上述宏的用法:
./init_shell.te:4:domain_auto_trans(init, shell_exec, init_shell)除了DT外,还有针对Type的Transition。举个例子,假设目录A的SContext为u:r:dir_a,那么默认情况下在该目录下创建的文件都具有u:r:dir_a这个SContext。所以我们也要针对死得东西进行打标签。
# 定义file_type_trans(domain, dir_type, file_type)宏 # define(`file_type_trans', ` # ra_dir_perms是一个宏,由global_macros文件定义,其值为: #define(`ra_dir_perms', `{ r_dir_perms add_name write }') allow $1 $2:dir ra_dir_perms; # create_file_perms也是一个宏,定义在global_macros文件中,其值为: # define(`create_file_perms', `{ create setattr rw_file_perms # link_file_perms }') #而r_dir_perms=define(`r_dir_perms', `{ open getattr read search ioctl } 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域中的进程在某个Type为dir_type的目录中创建文件时,该文件的 #SContext应该是file_type define(`file_type_auto_trans', ` file_type_trans($1, $2, $3) type_transition $1 $2:dir $3; #notdevfile_class_set也是一个宏,由global_macros文件定义,其值为 # define(`notdevfile_class_set', `{ file lnk_file sock_file fifo_file }') type_transition $1 $2:notdevfile_class_set $3; ')WoW,SEAndroid太这两个宏定义太复杂了,来看看官方文档中的最小声明是什么:
type_transition acct_t var_log_t:file wtmp_t; allow acct_t var_log_t:dir { read getattr lock search ioctl add_name remove_name write }; allow acct_t wtmp_t:file { create open getattr setattr read write append rename link unlink ioctl lock };在SEAndroid的app.te中,有如下TT设置:
./app.te:86:file_type_auto_trans(appdomain, download_file, download_file)DT和TT就介绍到这,翻来覆去就这么点东西,多看几遍就“柜”(用柜字,打一成语,参考2014年中国首次猜谜大会)了