Selinux SeAndroid理解

 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、背景知识

         SELinux出现之前,Linux上的安全模型叫DAC,全称是Discretionary Access Control,自主访问控制。自主访问控制, 即系统只提供基本的验证, 完整的访问控制由开发者自己控制。Linux DAC采用了一种非常简单的策略, 将资源访问者分成三类, 分别是Owner, Group,Other;资源针对这三类访问者设置不同的访问权限。而访问权限又分成read、 write、 execute。访问者通常是进程有自己的uid/gid, 通过uid/gid和文件权限匹配, 来确定是否可以访问。将Root权限根据不同的应用场景划分成许多的Root Capabilities, 其中如果有CAP_DAC_OVERRIDE这项的话, 可以直接绕过Linux DAC限制。DAC的核心思想很简单,就是:

       进程理论上所拥有的权限与执行它的用户的权限相同。比如,以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验证,那么一样的无法真正执行相关的操作。另外对每一项权限进行了更加完整的细化,可限制用户对资源的访问行为。
      我们知道android是基于linux实现的,NSA为了linux的安全性开发了一套安全机制SElinux,但由于Android系统有着独特的用户空间,因此SELinux不能完全适用于Android系统。为此,NSA针对Android系统,在SElinux基础上开发了SEAndroid。 
     SEAndroid安全机制所要保护的对象是系统中的资源,这些资源分布在各个子系统中,例如我们经常接触的文件就是分布文件子系统中的。实际上,系统中需要保护的资源非常多,除了前面说的文件之外,还有进程、socket和IPC等等。对于Android系统来说,由于使用了与传统Linux系统不一样的用户空间运行时,即应用程序运行时框架,因此它在用户空间有一些特有的资源是需要特别保护的,例如系统属性的设置等等。

2、SELinux and SEAndroid的发展

       Android是建立在标准的Linux Kernel基础上,自然也可以开启SELinux,通常在通用移动平台上,很少开启这样的安全服务,Google为了进一步增强Android的安全性,经过长期的准备,目前已经在Android 5.0(L)上有完整的开启SELinux,并对SELinux进行深入整合形成了SEAndroid。
      SELinux的更新史如下图所示:

                                                                                                                              图一:SELinux发展图
        上图中有Permissive跟Enforce两种模式,Permissive 模式,只打印audit 异常LOG,不拒绝请求, Enforce 模式,即打印audit 异常LOG, 也拒绝请求。AndroidKK版本只对netd、installd、zygote、vold四个原本具有root权限的process,以及它们fork出的子进程启用Enforce模式。但到了L 5.0以上版本普遍性开启SELinux Enforce mode。
    
                                                                                                                    图2:Android 启动模式变化
    

估计在后续版本中,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的整体结构如下图所示:
     
图3:SELinux整体结构体
      

SELinux 包含五个基本组成:

     * 用于处理文件系统的辅助模块,即SELinuxFS。
     * 集成Linux Security Modules的hooks sets。
     * Security Policy Database。
     * Security Label验证模块。
     *  Access Vector Cache (AVC),访问向量缓存,以便提高验证速度。
基本的访问流程如下图所示:

图4:基本访问流程图
  具体 流程如下:
     * 进程通过系统调用(System Call)访问某个资源,进入Kernel后,先会做基本的检测,如果异常则直接返回。
     * Linux Kernel DAC审查,如果异常则直接返回.
     * 调用Linux Kernel Modules的相关hooks,对接到SELinux的hooks,进而进行MAC验证,如果异常则直接返回。
     * 访问真正的系统资源。
     * 返回用户态,将结构反馈。 
        SEAndroid安全机制是基于SELinux实现的,SEAndroid所要保护的对象是系统中的资源,这些资源分布在各个子系统中,例如我们经常接触的文件就是分布文件子系统中的。实际上,系统中需要保护的资源非常多,除了前面说的文件之外,还有进程、socket和IPC等等。对于Android系统来说,由于使用了与传统Linux系统不一样的用户空间运行时,即应用程序运行时框架,因此它在用户空间有一些特有的资源是需要特别保护的,例如系统属性的设置。
       SEAndroid安全机制也是分为内核空间跟用户空间两部分,以SELinux文件系统接口为边界。内核空间的实现是基于LSM实现的,上面主要是内核空间实现。而在用户空间,涉及到安全上下文(Security Context)、安全策略(SEAndroid Policy)和安全服务(Security Server)等模块。下面主要分析一下这三个模块。

      SELinux 给Linux 的所有对象都分配一个安全上下文(Security Context), 描述成一个标准的字符串。这里的对象分为两种类型,Subject主体和Object访问对象。主体通常是以进程为单位,通常就是进程,而客体就是进程访问的资源,通常是以文件为单位。

       在开启了SEAndroid安全机制的设备上执行ls –Z就可以看到一个文件的上下文。

图5:文件SContext
  图5中最右边的那一列是文件的SContext,以结点touch的SContext为例,其值为u:object_r:touch_device:s0,其中:

     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的属性,如下所示:

[cpp]  view plain  copy
  1. type init, domain;   

   由于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问题解决方法

      查找SELinux权限问题,一般的方法是通过查看LOG中是否有标准的SELinux Policy Exception。在Kernel Log / Main Log 中查询关键字"avc:",如果出现以下log,则基本说明该问题就是SELinux权限问题引起的,在解决安全权限问题前,我们必须先读懂log中的信息:
[cpp]  view plain  copy
  1. [  215.214223]<0> (0)[307:logd.auditd]type=1400 audit(1420070451.950:8): avc: denied { write } for pid=4663 comm="om.zte.engineer" name="brightness" dev="sysfs" ino=9451 scontext=u:r:radio:s0 tcontext=u:object_r:sysfs:s0 tclass=file permissive=0  
    * [  215.214223]:kernel time ;

    * [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)。

上面语句的大概意思为:om.zte.engineer这个process,使用radio的source context,访问sysfs这个文件类型时,并write文件时,被SELinux拒绝访问。
        而对于如何解决该类权限问题,一般的做法是,缺少什么就补什么,先介绍一下简化方法:

简化方法:

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为例:

[cpp]  view plain  copy
  1. [  215.214223]<0> (0)[307:logd.auditd]type=1400 audit(1420070451.950:8): avc: denied { write } for pid=4663 comm="om.zte.engineer" name="brightness" dev="sysfs" ino=9451 tcontext=u:object_r:sysfs:s0 tclass=file permissive=0  
    分析过程:

     缺少什么权限:           { 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


  



猜你喜欢

转载自blog.csdn.net/sam0535/article/details/78731434