android中SELINUX规则分析和语法简介

SELINUX简介

SELINUX是可以理解为一种android上面的安全机制,是有美国国家安全局和一些公司设计的一个针对linux的安全加强系统,我们可以通过配置SELINUX的相关policy,来定制自己的手机的一些权限,比如,我们可以完全让root用户没有任何的权限和user一样

查看SELINUX权限

在android里面,有两个类型:

  1. 进程
  2. 文件

针对这两种类型,我们可以先来看看他们的不同。

1. 查看进程权限

在android上面,adb shell之后进入手机,ps -Z可以查看当前进程所拥有的selinux的权限。

$ ps -Z
LABEL                          USER     PID   PPID  NAME
u:r:init:s0                    root      1     0     /init
u:r:kernel:s0                  root      2     0     kthreadd
...
u:r:kernel:s0                  root      258   2     irq/322-HPH_R O
u:r:logd:s0                    logd      259   1     /system/bin/logd
u:r:healthd:s0                 root      260   1     /sbin/healthd
u:r:lmkd:s0                    root      261   1     /system/bin/lmkd
u:r:servicemanager:s0          system    262   1     /system/bin/servicemanager
u:r:vold:s0                    root      263   1     /system/bin/vold
u:r:surfaceflinger:s0          system    264   1     /system/bin/surfaceflinger
u:r:tctd:s0                    root      265   1     /system/bin/tctd
u:r:rfs_access:s0              system    268   1     /system/bin/rfs_access
u:r:tee:s0                     system    271   1     /system/bin/qseecomd
u:r:kernel:s0                  root      280   2     kworker/3:1H
u:r:kernel:s0                  root      290   2     kauditd
u:r:rmt_storage:s0             nobody    291   1     /system/bin/rmt_storage
u:r:shell:s0                   shell     292   1     /system/bin/sh
u:r:netd:s0                    root      295   1     /system/bin/netd
u:r:debuggerd:s0               root      296   1     /system/bin/debuggerd
u:r:tee:s0                     system    297   271   /system/bin/qseecomd

在这个例子中,我们可以进行分析。

  • u:在android中,只定义了一个user即为u
  • r:进程统一定义成r , 文件统一定义成 object_r
  • init:进程所属的域 , 不唯一。
  • s0:一种安全等级

2. 查看文件权限

另外就是文件,文件想要查看相关SELINUX权限的话,需要去执行ls -Z

$ ls -Z
drwxr-x--x root     sdcard_r          u:object_r:rootfs:s0 storage
drwx--x--x root     root              u:object_r:tmpfs:s0 synthesis
dr-xr-xr-x root     root              u:object_r:sysfs:s0 sys
drwxr-xr-x root     root              u:object_r:system_file:s0 system
drwxrwxr-x system   tctpersist          u:object_r:tct_persist_file:s0 tctpersist
lrwxrwxrwx root     root              u:object_r:rootfs:s0 tombstones -> /data/tombstones
-rw-r--r-- root     root              u:object_r:rootfs:s0 ueventd.qcom.rc
-rw-r--r-- root     root              u:object_r:rootfs:s0 ueventd.rc
  • u:在android中,只定义了一个user即为u
  • object_r:代表是一个文件
  • rootfs:这种代表文件所属的域,看有的解释说可以理解成type
  • s0:一种安全等级

如何配置selinux

1.基本语法

很多te文件集中在external\sepolicy文件夹下,厂商也是有自己的配置规则在devic目录下,例如RK项目中
BOARD_SEPOLICY_DIRS ?= device/rockchip/common/sepolicyBOARD_SEPOLICY_DIRS在系统编译时会被使用,非本文重点,有兴趣的话请自行查阅相关资料。

A. 上下文描述文件

可以在上下文的描述文件中为您的对象指定标签。

文件名归类
mac_permissions.xmlApp进程
seapp_contextsApp数据文件
file_contexts系统文件
property_contexts系统属性
  • file_contexts 用于为文件分配标签,并且可供多种用户空间组件使用。在创建新政策时,请创建或更新该文件,以便为文件分配新标签。
  • genfs_contexts 用于为不支持扩展属性的文件系统(例如,procvfat)分配标签。
  • property_contexts 用于为 Android 系统属性分配标签,以便控制哪些进程可以设置这些属性。
  • service_contexts 用于为 Android Binder 服务分配标签,以便控制哪些进程可以为相应服务添加(注册)和查找(查询)Binder 引用。
  • seapp_contexts 用于为app进程和 /data/data 目录分配标签。
  • mac_permissions.xml 用于根据应用签名和应用软件包名称(后者可选)为应用分配 seinfo 标记。随后,分配的 seinfo 标记可在 seapp_contexts 文件中用作密钥,以便为带有该 seinfo 标记的所有应用分配特定标签。

B. 策略文件te

