1 概述
安全增强型 Linux (SELinux) 是适用于 Linux 操作系统的强制访问控制 (MAC) 系统。作为 MAC 系
统,它与 Linux 中用户非常熟悉的自主访问控制 (DAC) 系统不同。在 DAC 系统中,存在所有权的概
念,即特定资源的所有者可以控制与该资源关联的访问权限。这种系统通常比较粗放,并且容易出现无
意中提权的问题。MAC 系统则会在每次收到访问请求时都先咨询核心机构,再做出决定。 Linux系统会
先检查DAC权限也就是linux中的uid/gid检查,如果权限不够直接拒绝,通过则再根据具体调用检查
MAC配置策略中的权限,也就是Sepolicy策略文件。
在SEAndroid中主要包括Security context,Security server,libselinux,LSM四个模块。大概如图:
对于AOSP开发来说核心的东西基本都在Security context,如何配置te文件生成正确的Security
context文件是这份文档主要讲的,对其他模块Security server,libselinux,LSM等感兴趣的可以自己
去开源码或者网上查资料。
2 Sepolicy
SELinux 依靠标签来匹配操作和政策。标签用于决定允许的事项。套接字、文件和进程在 SELinux
中都有标签。SELinux 在做决定时需参照两点:一是为这些对象分配的标签,二是定义这些对象如何交
互的政策。
在 SELinux 中,标签采用以下形式: user:role:type:mls_level
,其中 type
是访问决定的主
要组成部分,可通过构成标签的其他组成部分进行修改。对象会映射到类,对每个类的不同访问类型由
权限表示。SEAndroid的政策文件主要放在sepolicy中,Linux中只活的东西(进程之类的)和死的东西(文件
之类),因此Sepolicy配置分为对主体的配置和对客体的配置。
- 主体配置 - 后缀名为“.te”的文件,主要是针对进程的配置。
- 客体配置 - 以_contexts结尾的文件基本都是对文件,property等配置。
2.1标签
Android中主要的标签形式:
user:role:type:mls_level
,主体和客体的区别主要也体现在标
签上。
主体标签:u:r:type:mls_level
例如:
u:r:init:s0
//init 进程的selinux标签
u - user Sepolicy中定义的user u
r - role 角色,Sepolicy中定义的角色,主体主要是用r
init - domain, Sepolicy中为init进程定义的domain,也就是type。
s0 - MLS(Multi-Level Security)安全级别,在linux中范围s0 - mls_systemhigh,s0是最低
级别,其他级别AOSP中基本用不到,在美国军方,教育,政府等安全要求高的项目中会用到。
客体标签: u:object_r:type:mls_level
例如:
u:object_r:proc:s0
//这个是系统proc目录的selinux标签
object_r - role,客体的角色主要用object_r,死的东西。
2.2 主体配置
政策规则采用以下形式: allow *domains* *types*:*classes* *permissions*;
,其中:
- Domain - 一个进程或一组进程的标签。也称为域类型,因为它只是指进程的类型。
- Type - 一个对象(例如,文件、套接字)或一组对象的标签。
- Class - 要访问的对象(例如,文件、套接字)的类型。
- Permission - 要执行的操作(例如,读取、写入)。
使用政策规则时将遵循的结构示例:
allow appdomain app_data_file:file rw_file_perms;
上面的配置表示允许appdomain类型进程对app_data_file类型下面的file类具有读写权限。
2.3 客体配置
客体的配置主要是采用以下形式: object
user:role:type:mls_level
。您可以在上下
文的描述文件中为您的对象指定标签。
file_contexts
用于为文件分配标签,并且可供多种用户空间组件使用。在创建新政策时,请创
建或更新该文件,以便为文件分配新标签。要应用新的file_contexts
,请重新构建文件系统映
像,或对要重新添加标签的文件运行restorecon
。在升级时,对file_contexts
所做的更改
会在升级过程中自动应用于系统和用户数据分区。此外,您还可以通过以下方式使这些更改在升级
过程中自动应用于其他分区:在以允许读写的方式装载相应分区后,将restorecon_recursive
调用添加到init.board.rc
文件中。genfs_contexts
用于为不支持扩展属性的文件系统(例如,proc
或vfat
)分配标签。此配
置会作为内核政策的一部分进行加载,但更改可能对内核inode
无效。要全面应用更改,您需要
重新启动设备,或卸载并重新装载文件系统。此外,通过使用context=mount
选项,您还可以
为装载的特定系统文件(例如vfat
)分配特定标签。property_contexts
用于为 Android 系统属性分配标签,以便控制哪些进程可以设置这些属
性。在启动期间,init
进程会读取此配置。service_contexts
用于为 Android Binder 服务分配标签,以便控制哪些进程可以为相应服务
添加(注册)和查找(查询)Binder 引用。在启动期间, servicemanager 进程会读取此配置。seapp_contexts
用于为应用进程和 /data/data 目录分配标签。在每次应用启动时,zygote
进程都会读取此配置;在启动期间,installd
会读取此配置。mac_permissions.xml
用于根据应用签名和应用软件包名称(后者可选)为应用分配seinfo
标记。随后,分配的 seinfo 标记可在seapp_contexts
文件中用作密钥,以便为带有该seinfo
标记的所有应用分配特定标签。在启动期间,system_server
会读取此配置。
使用政策规则时将遵循的结构示例:
/dev(/.*)?
u:object_r:device:s0 //dev设备配置
persist.sys.safemode
u:object_r:safemode_prop:s0 //property配置
genfscon proc /gsboard/dev_info/dev_mac
u:object_r:proc_devinfo_mac:s0 //proc下的文件配置
genfscon sysfs /devices/platform/fe300000.ethernet/net/eth0/carrier
u:object_r:sysfs_gseth:s0 //sys上下文配置
seapp_contexts
比较特殊,既包含主体配置又包含客体配置:
user=_app seinfo=platform name=com.android.traceur domain=traceur_app
type=app_data_file levelFrom=all
user= : _app表示普通uid的App,可以使system,bluetooth,radio等
seinfo : platform在mac_permissions.xml中配置代表参与平台编译的App
name : 进程名称
domain : 进程的域名,在ps -eZ可以看到,主体配置
type : 表示/data/data/下面的文件domain,ls -lZ可以看到,客体的配置。
init
服务的配置既可以直接在init.rc
文件中直接以selabel
声明,也可以在Sepolicy中配置,下面示例中会讲到。
2.4 GS配置示例
这里以dbus-daemon的配置为例,dbus-daemon是init中注册的服务,会被init服务管理。
首先定义dbus域名:
device/rockchip/rk3399/sepolicy-gs/public/dbus.te
type dbus, domain, mlstrustedsubject; //定义一个type dbus,作为一个域名,mls检查安全的进程。
//File types must be defined for file_contexts.
type dbus_exec, exec_type, file_type; //定义为dbus可执行文件定义类型
分配权限:
device/rockchip/rk3399/sepolicy-gs/private/dbus.te
typeattribute dbus coredomain; //把dbus域名关联到coredomain,也可以直接写在定义的地
方。
init_daemon_domain(dbus) //加入到init域名中管理,包含域名转换,自动从init域转到dbus域。
宏的使用,可以查看system/sepolicy/public/te_macros中的init_daemon_domain定义。
# Add dbus to various domains
net_domain(dbus) //把dbus关联到netdomain域名,可以使用socket等基础网络操作。
allow dbus dbus_socket:sock_file write; //分配allow权限,dbus对dbus_socket域下的
socket文件有写数据权限。
定义相关的域
dbus_socket:device/rockchip/rk3399/sepolicy-gs/public/file.te
# DBUS socket type
type dbus_socket, file_type, coredomain_socket;
给dbus二进制文件和相关socket文件定义上下文:
device/rockchip/rk3399/sepolicy-gs/private/file_contexts
## gs-dbus-daemon
/dev/socket/dbus u:object_r:dbus_socket:s0
/system/bin/gs-dbus-daemon u:object_r:dbus_exec:s0
到这里一个dbus守护进程的相关主体和客体配置就完了,接下去验证,编译烧写验证。
2.5 编译验证
Android中selinux的编译主要是将Sepolicy下的各个政策文件经过libselinux解析编译到对应的目标
bin文件。烧写到板子上验证要经过下面几步:
-
编译sepolicy,在system/sepolicy/中有Android.bp或者早期的Android.mk,在这里会对开发者配
置的政策文件做neverallow检查和mls检查,政策不允许的会编译失败。 -
编译bootimg - 由于selinux相关的bin文件存在Ram-disk中需要编译bootimg,在Android 9 设备
默认使用system as root编译,bootimg跟systemimg合到一起了,由于使用ninja编译的系统只
要整编过一次后,再次整编也是比较快的,因此从Android 8开始可以整编验证,更早的编译
bootimg就可以"make bootimg"。在RK Android 9平台用根目录的脚本编译就行"./build.sh -A -
u",编译出update.img就行。 -
烧写bootimg - 这个在不同平台差别比较大,按各自平台的烧写文档来。RK Android 9 中直接用
平台提供的工具烧写update.img就行。
烧写成功后把系统selinux模式设置成宽容模式 permissive: setenforce permissive
,以前的版
本是设置: setenforce 0
,或者修改系统编译参数:
1. 在内核中启用 SELinux: CONFIG_SECURITY_SELINUX=y
2. 更改 kernel_cmdline 参数,BOARD_KERNEL_CMDLINE :=
androidboot.selinux=permissive
在Android中selinux主要有两种模式:
宽容模式 - Permissive,仅记录但不强制执行 SELinux 安全政策。
强制模式 - Enforcing,强制执行并记录安全政策。如果失败,则显示为 EPERM 错误。
查看当前模式: getenforce
命令,返回0或者permissive就是宽容模式,系统会做MAC检查但是不
会拒绝或者产生异常,当然init中注册的服务除外,init中的服务必须配置selinux标签才能被运行起来。
查看之前配置的dbus守护进程配置是否生效:
- 查看文件:
console:/ # ls -lZ /system/bin/gs-dbus*
-rwxr-xr-x 1 root shell u:object_r:dbus_exec:s0 202808 2009-01-01 00:00
/system/bin/gs-dbus-daemon
console:/ # ls -lZ dev/socket/dbus
srwxrwxrwx 1 root root u:object_r:socket_device:s0 0 2013-01-18 16:52
dev/socket/dbus
这里dbus守护进程二进制文件生效了,但是socket文件配置没有生效,不是dbus_socket
类型,
还需要做特别的修改,下面再讲。
2.查看dbus守护进程:
console:/ # ps -eZ|grep dbus
u:r:dbus:s0
root
467
1
12276
4408
poll_schedule_timeout 77796b37c0 S gs-dbus-daemon
dbus进程的域名是dbus,说明生效了。
到这里dbus相关就算配置完成了吗,显然还不够,太小看selinux
了,就算前面的socket文件配置对
了,也还不够,selinux
是一个复杂的白名单关系网,任何与dbus有关系的都进程或者文件都需要在白
名单配出来。这里我们过滤下系统打印出来的denied消息:
console:/ # dmesg | grep "avc: "|grep dbus
[ 24.209253] type=1400 audit(1568196156.003:300): avc: denied { read } for pid=503 comm="gs-
dbus-daemon" scontext=u:r:dbus:s0 tcontext=u:r:nvramprop:s0 tclass=file permissive=1
[ 24.210475] type=1400 audit(1568196156.003:300): avc: denied { read } for pid=503 comm="gs-
dbus-daemon" scontext=u:r:dbus:s0 tcontext=u:r:nvramprop:s0 tclass=file permissive=1
[ 24.210537] type=1400 audit(1568196156.003:301): avc: denied { search } for pid=503
comm="gs-dbus-daemon" name="1450" dev="proc" ino=27712 scontext=u:r:dbus:s0
tcontext=u:r:nvramprop:s0 tclass=dir permissive=1
[ 24.210701] type=1400 audit(1568196156.003:301): avc: denied { search } for pid=503comm="gs-dbus-daemon" name="1450" dev="proc" ino=27712 scontext=u:r:dbus:s0
tcontext=u:r:nvramprop:s0 tclass=dir permissive=1
[ 24.210727] type=1400 audit(1568196156.003:302): avc: denied { open } for pid=503 comm="gs-
dbus-daemon" path="/proc/1450/cmdline" dev="proc" ino=28152 scontext=u:r:dbus:s0
tcontext=u:r:nvramprop:s0 tclass=file permissive=1
[ 24.218018] type=1400 audit(1568196156.003:302): avc: denied { open } for pid=503 comm="gs-
dbus-daemon" path="/proc/1450/cmdline" dev="proc" ino=28152 scontext=u:r:dbus:s0
tcontext=u:r:nvramprop:s0 tclass=file permissive=1
selinux审查会把每一步操作的denied信息记录下来,在permissive模式下仅仅只做记录,方便开发
者优化。我们以其中一条为例:
[
24.210727] type=1400 audit(1568196156.003:302): avc: denied { open } for
pid=503 comm="gs-dbus-daemon" path="/proc/1450/cmdline" dev="proc" ino=28152
scontext=u:r:dbus:s0 tcontext=u:r:nvramprop:s0 tclass=file permissive=1
//这条语句说是dbus进程在访问nvramprop进程中的文件时缺少权限。
denied { } - 被拒绝的操作open
pid - dbus守护进程的pid 503
comm - dbus守护进程的名称 gs-dbus-daemon
path - 被拒绝操作的文件路径/proc/1450/cmdline,1450是nvramprop的pid
dev - 被拒绝的文件在哪个磁盘设备
ino - 文件的inode
scontext - 操作源,dbus域
tcontext - 操作目标,nvramprop域
tclass - 操作目标的类型,file文件类型
permissive - 也就是当前宽容模式的状态
根据scontext确定源,根据tcontext确定目标,根据tclass确定目标类型,根据denied { open }确定
操作。这样就可以配置一条allow规则: allow dbus nvramprop:file open
。
前面dev/socket/dbus文件的类型配置没有生效,主要是因为这个socket文件不是由Security server
管理的,是程序自己后面生成的,需要在创建的地方手动调用libselinux库给它配置权限,如果是已经
在sepolicy下配置了策略,调用 selinux/android.h
中的 int selinux_android_restorecon(const char *file, unsigned int flags)
方法即可,系统会根
据context文件重新分配域。在Java端也有对应的接口,使用Selinux.java
。在串口下调试可以用
restorecon
命令重新配置。还有一些其他的libselinux接口这里不做讲解自己去看:
android/external/selinux/libselinux/include/selinux/android.h
android/frameworks/base/core/java/android/os/SELinux.java
2.6 难点
从Android8开始系统google把sepolicy配置分成了平台和供应商两部分,这里平台的又分为private
和public的。 政策位于 AOSP 中的以下位置:
system/sepolicy/public
。其中包括所导出的用于供应商特定政策的政策。所有内容都会纳入
Android 8.0 兼容性基础架构。公共政策会保留在不同版本上,因此您可以在自定义政策的
/public 中添加任何内容。正因如此,可存放在 /public
中的政策类型的限制性更强。将此目
录视为相应平台的已导出政策 API:处理 /system
与 /vendor
之间的接口的所有内容都位于这
里。system/sepolicy/private
。包括系统映像正常运行所必需(但供应商映像政策应该不知道)的
政策。
system/sepolicy/vendor
。包括位于 /vendor
但存在于核心平台树(非设备特定目录)中的组
件的相关政策。这是编译系统区分设备和全局组件的软件工件;从概念上讲,这是下述设备专用政
策的一部分。
device/manufacturer/device-name/sepolicy
。包含设备专用政策,以及对政策进行的设备自
定义(在 Android 8.0 及更高版本中,该政策对应于供应商映像组件的相关政策)。
在GS项目中主要sepolicy配置放在 device/rockchip/rk3399/sepolicy-gs
中:
.
├── private
│ ├── bluetooth.te
│ ├── dbus.te
│ ├── file_contexts
│ ├── genfs_contexts
│ ├── init.te
│ ├── nvram_prop.te
│ ├── property_contexts
│ ├── pvalue_init.te
│ ├── pvalue_validation.te
│ ├── sux.te
│ ├── system_app.te
│ └── system_server.te
├── public
│ ├── bcmdhd.te
│ ├── dbus.te
│ ├── file.te
│ ├── gs-pppoe.te
│ ├── gs-supplicant.te
│ ├── gs-voip_apply.te
│ ├── healthd.te
│ ├── lmkd.te
│ ├── netd.te
│ ├── nvram_device.te
│ ├── nvram_prop.te
│ ├── property.te
│ ├── pvalue_init.te
│ ├── pvalue_validation.te
│ ├── servicemanager.te
│ └── sux.te
├── Readme.txt
└── vendor
├── hal_bluetooth_default.te
├── hal_bootctl_default.te
└── vendor_init.te
已经配置了部分已知的进程和文件,还有一些init
中注册的服务没有分配独立的域名,使用的是netd
的,会导致一些系统针对netd
的neverallow
政策失败,需要模块负责人尽量自己去添加下对应的政策,
因为模块需要操作哪些进程或者文件都需要很清楚才能很好的去配置这些东西,下面是目前或者接下来
需要改善的一些init进程:
service syslogd /system/xbin/syslog.sh
service sysinit /system/etc/startup/init_xpndr_board.sh
service gparse /system/xbin/gparse
service phone_service /system/bin/gs_phoneservice gs_avs /system/bin/gs_avs
service lighttpd /system/lighttpd/sbin/lighttpd
service lldpd
/system/bin/start_lldpd.sh
service pppoe /system/xbin/pppoe.sh
service vlan /system/etc/vlan.sh
service supplicant /system/xbin/supplicant.sh restart
service onbootcomplete /system/xbin/onBootCompleted.sh
service do_factory_reset /system/xbin/do_factory_reset.sh
service gs_cpe /system/bin/cpestart.sh
service udisk_upgrade /system/xbin/prov_u_disk_upgrade.sh
service provinstall /system/xbin/prov_install
service provision /system/xbin/provision.sh
service auth_pem /system/xbin/auth_pem
//还有一些没申明selabel的,估计没跑起来或者废弃了。
上面的这些基本都是使用了netd域名,有些网络相关的可以后面再优化,其他的有用到的需要相关模块
跟进优化了。
3 辅助工具
3.1 audit2allow
在编写allow策略的时候有一个host工具audit2allow可以协助快速编写te策略,用法很简单:
audit2allow -i ~/sda4/tmp/avc-0911.log -o ~/sda4/tmp/avc-0911.txt
-i file - 输入文件,保存的是audit denied log,可以在设备上"dmesg|grep 'avc: ' >
/data/avc-0911.log"
-o file - 输出文件,会把所有allow策略生成在一个文件中,可以参考这个输出文件进行我们的配置。
值得注意的是audit2allow生成的规则只是个参考,很多是会被neverallow限制的,如果遇到有很多
block_device、socket_device、default_service等默认域的应该是需要新增策略配置了。
3.2 sepolicy-analyze
一个host工具,部分用法:
sepolicy-analyze out/target/product/<board>/root/sepolicy permissive
: 查看当
前定义的permissve域,在user版本编译的时候不允许存在。
sepolicy-analyze out/target/product/<board>/root/sepolicy attribute -l : 列出当
前policy下所有的attribute
sepolicy-analyze out/target/product/<board>/root/sepolicy attribute <name> :
查找指定名字的attribute
sepolicy-analyze out/target/product/<board>/root/sepolicy attribute -r <name>
: 查找指定名字type
路径:android/system/sepolicy/tools/sepolicy-analyze/README
3.3 其他的一些工具
参考:android/system/sepolicy/tools/README
checkfc - 检查file_contexts或者property_contexts配置
checkseapp - 检查seapp_contexts配置
sepolicy-check - 检查具体域名配置,如下
sepolicy-check -s <domain> -t <type> -c <class> -p <permission> -P
out/target/product/<board>/root/sepolicy
4 总结
Selinux中的Sepolicy配置是个很耗时的工程,关系也挺复杂的,这里也只是讲了部分配置,涉及到更
复杂权限也需要对linux系统有很深的了解,以及对文件系统的各种操作比较熟悉。前期的目标是能过
CTS测试,如果很熟悉这些规则了,也可以使用Sepolicy中的很多宏,还有自定义复杂的neverallow规
则等。
5 参考
AOSP源码: android/system/sepolicy/
AOSP官网:https://source.android.com/security/selinux
文章评论