1 Selinux 概述
1.1 概述
两种安全机制
DAC –> Discretionary Access Control
在DAC里,如果一个应用获取了一个用户权限,如root,那么他的所有的操作都是基于这个用户的权限
MAC –> Mandatory Access Control
无论你是谁,甚至是有Root用户权限,文件权限为777,但每个动作都是需要被允许之后可以被执行。这里可以是在安全策略文件中被允许,与可以是用户手动允许。
MAC基于LSM(Linux Security Module)标准实现的。
SELinux全称Security-Enhanced Linux。SELinux在MAC上实现的,所以SELinux也是基于LSM标准。在Linux kernel 2.6后正式直接整合进Linux里面
MAC的安全策略文件的作用就是表明了允许干什么。学名是TEAC(Type Enforcement Access Control)。简称TE。里面的语言被称为强制类型语言。
Security Context,安全上下文。Security Context的作用就是相当于这些文件和进程的“身份证”。
SELinux Mode
Permissve Mode(宽容模式)
只会打印acvl log,不会进行拦截
Enforcing Mode(强制模式)
真正的拦截并打印avc log
模式查看
getenforce
cat /sys/fs/selinux/enforce
1 –> Enforcing Mode
0 –> Permissve Mode
1.2 架构
- TE文件的规则和用户主动允许的动作等等会存储在图中的database里
- 当一个进程执行read动作(Event)被Security Server检测到的时候,DAC会作初步检查。然后把动作传进LSM,同时LSM会找到该进程和文件的安全上下文。
- SELinux在初始化的时候会实现一些由LSM提供的抽象函数(abstract)和把一些LSM回调(Hook)注册进LSM。LSM会读取SELinux里database的TE规则,或者在AVC(AccessVector cache)里寻找相应的规则。AVC相当于一个规则的缓存,加快读取的速度。找到相应的规则后又把它传回LSM,在LSM里做出最后的判断
从图中可以看到,SEAndroid安全机制包含有内核空间(Kernel Space)和用户空间(User Space)两部分支持,以SELinux文件系统(SELinux File system)接口为边界。在内核空间,主要涉及到一个SELinux LSM模块。而在用户空间中,涉到安全上下文(Security Context)、安全服务(Security Server)和安全策略(SEAndroid Policy)等模块。这些内核空间模块和用户空间模块的作用以及交互如下:
内核空间的SELinux LSM模块负责内核资源的安全访问控制。
用户空间的Security Context描述的是资源安全上下文。
从上文我们知道,SEAndroid的安全访问策略就是在资源(进程、文件等)的安全上下文基础上实现的
用户空间的SEAndroid Policy描述的是资源安全访问策略。
系统在启动的时候,用户空间的Security Server会将这些安全访问策略加载内核空间的SELinux LSM模块中去。这是通过SELinux文件系统接口实现的。
用户空间的Security Server一方面需要到用户空间的Security Context去检索对象的安全上下文,另一方面也需要到内核空间去操作对象的安全上下文。
用户空间的libselinux库封装了对SELinux文件系统接口的读写操作。
用户空间的Security Server访问内核空间的SELinux LSM模块时,都是间接地通过libselinux进行的。这样可以将对SELinux文件系统接口的读写操作封装成更有意义的函数调用。
用户空间的Security Server到用户空间的Security Context去检索对象的安全上下文时,同样也是通过libselinux库来进行的。
1.3 流程
从上层到驱动层的调用流程,但是我们重点关注sContext
file_contexts
系统中所有file_contexts安全上下文
seapp_contexts
app安全上下文
property_contexts
属性的安全上下文
service_contexts
service文件安全上下文
genfs_contexts
虚拟文件系统安全上下文
2 基础概念
2.1 主体(Subject)/客体(Object)
SELinux两个最基本的对象是主体(Subject)和客体(Object)。主体和客体分别对应的是“进程”和“文件”。这里的文件并不单指的是实际存在的文件,而是指Linux里“一切皆文件”里指的文件。如Socket,系统属性等
在SEAndroid,对主体和客体进行了进一步形式上的封装和扩展,其实差不多。SEAndroid里细分为:系统文件,服务,系统属性,Bindert和Socket。这里的系统属性指的是build.prop里的属性,也是getprop命令查询出来的属性。
2.2 安全上下文
全策略又是建立在对象的安全上下文的基础上的,安全上下文实际上就是一个附加在对象上的标签(label)。这个标签实际上就是一个字符串,它由四部分内容组成,分别是SELinux用户、SELinux 角色、类型、安全级别,每一个部分都通过一个冒号来分隔,格式为“user:role:type:rank”。
文件的安全上下文
ls -Z init.rc
-rwxr-x— root root u:object_r:rootfs:s0 init.rc
进程的安全上下文
ps -Z
LABEL USER PID PPID NAME
u:r:init:s0 root 1 0 /init*
2.2.1 用户和角色
对于进程来,SELinux用户和SELinux角色只是用来限制进程可以标注的类型。对于文件来说,SELinux用户和SELinux角色就可以完全忽略不计。
进程的安全上下文中,用户固定为 u,角色固定为 r。
文件的安全上下文中,用户固定为 u,角色固定为 obejct_r
2.2.2 安全级别
在SELinux中,安全级别是可选的,也就是说,可以选择启用或者不启用。通常在进程及文件的安全上下文中安全级别都设置为s0
2.2.3 类型
进程的安全上下文的类型称为domain,文件的安全上下文中的类型称为file_type。
上面我们看到进程init的安全上下文为u:r:init:s0,type为init,这也是合法的,为什么呢?在Android中我们可以通过如下语句定义type:
type init domain;
即将domain设置为init的属性,这样就可以用init为type来描述进程的安全上下文了。
2.3 安全上下文描述
安全上下文根据主体对象不同,有如下几种,在Android O上一般都存放在/system/sepolicy/private/目录下。
2.3.1 mac_permissions.xml
用于给不同签名的App分配不同的seinfo字符串,例如,在AOSP源码环境下编译并且使用平台签名的App获得的seinfo为“platform”。这个seinfo描述的是其实并不是安全上下文中的Type,它是用来在另外一个文件seapp_contexts中查找对应的Type的。
2.3.2 Seapp_contexts
用于声明APP进程和创建数据目录的安全上下文,O上将该文件拆分为plat和nonplat 前缀的两个文件,plat前缀的文件用于声明system app,nonplat前缀的文件用于声明vendor app。
从前面的分析可知,对于使用平台签名的App来说,它的seinfo为“platform”。这样我们就可以知道,使用平台签名的App所运行在的进程domain为“platform_app”,并且它的数据文件的file_type为“platform_app_data_file”。
2.3.3 File_contexts
用于声明文件的安全上下文,plat前缀的文件用于声明system、rootfs、data等与设备无关的文件。Nonplat 用于声明vendor、data/vendor 等文件。
如上,/system目录包括子目录和文件的安全上下文为u:object_r:system_file:s0,这意味着只有有权限访问Type为system_file的资源的进程才可以访问这些文件。
2.3.4 Service_contexts
用于声明java service 的安全上下文, O上将该文件拆分为plat和nonplat 前缀的两个文件,但nonplat前缀的文件并没有具体的内容(vendor和system java service不允许binder 操作)
如上,service为netd的安全上下文为u:object_r:netd_service:s0,这意味着只有有权限访问Type为netd_service的资源的进程才可以访问这些service。
2.3.5 Property_contexts
用于声明属性的安全上下文,plat 前缀的文件用于声明system属性,nonplat前缀的文件用于声明vendor 属性。
如上,ril.开头的属性的安全上下文为u:object_r:radio_prop:s0,这意味着只有有权限访问Type为radio_prop的资源的进程才可以访问这些属性。
2.3.6 Hwservice_contexts
O 上新增文件,用于声明HIDL service 安全上下文。
如上,android.frameworks.sensorservice::ISensorManager的hw service的安全上下文为u:object_r:fwk_sensor_hwservice:s0,这意味着只有有权限访问Type为fwk_sensor_hwservice的资源的进程才可以访问这些hw service。
3 策略文件
3.1 文件预览
1 | sepolicy/attributes |
3.2 基本定义
3.2.1 定义类型
1 | 定义type |
在TE中所有的东西都会被抽象成类型。进程抽象成类型、资源抽象成类型。属性是类型的集合。所以在TE规则中的最小单位就是类型
3.2.2 申明别名
1 | 使用typealias申明别名 |
3.2.3 声明属性
1 | 格式:attribute 属性名 |
3.2.4 类型与属性关联
类型与属性的关联有两种方式,一种是定义类型的时候进行关联
1 | 格式:type 类型名称 [alias 别名集] [,属性集] |
另一种是使用typeattribute进行类型与属性的关联
1 | 格式:typeattribute 类型名 [属性集] |
3.3 访问向量(AV)
访问向量(AV)规则用来描述主体对客体的访问许可。通常有类AV规则:
allow
表示允许主体对客体执行许可的操作。
neverallow
表示不允许主体对客体执行制定的操作。
auditallow
表示允许操作并记录访问决策信息。
dontaudit
表示不记录违反规则的决策信息,切违反规则不影响运行。
3.4 语法
格式:
1 | rule_name source_type target_type:class perm_s |
rule_name 就是上面说的allow 等
注意:
如果有多个source_type,target_type,class或perm_set,可以用”{}”括起来;
”~”
号,表示除了”~”以外;”-”
号,表示去除某项内容;“*”
号,表示所有内容
3.4.1 Demo1
1 | allow appdomain zygote_tmpfs:file read; |
允许appdomain域的进程对zygote_tmpfs类型的文件(file)执行read操作;
3.4.2 Demo2
1 | allow zygote appdomain:process { getpgid setpgid }; |
允许zygote域的进程对appdomain类型的进程执行getpgid和setpgid操作;
3.4.3 Demo3
1 | allow bluetooth { tun_device uhid_device hci_attach_dev }:chr_file { read write } |
允许bluetooth域的进程对tun_device, uhid_device, hci_attach_dev类型的字符文件(chr_file)执行读写操作;
3.4.4 Demo4
1 | allow init { |
表示允许init域的进程对shell_data_file,app_data_file类型的字符文件(chr_file),普通文件(file)执行除了entrypointexecute_no_trans execmod execute relabelto以外的操作;
3.4.5 Demo5
1 | allow init { file_type -system_file }:dir relabelto; |
”-”符号表示去除某项,即允许init域的进程对file_type类型中除了system_file类型外的目录执行relabelto操作
3.4.6 Demo6
1 | allow system_server self:netlink_selinux_socket *; |
”*”符号表示所有内容,即system_server域的进程能够对system_server类型执行所有netlink_selinux_socket类相关的操作;
3.4.7 Demo7
1 | neverallow { |
简单来说就是不允许除keystore域外的其它域对keystore_data_file类型的目录执行open,create等操作;
特别注意,前面提到权限必须显示声明,没有声明的话默认就没有权限。那么neverallow语句就没有存在的必要了,因为“无权限”是不需要声明的。确实如此,neverallow语句的作用只是在生成安全策略文件时进行检查,判断是否有违反neverallow语句的策略存在。
4 Type转换
4.1 概述
type类型转换分两种情况,一种是进程主体type转换,另一种是资源客体type转换。
DT –>Domain Transition
某个进行的Domain切换到一个合适的Domain中去
TT –>Type Transition
假设目录A的SContext为u:r:dir_a,那么默认情况下,在该目录下创建的文件的SContext就是u:r:dir_a,如果想让它的SContext发生变化,那么就需要TypeTransition
4.2 DT转换
语法格式:
1 | type_transition source_type target_type : class default_type |
表示source_type域的进程在对target_type类型的资源进行object class定义的许可操作时,进程会切换到default_type域中.
例如:
1 | type_transition init shell_exec:process init_shell |
当init域的进程执行(process)shell_exec类型的可执行文件时,进程会从init域切换到init_shell域.
但是上述策略要想被执行的话,至少还有三个前提:
首先,你得让init域的进程要能够执行shell_exec类型的文件
1
allow init shell_exec:file execute;
告诉SELinux,允许init域的进程切换到init_shell域
1
allow init init_shell:process transition;
告诉SELinux,域切换的入口(entrypoint)是执行shell_exec类型的文件
1
allow init_shell shell_exec:file entrypoint;
这样看来转换需要的步骤还挺多的,好在Android Selinux定义了一个宏,可以一次性做完上面的四步操作.
1 | domain_auto_trans(init, shell_exec, init_shell) |
4.3 TT转换
1 | file_type_trans(domain, dir_type, file_type) |
该宏的意思就是当domain域的进程在dir_type类型的目录创建文件时,该文件的SContext应该是file_type类型