以te结尾的文件是SELinux中的策略文件,它定义了作用域和标签。
来看一个te文件:

allow factory ttyMT_device:chr_file { read write open ioctl};
allow factory ttyGS_device:chr_file { read write open ioctl};
allow factory irtx_device:chr_file { read write ioctl open };

上面这几行就是最基本的te语句了,相似的te语句的会被归类在一个的te文件下面。如上面的语句都是作用于factory,则会在factory.te文件里。external\sepolicy中集中了很多系统定义的te文件

例如第一条

allow factory ttyMT_device:chr_file { read write open ioctl};

翻译一下就是

允许`factory`域里的进程或服务
对类型为`ttyMT_device`的类别为文件(`chr_file`)
执行`read`,`write`,`open`,`ioctl`权限的操作

te文件的基本语法:

# rule_name source_type target_type:class perm_set
  • rule_name:规则名,分别有allow,dontaudit,auditallow,neverallow等
  • source_type:源类型,主要作用是用来填写一个域(domain)
  • target_type:目标的类型
  • class:类别,目标(客体)是哪种类别,主要有File,Dir,Socket,SEAndroid还有Binder等,在这些基础上又细分出设备字符类型(chr_file),链接文件(lnk_file)等。可以通过ls -l查看文件类型
  • perm_set:动作集

我们从上到下按顺序介绍一下:
B1. rule_name

规则名称匹配是否允许不匹配是否允许匹配是否记录不匹配是否记录
allowYesNoNoYes
dontauditNoNoNoNo
auditallowNoNoYesYes
neverallow----
  • allow:允许某个进程执行某个动作
  • auditallow:audit含义就是记录某项操作。默认SELinux只记录那些权限检查失败的操作。 auditallow则使得权限检查成功的操作也被记录。注意,allowaudit只是允许记录,它和赋予权限没关系。赋予权限必须且只能使用allow语句。
  • dontaudit:对那些权限检查失败的操作不做记录。
  • neverallow:没有被allow到的动作默认就不允许执行的。neverallow只是显式地写出某个动作不被允许,如果添加了该动作的allow,则会编译错误

B2. source_type
指定一个“域”(domain),一般用于描述进程,该域内的的进程受该条TE语句的限制。用type关键字,把一个自定义的域与原有的域相关联

type shell, domain;

上面这句话的意思是,赋予shelldomain属性,同时,shell域属于domain这个集合里。如果有一个allow domain xxxxx的语句,同样地也给了shell xxxxx的属性

B3. target_type
指定进程需要操作的客体(文件,文件夹等)类型,同样是用type与一些已有的类型,属性相关联
以上面的ttyMT_device为例:

# 定义一个类型,属于dev_type属性
type ttyMT_device, dev_type; 

属性dev_typeexternal/sepolicyattributes的定义如下

attribute dev_type;

attribute关键字定义一个属性,type可以与一个或多个属性关联,例如:

type usb_device, dev_type, mlstrustedobject;

另外,还有一个关键字typeattribute,type有两个作用:

  1. 定义(声明)
  2. 关联某个属性。

可以把这两个作用分开,type定义,typeattribute进行关联

# 定义httpd_user_content_t,并关联两个属性
type httpd_user_content_t, file_type, httpdcontent;

分成两条语句进行表述:

#定义httpd_user_content_t
type httpd_user_content_t;
#关联属性
typeattribute httpd_user_content_t file_type, httpdcontent;

external/sepolicy/attributes里定义了很多属性,下面截取了一些常见的定义:

# All types used for devices.
attribute dev_type;
# All types used for processes.
attribute domain;
# All types used for filesystems.
attribute fs_type;
# All types used for files that can exist on a labeled fs.
# Do not use for pseudo file types.
attribute file_type;
# All types used for domain entry points.
attribute exec_type;
# All types used for property service
attribute property_type;
# All service_manager types created by system_server
attribute system_server_service;
# All domains that can override MLS restrictions.
# i.e. processes that can read up and write down.
attribute mlstrustedsubject;
# All types that can override MLS restrictions.
# i.e. files that can be read by lower and written by higher
attribute mlstrustedobject;
# All domains used for apps.
attribute appdomain;
# All domains used for apps with network access.
attribute netdomain;
# All domains used for binder service domains.
attribute binderservicedomain;

B4. class
客体的具体类别。用class来定义一个客体类别,来看一下external/sepolicy/security_classes文件

# 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

B5. perm_set
具体的操作,系统的定义在external/sepolicy/access_vectors中。有两种定义方法。
1.用common命令定义:
格式为:

common common_name { permission_name ... }

common定义的perm_set能被另外一种perm_set命令class所继承,而class定义的perm_set则不能被继承
如:

common file {
	ioctl read write create getattr setattr lock relabelfrom relabelto
	append unlink link rename execute swapon quotaon mounton
}

