目录:
SELINUX简介
SELINUX是可以理解为一种android上面的安全机制,是有美国国家安全局和一些公司设计的一个针对linux的安全加强系统,我们可以通过配置SELINUX的相关policy,来定制自己的手机的一些权限,比如,我们可以完全让root用户没有任何的权限和user一样
查看SELINUX权限
在android里面,有两个类型:
- 进程
- 文件
针对这两种类型,我们可以先来看看他们的不同。
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/sepolicy
。BOARD_SEPOLICY_DIRS
在系统编译时会被使用,非本文重点,有兴趣的话请自行查阅相关资料。
A. 上下文描述文件
可以在上下文的描述文件中为您的对象指定标签。
文件名 | 归类 |
---|---|
mac_permissions.xml | App进程 |
seapp_contexts | App数据文件 |
file_contexts | 系统文件 |
property_contexts | 系统属性 |
file_contexts
用于为文件分配标签,并且可供多种用户空间组件使用。在创建新政策时,请创建或更新该文件,以便为文件分配新标签。- genfs_contexts 用于为不支持扩展属性的文件系统(例如,
proc
或vfat
)分配标签。 - 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
规则名称 | 匹配是否允许 | 不匹配是否允许 | 匹配是否记录 | 不匹配是否记录 |
---|---|---|---|---|
allow | Yes | No | No | Yes |
dontaudit | No | No | No | No |
auditallow | No | No | Yes | Yes |
neverallow | - | - | - | - |
allow
:允许某个进程执行某个动作auditallow
:audit含义就是记录某项操作。默认SELinux只记录那些权限检查失败的操作。 auditallow则使得权限检查成功的操作也被记录。注意,allowaudit只是允许记录,它和赋予权限没关系。赋予权限必须且只能使用allow语句。dontaudit
:对那些权限检查失败的操作不做记录。neverallow
:没有被allow
到的动作默认就不允许执行的。neverallow
只是显式地写出某个动作不被允许,如果添加了该动作的allow,则会编译错误。
B2. source_type
指定一个“域”(domain
),一般用于描述进程
,该域内的的进程受该条TE语句的限制。用type
关键字,把一个自定义的域与原有的域相关联
type shell, domain;
上面这句话的意思是,赋予shell
给domain
属性,同时,shell
域属于domain
这个集合里。如果有一个allow domain xxxxx
的语句,同样地也给了shell xxxxx
的属性
B3. target_type
指定进程需要操作的客体(文件,文件夹等)类型,同样是用type
与一些已有的类型,属性相关联
以上面的ttyMT_device为例:
# 定义一个类型,属于dev_type属性
type ttyMT_device, dev_type;
属性dev_type
在external/sepolicyattributes
的定义如下
attribute dev_type;
attribute
关键字定义一个属性,type
可以与一个或多个属性关联,例如:
type usb_device, dev_type, mlstrustedobject;
另外,还有一个关键字typeattribute
,type有两个作用:
- 定义(声明)
- 关联某个属性。
可以把这两个作用分开,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 address
的ValueError
位置,未能继续跟进,所以在捕获到异常时又重新调用了一次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