赞
踩
SELINUX是可以理解为一种android上面的安全机制,是有美国国家安全局和一些公司设计的一个针对linux的安全加强系统,我们可以通过配置SELINUX的相关policy,来定制自己的手机的一些权限,比如,我们可以完全让root用户没有任何的权限和user一样
在android里面,有两个类型:
针对这两种类型,我们可以先来看看他们的不同。
在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
:一种安全等级另外就是文件,文件想要查看相关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
很多te文件集中在external\sepolicy
文件夹下,厂商也是有自己的配置规则在devic
目录下,例如RK项目中
BOARD_SEPOLICY_DIRS ?= device/rockchip/common/sepolicy
。BOARD_SEPOLICY_DIRS
在系统编译时会被使用,非本文重点,有兴趣的话请自行查阅相关资料。
可以在上下文的描述文件中为您的对象指定标签。
文件名 | 归类 |
---|---|
mac_permissions.xml | App进程 |
seapp_contexts | App数据文件 |
file_contexts | 系统文件 |
property_contexts | 系统属性 |
file_contexts
用于为文件分配标签,并且可供多种用户空间组件使用。在创建新政策时,请创建或更新该文件,以便为文件分配新标签。proc
或 vfat
)分配标签。seinfo
标记。随后,分配的 seinfo
标记可在 seapp_contexts
文件中用作密钥,以便为带有该 seinfo
标记的所有应用分配特定标签。以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
我们从上到下按顺序介绍一下:
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
}
最后是一些特殊的配置文件:
# 将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 -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
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。