SEAndroid简介

SEAndroid简介


http://blog.csdn.net/modianwutong/article/details/43114883

  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架构


  SEAndroid在架构和机制上与SELinux完全一样,考虑到移动设备的特点,所以移植到Android上的只是SELinux的一个子集。SEAndroid的安全检查几乎覆盖了所有重要的系统资源,包括域转换,类型转换,进程、内核、文件、目录、设备,App,网络及IPC相关的操作。

  接下来,我们就来看一下SEAndroid安全机制的整体框架,如下所示:图1.SEAndroid安全机制框架

       SEAndroid简介_第1张图片

从上面的架构图可以看出来,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文件系统访问接口;


SEAndroid策略


DAC和MAC


  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 }


RBAC(选读)


  绝大多数情况下,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。


SEAndroid实战


  通过上面的讲解,各位应该对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


CTS相关问题


  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测试项。

       kernel

       sdcardd

       init_shell

       … …

       security_file

       

              create

              setattr

      

  看来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:百度百科

你可能感兴趣的:(SEAndroid简介)