1.什么是SeLinux
安全增强型 Linux(Security-EnhancedLinux)简称 SELinux,它是一个 Linux 内核模块,也是 Linux 的一个安全子系统。
SELinux 主要由美国国家安全局开发。2.6 及以上版本的 Linux 内核都已经集成了 SELinux 模块。
In Android 4.4 → Android 7.0, SELinux 策略文件(sepolicy、file_contexts.bin、property_contexts 等)包含在 rootfs img中:
SELinux for Android 7.x.
Figure 2 . SELinux build logic.
sepolicy 文件由多个源文件组成:
Android 8.0 更新了 SELinux 以便与 Treble 配合使用,后者可将较低级别的供应商代码与 Android 系统框架分离开来。此版本更新了 SELinux 政策以允许设备制造商和 SOC 供应商更新自己的政策部分、构建自己的映像(vendor.img、boot.img 等),然后更新这些映像而不受平台影响,反之亦然。
2.为什么要使用SeLinux
2.1 DAC
DAC(Discretionary Access Control)自主式权限控制
参与的对象有3种:主体(subject)、客体(object)、规则(policy)。
权限判定过程大概如下:
1、主体拥有自己的凭证来标识自己的身份。在DAC中,主体通常是进程,而凭证是进程对应用的euid和egid。
2、客体拥有属性来标识自己的身份。在DAC中,客体通常是文件,而权限相关属性是文件对应的uid和gid。
3、主体对客体的操作可以称之为行为。DAC的特点就是行为比较简单,行为仅包括R(读)、W(写)、X(执行)这三种。
4、针对"主体对客体发起的行为",查询规则表来进行权限判定。DAC的UGO规则非常简单,把主体分为User、Group、Other三种类型,每种类型拥有自己的RWX mask。
DAC的权限控制策略是非常简洁,行为是简单的RWX三种,主体也很简单的就能被分为UGO三类。这种简洁造也就了DAC检查的开销非常小。
但是过于简洁也让它的权限划分粒度过大,一旦获得了root权限,几乎就是无所不能。在CPU日益高涨的今天,性能开销已经不是问题了,权限的细粒度管理更加重要,所以诞生了MAC。MAC在DAC的基础上,把行为、规则、判定结果进一步细分。所以它的权限管理粒度更细,但是开销也稍大。
DAC是Linux权限管理的基础机制。
2.2 MAC
DAC 管理太过宽松,只要想办法在 Android 系统上获取到 root 权限就可以了。那么 SELinux 是怎么解决这个问题呢?在 DAC 之外,它设计了一种新的安全模型,叫 MAC(Mandatory Access Control),翻译为强制访问控制。
MAC 的理论也很简单,任何进程想在 SELinux 系统上干任何事情,都必须在《安全策略文件》中赋予权限,凡是没有出现在安全策略文件中的权限,就不行。
2.3 DAC和MAC有什么区别?
DAC
DAC(Discretionary Access Control,自主访问控制)
DAC是传统的Linux的访问控制方式,DAC可以对文件、文件夹、共享资源等进行访问控制。
在DAC这种模型中,文件客体的所有者(或者管理员)负责管理访问控制。
DAC使用了ACL(Access Control List,访问控制列表)来给非管理者用户提供不同的权限,而root用户对文件系统有完全自由的控制权。
MAC
MAC(Mandatory Access Control,强制访问控制)
SELinux在内核中使用MAC检查操作是否允许。
在MAC这种模型中,系统管理员管理负责访问控制,用户不能直接改变强制访问控制属性。
MAC可以定义所有的进程(称为主体)对系统的其他部分(文件、设备、socket、端口和其它进程等,称为客体)进行操作的权限或许可。
DAC和MAC的其它区别
① DAC的主体是真实有效的用户和组ID,MAC的主体是安全上下文,两者的UID是各自独立的。
② DAC的访问控制模式是rwxrwxrwx,MAC的访问控制模式是user:role:type。
3.如何使用SeLinux
首先要对 Type、Class、Permissions和 安全上下文有一定的了解。
3.1 Type、Class和Permissions
MAC 基本管理单位是 TEAC(Type Enforcement Accesc Control),然后是高一级别的 Role Based Accesc Control。RBAC 是基于 TE 的,而 TE 也是 SELinux 中最主要的部分。 allow 语句就是 TE 的范畴。
根据 SELinux 规范,完整的 SELinux 策略规则语句格式为:
allow domains types:classes permissions;
Domain - 一个进程或一组进程的标签。也称为域类型,因为它只是指进程的类型。
Type - 一个对象(例如,文件、套接字)或一组对象的标签。
Class - 要访问的对象(例如,文件、套接字)的类型。
Permission - 要执行的操作(例如,读取、写入)。
= allow : 允许主体对客体进行操作
= neverallow :拒绝主体对客体进行操作
= dontaudit : 表示不记录某条违反规则的决策信息
= auditallow :记录某项决策信息,通常 SElinux 只记录失败的信息,应用这条规则后会记录成功的决策信息。
语句:
allow appdomain app_data_file:file rw_file_perms;
这表示所有应用域都可以读取和写入带有 app_data_file 标签的文件
下面来看看Android官方文档中的定义。
Android 依靠 SELinux 的类型强制执行 (TE) 组件来实施其政策。这表示所有对象(例如文件、进程或套接字)都具有相关联的类型。例如,默认情况下,应用的类型为 untrusted_app。对于进程而言,其类型也称为域。可以使用一个或多个属性为类型添加注解。属性可用于同时指代多种类型。
对象会映射到类(例如文件、目录、符号链接、套接字),并且每个类的不同访问权限类型由权限表示。例如,file 类存在权限 open。虽然类型和属性作为 Android SELinux 政策的一部分会进行定期更新,但权限和类是静态定义的,并且作为新 Linux 版本的一部分也很少进行更新。
政策规则采用以下格式:
allow source target:class permissions;
Source - 规则主题的类型(或属性)。谁正在请求访问权限?
Target - 对象的类型(或属性)。对哪些内容提出了访问权限请求?
Class - 要访问的对象(例如,文件、套接字)的类型。
Permissions - 要执行的操作(或一组操作,例如读取、写入)。
规则的一个示例如下:
allow untrusted_app app_data_file:file{ read write };
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.2 安全上下文(Security Context)
安全上下文的结构及含义
安全上下文有四个字段,分别用冒号隔开。形如:system_u:object_r:admin_home_t:s0
。
常见的安全上下文如下。
file_contexts //系统中所有file_contexts安全上下文
seapp_contexts //app安全上下文
property_contexts //属性的安全上下文
service_contexts //service文件安全上下文
genfs_contexts //虚拟文件系统安全上下文
以上文件system/sepolicy中都有对应的内容
3.3 简单使用
下面以新增一个build.prop为例说明。
当在.mk文件中,新增一个prop定义的时候,例如 PRODUCT_PROPERTY_OVERRIDES += vendor.xxx.sys.test=1
会发生无法读写vendor.xxx.sys.test
这个属性的问题, 因为存在SeLinux相关的问题。
这里以set prop权限为例去说明。
3.3.1 定义type
在property.te文件中新增
type mtk_xxx_sys_prop, property_type,extended_core_property_type;
声明一个mtk_xxx_sys_prop
的type,也可以说是标签。
Type对应一个或者几个attribute,Type的定义格式:
type type_name, attribute1, attribute2;
3.3.2 定义vendor.xxx.sys.test 的安全上下文
定义其type为mtk_xxx_sys_prop。
在property_contexts
文件中新增
vendor.xxx.sys.test u:object_r:mtk_xxx_sys_prop:s0
3.3.3 定义allow规则
允许system_app
和system_server
进程
在system_app.te
文件中新增
set_prop(system_app, mtk_xxx_sys_prop)
在system_server.te
文件中新增
set_prop(system_server,mtk_xxx_sys_prop)
下面来看看set_prop函数。
alps/system/sepolicy/public/te_macros
305 #####################################
306 # set_prop(sourcedomain, targetproperty)
307 # Allows source domain to set the
308 # targetproperty.
309 #
310 define(set_prop',
311 unix_socket_connect($1, property, init)
312 allow $1 $2:property_service set;
所以根据这意思,完全可以拼凑出如下权限声明:
allow system_app mtk_xxx_sys_prop:property_service set;
允许 域为system_app
的进程,对 标签 为mtk_xxx_sys_prop
(其class属于property_service),执行set
操作。
而vendor.xxx.sys.test
的安全上下文中 声明了
type(标签)为mtk_xxx_sys_prop
,
因此,type(域)为 system_app 的进程 可以对vendor.xxx.sys.test 执行set操作。system_server同理。
3.4 SeLinux文件的路径
关于SeLinux文件的路径,对于不同的平台,不同平台厂商也设定了不同的存放目录,以MTK平台为例:
首先,根据不同的platform共用sepolicy、platform独有、project独有,分为:
/device/mediatek/sepolicy :下面是ALL platform 都需要.
/device/mediatek/[platform]/sepolicy :其中platform为某个具体的平台,如mt6763.
/device/[customer]/[project]/sepolicy :客户和项目自定义配置, 客户可根据更新BroadConfig.mk 中BOARD_SEPOLICY_DIRS 以新增目录。
对应的,不同版本会导入不同目录下的sepolicy配置
Basic 版本 导入 [common]|[platfrom]/basic
Bsp 版本 导入 [common]|[platfrom]/basic & bsp
TK 版本(一般情况) 导入 [common]|[platfrom]/basic & bsp & full
以mt6763平台为例,导入时:
[common] 路径为:/device/mediatek/sepolicy
[platfrom] 路径为:/device/mediatek/mt6763/sepolicy/
具体的定义在BoardConfig.mk文件中。
3.5 根据log添加规则
例如报错
avc: denied { read } for pid=1494 comm=“sdcard” name=“0” dev=“nandk” ino=245281
scontext=u:r:sdcardd:s0
tcontext=u:object_r:system_data_file:s0
tclass=dir permissive=0
==============
scontext = u:r:sdcardd:s0
tcontex t= u:object_r:system_data_file:s0
tclass = dir
avc: denied { read }
可以看到有avc denied,且最后有permissive=0,表示不允许。我们的log重新排列一下
缺少什么权限: { read }权限,
谁缺少权限: scontext= u:r:sdcardd:s0
对哪个文件缺少权限:tcontext=u:object_r:system_data_file:s0
什么类型的文件: tclass=dir
某个scontext对某个tclass类型的tcontext缺乏某个权限
完整的意思: Type(域)为sdcardd的进程对type为system_data_file的dir(class)缺少read权限。
就是需要这样一条规则。
# file: sdcardd.te
allow sdcardd system_data_file:dir read;
标志性 log: avc: denied { 操作权限 } for pid=7201 comm=“进程名” scontext=u:r:源类型:s0 tcontext=u:r:目标类型:s0 tclass=访问类型 permissive=0
源类型:授予访问的类型,通常是进程的域类型
目标类型:客体的类型,它被授权可以访问的类型
访问类型
操作权限:表示主体对客体访问时允许的操作类型(也叫做访问向量)。
例如添加一个service,不加权限的话会报错。
E SELinux : avc: denied { add } for service=car_model_service pid=10283 uid=1000
scontext=u:r:system_app:s0
tcontext=u:object_r:default_android_service:s0
tclass=service_manager permissive=0
#============= system_app.te ==============
allow system_app default_android_service:service_manager add;
但是,有的时候 ,这样直接添加是违反neverallow 规则,编译是无法通过的。
#============= domain.te =============
neverallow * default_android_service:service_manager add;
这个策略的意思是,不允许 任何 进程将 Type (域)为default_android_service的service 对service_manager 做 add操作(service_manager.c中的add service 函数中会根据selinux策略来检查)。
这种时候就需要绕开neverallow规则了。给这个service 单独定义一个Type,安全上下文,然后再添加allow规则。
假设service名字定义为car_model_service(add到ServiceManager中的那个字符串"car_model_service"),接下来分3步走。
3.5.1 定义Type
# ============= service.te =============
type my_car_model_service, service_manager_type;
3.5.2 定义安全上下文
# ============= service_contexts =============
car_model_service u:object_r:my_car_model_service:s0
3.5.3 定义allow规则
# ============= system_app.te =============
add_service(system_app, my_car_model_service)
编译后等于
allow system_app my_car_model_service:service_manager { add find };
要始终记得 最小权限原则。
4.调试用到的一些命令
4.1 显示SeLinux上下文
ls -Z 查看文件信息,包括SeLinux上下文,名称。
ps -Z 查看进程信息,包括SeLinux上下文,PID等
ps显示不全,ps -A 显示所有
ps -AZ
ps -AZ | grep "xxx" 根据grep查看某个具体的进程信息。
ls -al -Z
4.2 设置SELinux工作模式
SELinux有3种工作模式
Enforcing:强制模式。违反SELinux规则的行为将被阻止并记录到日志中。
Permissive:宽容模式。违反SELinux 规则的行为只会记录到日志中。一般为调试用。
Disabled:关闭SELinux。
查看当前工作模式:getenforce
修改工作模式:setenforce
setenforce 0(permissive) ,setenforce 1(enforcing)
注意:需在设备启动后迅速输入这个命令,刷完机后连上电脑就要一直输入adb shell,shell输入成功后,要马上setenforce 0,否则就会设置失败。可用getenforce查看设置结果。
4.3 查看log,问题定位
adb shell "cat /proc/kmsg | grep avc" > avc_log.txt 可以直接提出avc的log
cat /dev/kmsg | grep avc //查看kernel中的SELinux权限报错
dmesg | grep avc //查看kernel中的SELinux权限报错
logcat | grep -i -e avc -e selinux
dmesg -c |grep avc
dmesg -C,清空kernel log
编译过程中,偶尔会出现编译错误。类似这种
device/qcom/sepolicy/msm8952/system_app.te:3 'syntax error' at token 'allow' on line 33320
由于在编译过程中SELinux报的错误有时并不是实际问题出现的位置,因此导致有时SELinux语法错误很难定位。
事实上,在编译过程中,是通过如下命令对SELinux进行检查的:
/bin/bash -c "(out/host/linux-x86/bin/checkpolicy -M -c 30 -o out/target/product/msm8952/obj/ETC/sepolicy_intermediates/sepolicy.tmp out/target/product/msm8952/obj/ETC/sepolicy_intermediates/policy.conf ) && (out/host/linux-x86/bin/checkpolicy -M -c 30 -o out/target/product/msm8952/obj/ETC/sepolicy_intermediates/sepolicy.dontaudit out/target/product/msm8952/obj/ETC/sepolicy_intermediates/policy.conf.dontaudit ) && (out/host/linux-x86/bin/sepolicy-analyze out/target/product/msm8952/obj/ETC/sepolicy_intermediates/sepolicy.tmp permissive > out/target/product/msm8952/obj/ETC/sepolicy_intermediates/sepolicy.permissivedomains ) && (if [ \"eng\" = \"user\" -a -s out/target/product/msm8952/obj/ETC/sepolicy_intermediates/sepolicy.permissivedomains ]; then echo \"==========\" 1>&2; echo \"ERROR: permissive domains not allowed in user builds\" 1>&2; echo \"List of invalid domains:\" 1>&2; cat out/target/product/msm8952/obj/ETC/sepolicy_intermediates/sepolicy.permissivedomains 1>&2; exit 1; fi ) && (mv out/target/product/msm8952/obj/ETC/sepolicy_intermediates/sepolicy.tmp out/target/product/msm8952/obj/ETC/sepolicy_intermediates/sepolicy )"
从上述命令可以看出checkpolicy命令是对out/target/product/msm8952/obj/ETC/sepolicy_intermediates/policy.conf这个文件进行语法检查的,打开policy.conf可以看到所有的SELinux配置最终都汇总到policy.conf中,因此为了解决“syntax error"问题,我们只需要在policy.conf搜索编译过程中报的出错语句(如device/qcom/sepolicy/msm8952/system_app.te:3),并在policy.conf中检查其周围的语句即可定位是哪一个SELinux配置语句导致的syntax error。
4.4 快速验证
mmm system/sepolicy
push system/etc/selinux 和 vendor/etc/selinux 到手机里面, 并重启手机.
adb push $(PRODUCT_OUT)/system/etc/selinux /system/etc/
adb push $(PRODUCT_OUT)/vendor/etc/selinux /vendor/etc/
adb reboot
5.sys目录下的权限
sys下的权限由于Android P收紧的对sysfs 的权限,导致直接配置对sysfs的SElinux权限,会触发编译时的neverallow规则。
cts版本不能有任何neverallow,只能去掉添加的权限。单独配置type。
国内版本可适当注释掉原生相关neverallow进行规避。
我遇到过一个复现率很高的sys目录下节点type配置失败的情况。
具体现象是开机后,一定几率,设置type失败。
就像这种,虽然将节点都设置了sysfs_egbin这个type,但是开机后,sda的type却是sysfs。
drwxr-xr-x 4 root root u:object_r:sysfs:s0 0 1970-01-29 19:16 .
drwxr-xr-x 11 root root u:object_r:sysfs:s0 0 1970-01-29 19:16 ..
drwxr-xr-x 2 root root u:object_r:sysfs_egbin:s0 0 1970-01-29 19:16 features
drwxr-xr-x 2 root root u:object_r:sysfs:s0 0 2018-10-30 18:16 sda
但是,我即使添加了
restorecon --recursive /sys/class/xxx
还是无法解决这个问题。
在系统启动过程中,当执行到restorecon这句的时候,我需要访问的/sys/class/xxx节点,有一定几率还是不存在,没有创建完成,因此在后面解决办法是不使用/sys/class/xxx节点,使用/proc/driver/xxx节点去设置type。这个节点挂载时间更早一点。
参考链接:
官方文档:
https://source.android.google.cn/static/security/selinux/images/SELinux_Treble.pdf
https://source.android.google.cn/security/selinux/concepts
https://opensource.com/business/13/11/selinux-policy-guide
https://deepinout.com/seandroid/seandroid-policy-dac-and-mac.html
android 8.1 安全机制 — SEAndroid & SELinux
SELinux_Treble学习记录
Android Q system_app默认写persist.sys.系统属性SEliux权限来源
SELinux权限
android P 修改sysfs的 u:object_r:失效问题分析
SELinux syntax error问题定位
Android/SELinux 添加 AVC 权限
Android P SElinux权限调试
SEAndroid学习笔记
Android 9 SELinux
Linux DAC 权限管理详解