class命令定义:

# class class_name [ inherits common_name ] { permission_name ... }

例如class dir继承了file

class dir
inherits file
{
	add_name
	remove_name
	reparent
	search
	rmdir
	open
	audit_access
	execmod
}

最后是一些特殊的配置文件:

  • external/sepolicy/attributes -> 所有定义的attributes都在这个文件
  • external/sepolicy/access_vectors -> 对应了每一个class可以被允许执行的命令
  • external/sepolicy/roles -> Android中只定义了一个role,名字就是r,将r和attribute domain关联起来
  • external/sepolicy/users -> 其实是将user与roles进行了关联,设置了user的安全级别,s0为最低级是默认的级别,mls_systemHigh是最高的级别
  • external/sepolicy/security_classes -> 指的是上文命令中的class,个人认为这个class的内容是指在android运行过程中,程序或者系统可能用到的操作的模块
  • external/sepolicy/te_macros -> 系统定义的宏在te_macros文件
  • external/sepolicy/***.te -> 一些配置的文件,包含了各种运行的规则

2.几个例子

# 将init关联到domain,即将domain设置为init类型的属性
type init, domain;

# 允许init类型对unlabeled类型的filesystem进行mount的操作
allow init unlabeled:filesystem mount;

# 允许init类型对fotad类型的unix_stream_socket 进行bind和create的操作
allow init fotad:unix_stream_socket { bind create };

# appdomain是定义在te_macros里面的一个宏,很多的app规则会使用类似app_domain(shell)的命令将其添加进去
# 允许app去对anr_data_file类型的目录进行查找的操作
allow appdomain anr_data_file:dir search;
# 允许app对anr_data_file类型的file进行打开和添加操作
allow appdomain anr_data_file:file { open append };

#绝不允许app(除了有unconfineddomain属性的app)对kmem_device类型的字符设备进行读写的操作
neverallow { appdomain -unconfineddomain } kmem_device:chr_file { read write };

# 绝不允许除了unconfineddomain以外的app对self类型的capability2进行任何的操作
neverallow { appdomain -unconfineddomain } self:capability2 *;

# 声明一个httpd_user_content_t的类型,具有file_type和httpdcontent的属性
type httpd_user_content_t, file_type, httpdcontent;

# 声明一个httpd_user_content_t的类型
type httpd_user_content_t;
# 定义httpd_user_content_t具有file_type, httpdcontent的属性
typeattribute httpd_user_content_t file_type, httpdcontent;

# 允许所有具有app属性的内容可以去对self属性的rawip_socket进行create的操作
allow appdomain self:rawip_socket create_socket_perms;

# 允许user_t和domain属性的类对bin_t, file_type, sbin_t类型的file进行可执行的操作
allow {user_t domain} {bin_t file_type sbin_t}:file execute ;

# 这两条语句的表述其实是一致的,其实self指的是目标的类型和发起人的类型是一致的
allow user_t user_t:process signal;
allow user_t self:process signal;

# 允许user_t对bin_t类型的file进行除了write setattr ioctl相关的操作
allow user_t bin_t:file ~{ write setattr ioctl };

生成规则文件的方法

下面介绍一下最简单的安全策略(se-policy)添加方法,大家碰到SELinux导致的访问禁止问题,可以参考用这种方法确认和解决。

1.安装pc上的工具,用于自动生成安全策略

$ sudo apt-get install policycoreutils

2.刷userdebug/eng软件,先将SELinux设置成Permissive模式,只输出警告不阻止操作
使用getenforce命令查看当前模式:

$ adb shell getenforce
Enforcing

在Enforcing模式下,除安全策略允许外的操作都会被阻止;使用setenforce命令更改当前模式(root权限需要):

$ adb root
restarting adbd as root

$ adb shell setenforce 0

$ adb shell getenforce
Permissive

开发如果碰到怀疑是SELinux 可以通过这种方法关闭SELiunx( setenforce 0),以确认是不是SELinux引起的

3.按照流程完成整个操作,抓取log,过滤出警告信息
如果log较多,可以先用grep工具过滤一下:

$ grep "avc: *denied" log.txt > denied.txt
$ cat denied.txt
<14>[  389.521062] avc:  denied  { set } for property=audio.ftm.rcv_reverse scontext=u:r:system_app:s0 tcontext=u:object_r:default_prop:s0 tclass=property_servic

4.使用pc工具audit2allow生成安全策略
命令audit2allow用来一次性生成所有安全策略,输入为前面抓取的 log

$ audit2allow -i denied.txt
#============= system_app ==============
allow system_app default_prop:property_servic set;

使用audit2allow遇到的问题

本地运行audit2allow时发现一个很奇怪的问题,执行audit2allow -i 123.txt的时候首行log会失效,解析出来的结果是从第二行开始的,然后跟了下源码,发现调用到python2.7/dist-packages/sepolgen/audit.py__parse_line方法时因捕获到ValueError异常unable to open (null): Bad address,因而msg被设置为DaemonStartMessage类型,导致未能完成之后的解析。
可是奇怪的是哪怕第二行和第一行写的一模一样,audit2allow却不会报错。

$ cat 123.txt
<14>[  389.521062] avc:  denied  { set } for property=audio.ftm.rcv_reverse scontext=u:r:system_app:s0 tcontext=u:object_r:default_prop:s0 tclass=property_servic

$ audit2allow -i 123.txt
木有输出。。。

$ cat 456.txt
<14>[  389.521062] avc:  denied  { set } for property=audio.ftm.rcv_reverse scontext=u:r:system_app:s0 tcontext=u:object_r:default_prop:s0 tclass=property_servic
<14>[  389.521062] avc:  denied  { set } for property=audio.ftm.rcv_reverse scontext=u:r:system_app:s0 tcontext=u:object_r:default_prop:s0 tclass=property_servic

$ audit2allow -i 456.txt
#============= system_app ==============
allow system_app default_prop:property_servic set;
这次正常输出了。。。

python2.7/dist-packages/sepolgen/audit.py

def __parse_line(self, line):
   rec = line.split()
   for i in rec:
       found = False
       if i == "avc:" or i == "message=avc:" or i == "msg='avc:":
           msg = AVCMessage(line)
           found = True
       elif i == "security_compute_sid:":
           msg = ComputeSidMessage(line)
           found = True
       elif i == "type=MAC_POLICY_LOAD" or i == "type=1403":
           msg = PolicyLoadMessage(line)
           found = True
       elif i == "type=AVC_PATH":
           msg = PathMessage(line)
           found = True
       elif i == "type=DAEMON_START":
           msg = DaemonStartMessage(list)
           found = True
           
       if found:
           self.check_input_file = True
           try:
               msg.from_split_string(rec)
           except ValueError:
               msg = InvalidMessage(line)
           return msg
   return None

因为未跟踪到具体raise信息为unable to open (null): Bad addressValueError位置,未能继续跟进,所以在捕获到异常时又重新调用了一次msg.from_split_string

commit 227845eb421a425568a3bc2a0e92e8ad20a720b3
Author: liuxiuquan <liuxiuquan@ren001.com>
Date:   Tue Dec 11 16:38:49 2018 +0800

    [liuxq]add for audit2allow parse first line error

diff --git a/audit.py b/audit.py
index 56919be..53cd61c 100644
--- a/audit.py
+++ b/audit.py
@@ -370,11 +370,17 @@ class AuditParser:
     #      and valid audit message.
     def __parse_line(self, line):
         rec = line.split()
+
+        # 20181211 liuxq add for audit2allow parse first line error begin
+        isAvc = False
+        # 20181211 liuxq add for audit2allow parse first line error end
+
         for i in rec:
             found = False
             if i == "avc:" or i == "message=avc:" or i == "msg='avc:":
                 msg = AVCMessage(line)
                 found = True
+                isAvc = True
             elif i == "security_compute_sid:":
                 msg = ComputeSidMessage(line)
                 found = True
@@ -387,14 +393,33 @@ class AuditParser:
             elif i == "type=DAEMON_START":
                 msg = DaemonStartMessage(list)
                 found = True
-                
+
+
+            # 20181211 liuxq add for audit2allow parse first line error begin
+            # if found:
+            #     self.check_input_file = True
+            #     try:
+            #         msg.from_split_string(rec)
+            #     except ValueError:
+            #         msg = InvalidMessage(line)
+            #     return msg
+
             if found:
                 self.check_input_file = True
                 try:
                     msg.from_split_string(rec)
-                except ValueError:
+                except ValueError,err: #liuxq add
                     msg = InvalidMessage(line)
+                    #print "liuxq first get ValueError",err.message
+                    if isAvc and 'Bad' in err.message:
+                        msg = AVCMessage(line)
+                        try:
+                            msg.from_split_string(rec)
+                        except ValueError,err:
+                            #print "liuxq second get ValueError",err.message
+                            msg = InvalidMessage(line)
                 return msg
+            # 20181211 liuxq add for audit2allow parse first line error end
         return None
 
     # Higher-level parse function - take a line, parse it into an

可以看到修改audit.py后可以正常解析第一行log了

$ audit2allow -i 123.txt 
#============= system_app ==============
allow system_app default_prop:property_servic set;

本地环境:
Ubuntu 14.04.5 LTS
Python 2.7.6

参考文章:
https://source.android.com/security/selinux/implement
https://blog.csdn.net/chaoy1116/article/details/45063629
https://blog.csdn.net/chaoy1116/article/details/45063629
https://blog.csdn.net/jaczen/article/details/73028302

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值