不积跬步无以至千里,补全自己的短板,完善体系,站在巨人的肩膀上,看到的更远,写这篇文章也算是对这个知识点的总结。
一,背景
SElinux出现之前,Linux上的安全模型叫DAC(Discretionary Access Control 自主访问控制),它的核心思想就是:进程拥有的权限与执行它的用户权限相同,比如,以root用户启动camera, 那么camera就拥有root的用户权限,我们知道root的权限是很大的,可以说为所欲为。
所以DAC方式管理太过宽松,只要应用获得了root权限,可以在后台做很多我们不知道的事情,所以在4.4之前都建议不要root破解,我记得5.0之前还有很多应用提示“如果想要运行应用,需要root”,现在想想,不知道该应用做了哪些不可告人的事情。
后来,研究过AOSP的应该知道Android5.0算是一次大的跨度升级,在5.0上google启用了新的权限管理,增加了系统安全性,
SElinux是一种新的安全模型,称为MAC(Mandatory Access Control 强制访问控制),他的思想就是:任何进程想要在SElinux系统中干任何事, 都必须在安全策略配置文件中赋予权限,凡是没有出现在安全策略配置文件中的权限,进程没有权限操作任何事。
我们来看个例子
下面这条SELinux语句表示 允许(allow )hal_nfc_default域(domain)中的进程 ”创建“类型为nfc_vendor_data_file的文件
allow hal_nfc_default nfc_vendor_data_file:file { create rw_file_perms };
如果没有在hal_nfc.te中使用上例中的权限配置allow语句,则hal_nfc_default就无法在data目录创建文件,即使hal_nfc_default具有root权限
DAC和MAC对比总结:
1)Android 系统先做DAC检查, 如果没有通过DAC检查,操作直接失败,通过DAC后,再做MAC权限检查。
2)SELinux中也有用户的概念,但和Linux中原有的user概念不同, Linux中的超级用户root在SELinux中可能就是一个没权限,没地位打打酱油的“路人甲”。
写这篇文章的目的是跟着前辈的经验,能够看懂现有的策略文件,能够编写新添加的权限文件或语句。
二,SELinux Policy 语言介绍
Linux有种思想,即万事万物皆文件,普通文件是文件,目录是文件,设备是文件。
SELinux中,每种东西都会被赋予一个安全属性,官方说法叫Security Context。Security Context(以后用SContext表示)是一个字符串,主要由三部分组成,分为进程和文件两种。
SEAndroid中,进程的SContext可通过ps -ZA命令查看,
最左边的LABEL列值进程的SContext,以第一个u:r:init:s0为例,说明代表含义
- u为user的意思。SEAndroid中定义了一个SELinux用户,值为u。
- r为role的意思。role是角色之意,它是SELinux中一种比较高层次,更方便的权限管理思路,即Role Based Access Control(基于角色的访问控制,简称为RBAC)。简单点说,一个u可以属于多个role,不同的role具有不同的权限。
- init,代表该进程所属的Domain为init。MAC的基础管理思路其实不是针对上面的RBAC,而是所谓的Type Enforcement Accesc Control(简称TEAC,一般用TE表示)。对进程来说,Type就是Domain。比如init这个Domain有什么权限,都需要通过[例子1]中allow语句来说明。
- S0和SELinux为了满足军用和教育行业而设计的Multi-Level Security(MLS)机制有关。简单点说,MLS将系统的进程和文件进行了分级,不同级别的资源需要对应级别的进程才能访问。
文件的Security Context,可通过ls -lZ命令查看,
上图中倒数第二列为手机内根目录下文件和目录的SContext信息,同样以第一行u:object_r:vendor_file:s0为例,说明代表含义
- u:同样是user之意,它代表创建这个文件的SELinux user。
- object_r:文件是死的东西,它没法扮演角色,所以在SELinux中,死的东西都用object_r来表示它的role。
- vendor_file:死的东西的Type,和进程的Domain其实是一个意思,它表示odm目录对应的Type是vendor_file。
- s0:MLS的级别。
根据SELinux规范,完整的SContext字符串为:
user:role:type[:range]
注意,方括号中的内容表示可选项,s0属于range中的一部分。
三,TE介绍
MAC(Mandatory Access Control 强制访问控制)基本管理单位是TEAC(Type Enforcement Access Control), 然后是高一级别的Role Based Accesc Control。RBAC是基于TE的,而TE也是SELinux中最主要的部分。我们常添加的allow就是TE的范畴。
3.1 根据 SELinux 规范,完整的 SELinux 策略规则语句格式为:
使用政策规则时将遵循的结构示例:
- allow:TE的allow语句,表示授权。除了allow之外,还有allowaudit、dontaudit、neverallow等。
- hal_nfc_default:source type,也叫subject,domain。
- nfc_vendor_data_file:target type,代表其后的file所对应的Type.
- file:代表Object Class,它代表能够给subject操作的一类东西。例如File、Dir、socket等。在Android系统中,有一个其他Linux系统没有的Object Class,那就是Binder。
- create rw_file_perms:在该类Object Class中所定义的操作
下面根据几个实例了解权限的写法
#允许zygote域中的进程search或getattr类型为appdomain的目录。注意,多个perm_set
#可用{}括起来
allow zygote appdomain:dir { getattr search };
#来个复杂点的:
#source_type为unconfineddomain target_type为一组type,由
#{ fs_type dev_type file_type }构成。object_class也包含两个,为{ chr_file file }#perm_set语法比较奇特,前面有一个~号。它表示除了{entrypoint relabelto}之外,{chr_file #file}这两个object_class所拥有的其他操作
allow unconfineddomain {fs_type dev_type file_type}:{ chr_file file }
~{entrypoint relabelto};
#特殊符号除了~外,还有-号和*号,其中:
# 1):-号表示去除某项内容。
# 2):*号表示所有内容。
#下面这条语句中,source_type为属于appdomain,但不属于unconfinedomain的进程。
#而 *表示所有和capability2相关的权限
#neverallow:表示绝不允许。
neverallow { appdomain -unconfineddomain } self:capability2 *;
特别注意,前面曾提到说权限必须显示声明,没有声明的话默认就没有权限。那neverallow语句就没必要存在了。因为”无权限“是不需要声明的。确实如此,neverallow语句的作用只是在生成安全策略文件时进行检查,判断是否有违反neverallow语句的allow语句。
3.2 Object Class有哪些?
文件路径: system/sepolicy/private/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
3.3 Permissions
指的是某种class 所拥有的权限,以file这种object class而言,拥有的permission如下
查看路径:system/sepolicy/private/access_vectors
#
# Define common prefixes for access vectors
#
# common common_name { permission_name ... }
#
# Define a common prefix for file access vectors.
#common file
{
ioctl
read
write
create
getattr
setattr
lock
relabelfrom
relabelto
append
map
unlink
link
rename
execute
quotaon
mounton
audit_access
open
execmod
watch
watch_mount
watch_sb
watch_with_perm
watch_reads
}
从上面例子可以看出SELinux定义perm set方式使用common命令,实际使用中我们可以参考该文件的定义
#除了common外,还有一种class命令也可定义perm set,如下面的例子:
#class命令的完整格式是:
#class class_name [ inherits common_name ] { permission_name ... }
#inherits表示继承了某个common定义的权限 注意,class命令定义的权限其实针对得就是
#某个object class。它不能被其他class继承
class dir inherits file {
add_name remove_name reparent search rmdir open audit_access execmod
}
#来看SEAndroid中的binder和property_service这两个Object class定义了哪些操作权限
class binder {
impersonate call set_context_mgr transfer }
class property_service { set }
3.4 type
type命令的完整格式为:type type_id [alias alias_id,] [attribute_id]
其中,方括号中的内容为可选。alias指定了type的别名,可以指定多个别名。
四,SELinux应用
上面理论讲述了一堆,估计你已经有点不耐烦,下面我们以实际使用解决问题来说说,
SELinux分两种模式:enforceing mode(限制访问) 和permissive mode(只审查权限,不限制)
4.1怎么关闭这个权限限制呢?
4.1.1 临时关闭,当你的手机是debug模式时可以通过adb shell设置
setenfroce 0
setenforce 命令修改的是 /sys/fs/selinux/enforce 节点的值,0 表示permissive,1 表示 enforcing,是 kernel 意义上的修改 selinux 的策略。系统重启后,节点值会复位
4.1.2永久关闭
SECURITY_SELINUX 设置为 false,重新编译 kernel
make bootimage 重新编译boot,刷机验证
4.1.3修改代码
设置 ro.boot.selinux=permissive 属性,并且修改在 system/core/init/Android.mk 中设置用于 user 版本下 selinux 模式为 permissive
4.2 系统运行权限不够,avc异常处理
在系统开发过程中,经常遇到的一个问题就是没有权限操作某个动作,直观的就是功能未生效。出现此类问题我们可以通过adb logcat 抓取系统log,过滤log中avc子串,看看是否有报对应进程的权限问题。或者关掉权限看看功能是否正常。
案例1,我们抓到一份异常
可以看到INxpNfc没有find权限,分析如下:
谁缺少权限:scontext=u:r:hal_nfc_default:s0 ---->hal_nfc_default
对什么缺少权限:tcontext=u:object_r:default_android_hwservice:s0 ---> default_android_hwservice
什么类型的权限:tclass=hwservice_manager ---> hwservice_manager
缺少什么权限:denied{find} --->find
找到关于nfc的te文件按照套路添加定义
allow scontext_type tcontext_type:tclass_type { denied }
故 allow hal_nfc_default default_android_hwservice:hwservice_manager {find}
也可以使用audit2allow工具生成这个sepolicy规则,路径:AOSP/external/selinux/prebuilts/bin
注意:使用这个工具,需要先 souce build/envsetup.sh -->lunch 之后,audit2allow -i avc_log.txt 会在终端输出,如果想输出到指定文件,使用-p 后面跟路径名
BOARD_SEPOLICY_DIRS += device/mediatek/sepolicy 通过这个命令添加厂家自定义的 sepolicy 规则
新添加的这个权限有可能会引起neverallowed,出现问题可以重新定义相同类型的type,以规避权限检查
案例2
SELinux : avc: denied { find } for interface=android.hardware.secure_element::ISecureElement sid=u:r:nfc:s0 pid=21355 scontext=u:r:nfc:s0 tcontext=u:object_r:hal_secure_element_hwservice:s0 tclass=hwservice_manager permissive=0
添加权限allow nfc hal_secure_element_hwservice:hwservice_manager find;
会发现编译报错
libsepol.report_failure: neverallow on line 5 of system/sepolicy/public/hal_secure_element.te (or line 19894 of policy.conf) violated by allow nfc hal_secure_element_hwservice:hwservice_manager { find };
libsepol.check_assertions: 1 neverallow failures occurred
Error while expanding policy
解决方案添加hal_client_domain(nfc, hal_secure_element)
4.3 SELinux Android 源码架构
- externel/selinux:包含编译 sepolicy 策略文件的一些实用构建工具
- external/selinux/libselinux:提供了帮助用户进程使用 SELinux 的一些函数
- external/selinux/libsepol:提供了供安全策略文件编译时使用的一个工具 checkcon
- system/sepolicy:包含 Android SELinux 核心安全策略(te 文件),编译生成 sepolicy 文件
- file_contexts: 系统中所有文件的安全上下文
- property_contexts: 系统中所有属性的安全上下文
- seapp_contexts:定义用户、seinfo和域之间的关系,用于确定用户进程的安全上下文
- sepolicy:二进制文件,保存系统安全策略,系统初始化时会把它设置到内核中SELinux 虚拟文件系统在 sys/fs/selinux 下,该目录下的文件是 SELinux 内核和用户进程进行通信的接口,libselinux 就是利用这边的接口进行操作
4.3.1 init进程SEAndroid启动源码分析
可以看到 SEAndroid 的启动设置在 init 进程内核态执行(first_stage)过程中,selinux_initialize 函数就是主要的初始化过程,包含加载 sepolicy 策略文件到内核的 LSM 模块中。
selinux_set_callback
selinux_set_callback 用来向 libselinux 设置 SEAndroid 日志和审计回调函数
selinux_load_policy
这个函数用来加载 sepolicy 策略文件,并通过 mmap 映射的方式将 sepolicy 的安全策略加载到 SELinux LSM 模块中去。
下面具体分析一下流程:
这里区分了从哪里加载安全策略文件,第一个是从 /vendor/etc/selinux/precompiled_sepolicy 读取,第二个是直接从根目录 /sepolicy
中读取,最终调用的方法都是 selinux_android_load_policy_from_fd
(1)set_selinuxmnt 函数设置内核 SELinux 文件系统路径,这里的值为 /sys/fs/selinux,SELinux 文件系统用来与内核空间 SELinux LSM 模块空间。
(2)通过 fstat 获取 sepolicy 文件(fd 值)的状态信息,通过 mmap 函数将文件内容映射到内存中,起始地址为 map
(3)security_load_policy 调用另一个 security_load_policy 函数将已经映射到内存中的 SEAndroid 的安全策略加载到内核空间的 SELinux LSM 模块中去。
函数 security_load_policy 的实现很简单,它首先打开 /sys/fs/selinux/load 文件,然后将参数 data 所描述的安全策略写入到这个文件中去。由于 /sys/fs/selinux 是由内核空间的 SELinux LSM 模块导出来的文件系统接口,因此当我们将安全策略写入到位于该文件系统中的 load 文件时,就相当于是将安全策略从用户空间加载到 SELinux LSM 模块中去了。
以后 SELinux LSM 模块中的 Security Server 就可以通过它来进行安全检查
(4)加载完成,释放 sepolicy 文件占用的内存,并且关闭 sepolicy 文件
selinux_init_all_handles
在 init 进程的用户态启动过程中会调用这个函数初始化 file_context、 property_context 相关内容 handler,根据前面的描述,init 进程给一些文件或者系统属性进行安全上下文检查时会使用 libselinux 的 API,查询文件根目录下 file_context、file_context 的安全上下文内容。所以需要先得到这些文件的 handler,以便可以用来查询系统文件和系统属性的安全上下文。
4.3.2 SEAndroid 编译
audit2allow -i avc_log.txt 即可自动输出生成的policy
source build/envsetup.sh
lunch
mma system/sepolicy/
adb push out/target/product/xx/vendor/etc/selinux/ vendor/etc/selinux/
adb push out/target/product/xx/system/etc/selinux/ system/etc/selinux/
参考:
深入理解SELinux/SEAndroid(第一部分) - 邓凡平的个人页面 - OSCHINA - 中文开源技术交流社区
android 8.1 安全机制 — SEAndroid & SELinux_岁月斑驳7的博客-CSDN博客_android selinux