SELinux 即Security-Enhanced Linux,由美国国家安全局(NSA)发起,Secure Computing Corporation (SCC) 和 MITRE直接参与开发,以及很多研究机构(如犹他大学)一起参与的强制性安全审查机制,该系统最初是作为一款通用访问软件,发布于2000年12月(代码采用 GPL 许可发布)。并在Linux Kernel 2.6 版本后,有直接整合进入SELinux, 搭建在Linux Security Module(LSM)基础上,目前已经成为最受欢迎,使用最广泛的安全方案。而SEAndroid是Google在Android 4.4上正式推出的一套以SELinux为基础于核心的系统安全机制。
1、背景知识
进程理论上所拥有的权限与执行它的用户的权限相同。比如,以root用户启动Browser,那么Browser就有root用户的权限,在Linux系统上能干任何事情。也就是像4.4以前的版本,只要我们对结点给予足够的权限,就可以随意的进行任意的操作。
Linux DAC有明显的不足,其中一个重要点就是,Root权限“无法无天”,几乎可以做任意事情,一旦入侵者拿到root权限,即已经完全掌控了系统。另外每一个进程默认都拿到对应这个用户的所有权限,可以改动/删除这个用户的所有文件资源,明显这个难以防止恶意软件。所以在DAC之外设计了一个新的安全模型,叫MAC(Mandatory Access control),强制性访问控制,即系统针对每一项访问都进行严格的限制,具体的限制策略由开发者给出。MAC的处世哲学非常简单:即任何进程想在SELinux系统中干任何事情,都必须先在安全策略配置文件中赋予权限。凡是没有出现在安全策略配置文件中的权限,进程就没有该权限。
LinuxMAC针对DAC的不足,要求系统对每一项访问,每访问一个文件资源都需要进行针对性的验证。而这个针对性的验证是根据已经定义好了的策略进行。在Linux Kernel,所有的MAC机制都是搭建在Linux Security Modules(LSM)基础上,包括有:SELinux、Apparmor、Smack 和TOMOYOLinux等。目前SELinux已经成了事实上的行业标准。针对Linux DAC,MAC可以明显弥补DAC的缺陷,一方面限制Root权限,即使你有root权限,如果无法通过MAC验证,那么一样的无法真正执行相关的操作。另外对每一项权限进行了更加完整的细化,可限制用户对资源的访问行为。
2、SELinux and SEAndroid的发展
估计在后续版本中,Google 都会要求强制性开启SELinux,以保证手机的安全。
SELinux 给Android 带来下面影响:
* 严格限制了ROOT 权限,以往ROOT “无法无天” 的情况将得到极大的改善。* 通过SELinux 保护,降低系统关键进程受攻击的风险,普通进程将没有权限直接连接到系统关键进程。
* 进一步强化APP 的沙箱机制,确保APP 难以做出异常行为或者攻击行为。
* 将改变APP 一旦安装,权限就已经顶死的历史,APP权限动态调整将成为可能。
3、SElinux、SEAndroid基本架构与原理
SELinux是典型的MAC实现,对系统中每个对象都生成一个安全上下文(Security Context), 每一个对象访问系统的资源都要进行安全上下文审查。审查的规则包括类型强制检测(type enforcement),多层安全审查(Multi-LevelSecurity),及基于角色的访问控制(RBAC: Role Based Access Control)。
SELinux的整体结构如下图所示:SELinux 包含五个基本组成:
* 用于处理文件系统的辅助模块,即SELinuxFS。* 集成Linux Security Modules的hooks sets。
* Security Policy Database。
* Security Label验证模块。
* Access Vector Cache (AVC),访问向量缓存,以便提高验证速度。
* 进程通过系统调用(System Call)访问某个资源,进入Kernel后,先会做基本的检测,如果异常则直接返回。
* Linux Kernel DAC审查,如果异常则直接返回.
* 调用Linux Kernel Modules的相关hooks,对接到SELinux的hooks,进而进行MAC验证,如果异常则直接返回。
* 访问真正的系统资源。
* 返回用户态,将结构反馈。
SELinux 给Linux 的所有对象都分配一个安全上下文(Security Context), 描述成一个标准的字符串。这里的对象分为两种类型,Subject主体和Object访问对象。主体通常是以进程为单位,通常就是进程,而客体就是进程访问的资源,通常是以文件为单位。
在开启了SEAndroid安全机制的设备上执行ls –Z就可以看到一个文件的上下文。
1、 u:同样是user之意,它代表创建这个文件的SELinux user。
2、 object_r:文件是死的东西,它没法扮演角色,所以在SELinux中,死的东西都用object_r来表示它的role。
3、 touch_device:死的东西的Type,和进程的Domain其实是一个意思。它表示root目录对应的Type是touch_device。
4、 s0:MLS的级别。
在来看看进程的SContext,同样在开启SEAndroid安全机制后,可以利用ps -Z来查看进程的上下文。
图6:进程SContext
图六中最左边的那一列是进程的SContext,以进程/system/bin/af7133d的SContext为例,其值为u:r:af7133d:s0,其中:
1、u为user的意思。SEAndroid中定义了一个SELinux用户,值为u。
2、r为role的意思。role是角色之意,它是SELinux中一种比较高层次,更方便的权限管理思路,即Role BasedAccess Control(基于角色的访问控制,简称为RBAC)。简单点说,一个u可以属于多个role,不同的role具有不同的权限。
3、af7133d,代表该进程所属的Domain为af7133d。MAC的基础管理思路其实不是针对上面的RBAC,而是所谓的Type Enforcement Accesc Control(简称TEAC,一般用TE表示)。对进程来说,Type就是Domain。比如init这个Domain有什么权限,都需要通过[例子1]中allow语句来说明。
4、S0和SELinux为了满足军用和教育行业而设计的Multi-Level Security(MLS)机制有关。简单点说,MLS将系统的进程和文件进行了分级,不同级别的资源需要对应级别的进程才能访问。
在SEAndroid中,只定义了一个SELinux用户u,因此我们通过ps -Z和ls -Z命令看到的所有的进程和文件的安全上下文中的SELinux用户都为u。同时,SEAndroid也只定义了一个SELinux角色r,因此,我们通过ps-Z命令看到的所有进程的安全上下文中的SELinux角色都为r。
有时可能注意到,前面我们通过ps -Z命令看到进程init的安全上下文为“u:r:init:s0”,按照上面的分析,这是不是一个非法的安全上下文呢?答案是否定的,因为在另外一个文件external/sepolicy/init.te中,通过type语句声明了类型init,并且将domain设置为类型init的属性,如下所示:
由于init具有属性domain,因此它就可以像domain一样,可以和SELinux用户u和SELinux角色组合在一起形成合法的安全上下文。
SContext的标准格式如下所示:
user:role:type[:range]
1、 User: 用户, 非Linux UID。
2、Role: 角色,一个user可以属于多个role,不同的role具有不同的权限。它是SELinux中一种比较高层次,更方便的权限管理思路,即Role BasedAccess Control(基于角色的访问控制,简称为RBAC, SELinux 不推荐使用)。
3、Type: Subject或者Object的类型。 MAC的基础管理思路其实不是针对上面的RBAC,而是所谓的Type Enforcement Access Control(简称TEAC,一般用TE表示)。对进程来说,Type就是Domain。
Range:Multi-Level Security(MLS)的级别。MLS将系统的进程和文件进行了分级,不同级别的资源需要对应级别的进程才能访问。上面我们分析了SEAndroid安全机制中的对象安全上下文,接下来我们就继续分析SEAndroid安全机制中的安全策略。SEAndroid安全机制中的安全策略是在安全上下文的基础上进行描述的,也就是说,它通过主体和客体的安全上下文,定义主体是否有权限访问客体。
SEAndroid安全机制主要是使用对象安全上下文中的类型来定义安全策略,这种安全策略就称TypeEnforcement,简称TE。在external/sepolicy目录中,所有以.te为后缀的文件经过编译之后,就会生成一个sepolicy文件。这个sepolicy文件会打包在ROM中,并且保存在设备上的根目录下,即它在设备上的路径为/sepolicy。
通常,TE的描述一般在device/mediatek/common/sepolicy/和external/sepolicy中,SEAndroid为系统定义了33个te策略文件,这33个策略文件是:
adbd.te、file.te、su.te、app.te、gpsd.te、netd.te、system.te、bluetoothd.te、init.te、net.te、ueventd.te、bluetooth.te、installd.te、nfc.te、unconfined.te、cts.te、kernel.te、qemud.te、vold.te、dbusd.te、keystore.te、radio.te、wpa_supplicant.te、debuggerd.te、mediaserver.te、rild.te、zygote.te、device.te、servicemanager.te、domain.te、shell.te、drmserver.te、surfaceflinger.te。
对上述33个文件根据其策略规则针对的对象可分为三类:针对attribute的策略制定:ttribute是多个具有共性的type的集合,有unconfined.te、domain.te、CTS.te、bluetooth.te、net.te、file.te六个文件主要是直接针对attribute制定的策略,这种针对attribute制定的策略也就是同时对多个type制定策略一样。针对daemon domain的策略制定:分别为adbd.te、gpsd.te、netd.te、bluetoothd.te、zygote.te、ueventd.te、installd.te、vold.te、dbusd.te、keystore.te、debuggerd.te、mediaserver.te、rild.te、drmserver.te、surfaceflinger.te、qemud.te、servicemanager.te、su.te、shell.te、wpa_supplicant.te。这些文件都是为系统中的daemon进程进行策略的制定,它们都有着相应的daemon domain。最后的7个文件分别对系统的其他模块进行策略制定。app.te: 在这一文件里将安装在系统上的第三方app分类为受信任的app和不受信任的app,分别用不同的type表示,再分别为这两种app在访问网络,bluetooth,sdcard,数据,缓存,binder等等名感位置时设置相应权限。system.te: 这一文件主要针对的是系统app和system server进程。对系统app访问binder、systemdata files、dalvikcatch、keystone等进行权限控制,对system server访问网络、bluetooth、netlink、app、binder、device、data files、socket、cache files等进行权限控制。init.te: 在这一文件中声明了init拥有无限权限。nfc.te: 在这一文件中制定了nfc domain对nfc设备和相关数据文件的访问权限。kernel.te: 在这一文件中声明了kernel拥有无限权限。radio.te: 在这一文件中制定了radio domain对init、rild和相关数据文件的访问权限。device.te: 在这一文件中定义了所有跟设备相关的type,并将这些type都归到了dev_type属性中。而对于安全服务,用户空间的Security Server主要是用来保护用户空间资源的,以及用来操作内核空间对象的安全上下文的,它由应用程序安装服务PackageManagerService、应用程序安装守护进程installd、应用程序进程孵化器Zygote进程以及init进程组成。其中,PackageManagerService和installd负责创建App数据目录的安全上下文,Zygote进程负责创建App进程的安全上下文,而init进程负责控制系统属性的安全访问。
4、SELinux问题解决方法
* [307:logd.auditd]:表示此LOG 是通过auditd 打印的;
* type=1400 : SYSCALL ;如果type=AVC 表示为kernelevents, type=USER_AVC 表示user-space object manager events ;
* audit(1420070451.950:8):audit(time:serial_number);
* avc: denied { write }: field depend on what type of event is being audited.;
* pid=4663 comm="om.zte.engineer":如果是个进程,pid表示为进程号,comm为运行的进程名字;
* scontext=u:r:radio:s0:subject context为u:r:radio:s0;
* tcontext=u:object_r:sysfs:s0:target context 为u:object_r:sysfs:s0;
* tclass : the object class of the target class=system;
* permissive: permissive (1)or enforcing (0)。
简化方法:
1、 提取所有的avc LOG. 如 adb shell "cat /proc/kmsg | grepavc" > avc_log.txt
2、 使用 audit2allow tool 直接生成policy. audit2allow -i avc_log.txt 即可自动输出生成的policy
3、 将对应的policy 添加到selinux policy 规则中,对应MTK Solution, 您可以将它们添加在KK: mediatek/custom/common/sepolicy, L:device/mediatek/common/sepolicy 下面,如
allow zygoteresource_cache_data_file:dir rw_dir_perms;
allow zygote resource_cache_data_file:filecreate_file_perms;
===>mediatek/custom/common/sepolicy/zygote.te (KK)
===> device/mediatek/common/sepolicy/zygote.te (L)
这样做就可以达到允许zygote对resource_cache_data_file进行create、r/w操作。注意audit2allow它自动机械的帮您将LOG 转换成policy, 而无法知道你操作的真实意图,有可能出现权限放大问题,经常出现policy 无法编译通过的情况。
如果直接按照avc: denied 的LOG 转换出SELinux Policy, 往往会产生权限放大问题. 比如因为要访问某个device, 在这个device 没有细化SELinux Label 的情况下, 可能出现: <7>[11281.586780] avc: denied { read write } for pid=1217comm="mediaserver" name="tfa9897" dev="tmpfs"ino=4385 scontext=u:r:mediaserver:s0 tcontext=u:object_r:device:s0tclass=chr_file permissive=0
如果直接按照此LOG 转换出SELinuxPolicy: allowmediaserver device:chr_file {read write}; 那么就会放开mediaserver 读写所有device 的权限。而Google 为了防止这样的情况, 使用了neverallow 语句来约束, 这样你编译sepolicy 时就无法编译通过。
下面主要介绍一下如何缺什么补什么,一步一步到没有avc: denied问题,以上面的avc log为例:
分析过程:
缺少什么权限: { write}权限;
谁缺少权限: scontext=u:r:radio:s0;
对哪个文件缺少权限: tcontext=u:object_r:sysfs:s0
什么类型的文件: tclass=file
所以解决方法是在:radio.te中加入
allow radio sysfs:file write;
通过上面例子,我们可以总结出一般规律:允许某个scontext对某个tcontext拥有某个权限。
所以,可以得到一个万能套用公式:Scontext tcontext tclass avc denied权限
allow radio sysfs : file write
有时候avc denied的log不是一次性显示所以问题,可能是要等你解决一个权限问题之后,才会提示另外一个权限问题,所以有时我们必须一次一次的试,一次一次的加。
SEAndroid在te文件中定义了安全策略中最基本的参量type,同时将具有共性的type归在一起构成一个称为attribute的集合,policy的规则执行也能以attribute作为执行对象。SEAndroid为所有type共定义了17个attribute:
dev_type |
这一attribute包含了所有关于设备的type |
domain |
这一attribute包含了如下所列的所有关于进程的type,通常策略中的访问主体也就是进程所在的domain都包含在了这一attribute中 |
fs_type |
这一attribute包含了所有与文件系统相关的type。如下所列,大多是虚拟文件系统 |
file_type |
这一attribute包含了所有存在于非伪文件系统的相关文件的type |
exec_type |
这一attribute包含了所有关于domian接入点的type,多被用在domain transition中 |
data_file_type |
这一attribute包含了所有在/data目录下的文件type |
sysfs_type |
这一attribute包含了在sysfs文件系统下的所有文件的type,在SEAndroid中只有sysfs_writable包含在这个attribute中 |
node_type |
这一attribute包含了所有与nodes/hosts有关的type,在SEAndroid中只有node包含在这个attribute中 |
netif_type |
这一attribute包含了所有与网络接口相关的type,在SEAndroid中只有netif包含在这个attribute中 |
port_type |
这一attribute包含了所有与网络端口相关的type,在SEAndroid中只有port包含在这个attribute中 |
mlstrustedsubject |
这一attribute包含了所有能越过MLS检查的主体domain |
unconfineddomain |
这一attribute包含了所有拥有无限权限的type |
appdomain |
这一attribute包含了所有与app相关的type |
netdomain |
这一attribute包含了所有与需要访问网络的app相关的type |
bluetoothdomain |
这一attribute包含了所有与需要访问bluetooth的app相关的type |
binderservicedomain |
这一attribute包含了所有与binder服务相关的type |
而在L 版本中,我们经常遇到下面几种访问对象通常与绑定的类:
1、 device
类型定义 |
external/sepolicy/device.te device/mediatek/common/sepolicy/device.te |
类型绑定 |
external/sepolicy/file_contexts device/mediatek/common/sepolicy/file_contexts |
2、 File 类型:
类型定义 |
external/sepolicy/file.te device/mediatek/common/sepolicy/file.te |
类型绑定 |
external/sepolicy/file_contexts device/mediatek/common/sepolicy/file_contexts |
3、 虚拟File类型:
类型定义 |
external/sepolicy/file.te device/mediatek/common/sepolicy/file.te |
类型绑定 |
external/sepolicy/genfs_contexts device/mediatek/common/sepolicy/genfs_contexts |
4、 Service类型
类型定义 |
external/sepolicy/service.te device/mediatek/common/sepolicy/service.te |
类型绑定 |
external/sepolicyservice_contexts device/mediatek/common/sepolicy/service_contexts |
5、 Property类型
类型定义 |
external/sepolicy/property.te device/mediatek/common/sepolicy/property.te |
类型绑定 |
external/sepolicy/property_contexts device/mediatek/common/sepolicy/property_contexts |