一、 SELinux简单介绍
如下基础概念性内容来源为google网站,建议打开如下链接浏览,本文此部分为摘抄。
https://source.android.google.cn/security/selinux
1. SELinux 概念
Android 中的安全增强型 Linux(SELinux )按照**默认拒绝**的原则运行:
任何未经明确允许的行为都会被拒绝。
SELinux 可按两种全局模式运行:
宽容模式:权限拒绝事件会被记录下来,但不会强制执行SELinux 安全政策。强制模式:权限拒绝事件会被记录下来并强制执行。
Android 中包含 SELinux(处于强制模式)和默认适用于整个 AOSP 的相应安全政策。在强制模式下,非法操作会被阻止,并且尝试进行的所有违规行为都会被内核记录到 和
dmesg。开发时,您应该先利用这些错误信息对软件和 SELinux 政策进行优化,再对它们进行强制执行。
logcat
2. 标签、规则和域
SELinux 依靠**标签**来匹配操作和政策。标签用于决定允许的事项。套接字、文件和进程在 SELinux 中都有标签。SELinux 在做决定时需参照两点:一是为这些对象分配的标签,二是定义这些对象如何交互的政策。
标签格式
标签采用以下形式:,其中:
user:role:type:mls_level
是访问决定的主要组成部分,可通过构成标签的其他组成部分进行修改。对象会映射到**类**,对每个类的不同访问类型由**权限**表示。
type
政策规则格式
allow domains types:classes permissions;
各字段说明:
– 一个进程或一组进程的标签(也称域类型)。
Domain – 一个对象(例如,文件、套接字)或一组对象的标签。
Type – 要访问的对象(例如,文件、套接字)的类型。
Class – 要执行的操作(例如,读取、写入)。
Permission
规则示例
# 允许所有应用域读写带有 app_data_file 标签的文件
allow appdomain app_data_file:file rw_file_perms;
常用宏说明
上述规则依赖于 文件中定义的宏,
global_macros 文件中还提供了其他实用宏(位于
te_macros 目录,Android 8.0+ 在
system/sepolicy 子目录)。应尽可能使用这些宏,以降低权限被拒的风险。
public
域的典型示例
| 应用类型 | 域标签 | 权限等级 |
|---|---|---|
| 第三方应用 | untrusted_app | 最低 |
| 平台应用 | platform_app | 中等 |
| 系统 UID 应用 | system_app | 最高 |
禁止直接访问的通用标签
在任何情况下,都不应直接允许域访问以下通用标签;而应为一个或多个对象创建一个更具体的类型:
socket_devicedeviceblock_devicedefault_servicesystem_data_filetmpfs
3. 关键文件
SELinux 被设置为“默认拒绝”模式,这意味着,对于在内核中存在钩子的每一次访问,都必须获得政策的明确许可。通常情况下,您不能直接修改 文件,但可以添加或修改自己的设备专用政策文件(位于
system/sepolicy 目录)。
/device/manufacturer/device-name/sepolicy
3.1 政策文件
以 结尾的文件是 SELinux 政策源代码文件,用于定义域及其标签。您可能需要在
*.te 中创建新的政策文件,但应尽可能尝试更新现有文件。
/device/manufacturer/device-name/sepolicy
3.2 上下文的描述文件
用于为系统对象指定标签,核心文件如下:
| 文件名 | 用途 | 生效方式 |
|---|---|---|
| file_contexts | 为文件分配标签 | 重新构建映像或运行 |
| genfs_contexts | 为不支持扩展属性的文件系统(proc/vfat)分配标签 | 重新启动设备或卸载重装文件系统 |
| property_contexts | 为系统属性分配标签,控制进程对属性的读写权限 | 启动时由 init 进程读取 |
| service_contexts | 为 Binder 服务分配标签,控制服务的注册和查询 | 启动时由 servicemanager 进程读取 |
| seapp_contexts | 为应用进程和目录分配标签 |
应用启动时由 zygote 读取;启动时由 installd 读取 |
| mac_permissions.xml | 根据应用签名和包名分配 seinfo 标记 | 启动时由 system_server 读取 |
3.3 BoardConfig.mk makefile
修改或添加政策文件和上下文描述文件后,需更新 以引用
/device/manufacturer/device-name/BoardConfig.mk 子目录和新政策文件:
sepolicy
BOARD_SEPOLICY_DIRS +=
<root>/device/manufacturer/device-name/sepolicy
4. 自定义 SELinux
SELinux 采用白名单方法,仅授予政策中明确允许的访问权限。如需自定义 SELinux 设置,应遵循以下步骤和原则:
核心原则
使用最新的 Android 内核。采用**最小权限原则**。仅针对您向 Android 添加的内容调整政策(默认政策已适配 AOSP)。将软件组件拆分为多个单一功能模块。为模块创建隔离的 SELinux 政策。政策文件放在 目录(
/device/manufacturer/device-name/sepolicy 扩展名),通过
.te 变量纳入编译。先将新域设为宽容域(便于调试)。分析拒绝日志并优化域定义。调试完成后移除宽容声明。
BOARD_SEPOLICY
4.1 政策声明示例
SELinux 基于 M4 计算机语言,支持宏定义以简化配置。
完整权限声明
# 允许所有域读写 /dev/null
allow domain null_device:chr_file { getattr open read ioctl lock append write};
# 允许所有域只读访问 /dev/zero
allow domain zero_device:chr_file { getattr open read ioctl lock };
宏简化声明(推荐)
# 允许所有域读写 /dev/null
allow domain null_device:chr_file rw_file_perms;
# 允许所有域只读访问 /dev/zero
allow domain zero_device:chr_file r_file_perms;
4.2 neverallow 规则
SELinux 规则用于禁止在任何情况下都不应该发生的行为。以下是关键规则示例及说明:
neverallow
规则 48(禁止非授权进程使用 ptrace)
neverallow { domain -debuggerd -vold -dumpstate -system_server } self:capability sys_ptrace;
说明: 权限允许对任意进程执行 ptrace 命令,仅指定系统组件可享有该权限。如需该权限,通常表明存在不必要的功能,应移除相关组件。
sys_ptrace
规则 76(禁止执行非系统分区代码)
neverallow { domain -appdomain -dumpstate -shell -system_server -zygote } { file_type -system_file -exec_type }:file execute;
说明:仅允许执行 分区中的代码,以保证启动时验证等安全机制。遇到该规则冲突时,应将违规代码移至
/system 分区。
/system
5. 验证 SELinux
步骤 1:设置宽容模式
在 boot 的 bootargs 参数中加入:
androidboot.selinux=permissive
步骤 2:查看拒绝日志
SELinux 拒绝事件会记录到 和
dmesg,日志包含“avc:”字样,可通过以下命令过滤:
logcat
# 查看当前拒绝事件
cat /proc/kmsg | grep avc
# 查看上次启动的拒绝事件
cat /sys/fs/pstore/console-ramoops | grep avc
# 实时查看 logcat 中的拒绝事件
logcat | grep avc
步骤 3:日志分析示例
日志示例
<5> type=1400 audit: avc: denied { read write } for pid=177
comm="rmt_storage" name="mem" dev="tmpfs" ino=6004 scontext=u:r:rmt:s0
tcontext=u:object_r:kmem_device:s0 tclass=chr_file
日志关键元素解析
| 元素 | 说明 | 示例值 |
|---|---|---|
| 操作 | 试图执行的操作 | read write |
| 操作方 | 进程的安全上下文(scontext) | u:r:rmt:s0(rmt 域) |
| 对象 | 目标资源的安全上下文(tcontext) | u:object_r:kmem_device:s0(kmem_device 类型) |
| 目标类别 | 操作对象的类型(tclass) | chr_file(字符设备) |
修复示例
针对上述日志,需在 文件中添加:
rmt.te
allow rmt kmem_device:chr_file { read write };
二、 中间件SELinux权限配置
1. 增加中间件sepolicy目录及mk文件
问题背景
中间件涉及不同芯片平台,若直接将 sepolicy 配置放在设备平台代码中,移植时需单独修改 ,不利于公共模块维护。
BoardConfig.mk
解决方案
在中间件内部创建 sepolicy 目录,按 Android 版本和芯片平台分目录存放配置文件,并通过独立 mk 文件管理编译。
目录结构
vendor/xxx/driverBase/android/sepolicy/
├── AndroidP
└── AndroidQ
├── common
└── platform
└── mstar
sepolicy.mk 文件内容
# te for driverbase
ifeq ($(shell test $(PLATFORM_SDK_VERSION) -ge 29 && echo OK),OK)
BOARD_SEPOLICY_DIRS += vendor/skyworth/driverBase/android/sepolicy/AndroidQ/common
ifneq ($(filter mstar, $(LONGAN_TARGET_PLATFORM)),)
BOARD_SEPOLICY_DIRS += vendor/skyworth/driverBase/android/sepolicy/AndroidQ/platform/mstar
endif
else
BOARD_SEPOLICY_DIRS += vendor/skyworth/driverBase/android/sepolicy/AndroidP
endif
BoardConfigCommon.mk 配置
在 中添加:
/device/skyworth/芯片/common/BoardConfigCommon.mk
# SELinux
include vendor/xxx/driverBase/sepolicy.mk
2. 增加中间件service对应的sepolicy配置文件
以 service 为例,需完成以下配置:
/vendor/bin/hw/vendor.skyworth.hardware.hwtv@1.0-service
2.1 增加hal_hwtvservice.te文件
# 声明hal_hwtvservice为一个域
type hal_hwtvservice, domain;
# 声明hal_hwtvservice的可执行程序类型
type hal_hwtvservice_exec, exec_type, file_type, vendor_file_type;
# 声明此service使用hwbinder(system分区进程通过hwbinder访问)
hwbinder_use(hal_hwtvservice);
# 声明此service使用vndbinder(vendor分区进程通过vndbinder访问)
vndbinder_use(hal_hwtvservice);
# init启动service时类型转换声明(宏)
# 将hal_hwtvservice_exec(客体)转换成hal_hwtvservice(进程域)
init_daemon_domain(hal_hwtvservice);
# 将service加入到hwservice_manager(通过hidl接口访问)
add_hwservice(hal_hwtvservice, hal_hwtvservice_hwservice);
2.2 在file_contexts里面增加service bin文件的声明
/(vendor|system/vendor)/bin/hw/vendor.skyworth.hardware.hwtv@1.0-service u:object_r:hal_hwtvservice_exec:s0
2.3 在hwservice.te文件中定义新加的service类型
type hal_hwtvservice_hwservice, hwservice_manager_type;
2.4 在hwservice_contexts文件中增加service的权限
vendor.skyworth.hardware.hwtv::IHwtv u:object_r:hal_hwtvservice_hwservice:s0
2.5 在service_contexts文件中增加service的权限
hwtvservice_1_0 u:object_r:hwtvservice_1_0_service:s0
2.6 配置其他进程/应用对此service的访问
允许platform_app访问
在 中添加:
platform_app.te
allow platform_app hal_hwtvservice_hwservice:hwservice_manager { find };
allow platform_app hal_hwtvservice:binder { call transfer };
允许system_app访问
在 中添加:
system_app.te
allow system_app hal_hwtvservice_hwservice:hwservice_manager { find };
allow system_app hal_hwtvservice:binder { call transfer };
配置原则
其他进程/应用的访问权限可先将 SELinux 设为宽容模式,根据 警告信息逐步添加。
avc
3. 中间件sepolicy配置实例说明
3.1 配置新增加分区的上下文
问题现象
查看 xxx 分区文件上下文,发现未正确配置(均为 类型):
system_file
ls -lZ /xxx
drwxrwxr-x 4 system system u:object_r:system_file:s0 4096 2020-11-09 10:35 app
-rw-rw-rw- 1 root root u:object_r:system_file:s0 43 2009-01-01 00:01 checksysVersion
配置步骤
在 中增加 xxx
file.te 文件类型:
_file
type xxx_file, file_type;
在 中声明
file_contextsxx 目录下的文件类型:
/x
/xxx(/.*)? u:object_r:xxx_file:s0
在 中设置目录文件继承标签:
init.rc
restorecon_recursive /xxx
验证结果
配置后文件上下文应显示为 xxx 类型:
_file
ls -lZ /xxx/app
drwxrwxr-x 4 system system u:object_r:xxx_file:s0 4096 2020-11-09 10:35 app
3.2 配置property属性的上下文
场景1:自定义属性访问权限
问题现象
自定义属性 默认为
third.get.facSingleKeyEnable 类型,Android 10.0 不允许 vendor 下的 native service 访问,开机出现
default_prop 警告:
avc
selinux: avc: denied { get } for property=third.get.facSingleKeyEnable pid=2507 uid=0 gid=0 scontext=u:r:hal_hwtvservice:s0 tcontext=u:object_r:default_prop:s0 tclass=property_service permissive=1
配置步骤
在 中增加自定义属性类型:
property.te
type vendor_xxx_default_prop, property_type;
在 中将自定义属性关联新类型:
property_contexts
third.get.facSingleKeyEnable u:object_r:vendor_xxx_default_prop:s0
在 中添加访问权限:
hal_hwtvservice.te
get_prop(hal_hwtvservice, vendor_xxx_default_prop); # 读权限
set_prop(hal_hwtvservice, vendor_xxx_default_prop); # 写权限
场景2:系统属性访问权限(间接访问)
问题现象
系统属性 为
dev.bootcomplete 类型,vendor 下的 native service 无法直接访问,开机出现
exported3_system_prop 警告:
avc
selinux: avc: denied { get } for property=dev.bootcomplete pid=2507 uid=0 gid=0 scontext=u:r:hal_hwtvservice:s0 tcontext=u:object_r:exported3_system_prop:s0 tclass=property_service permissive=1
解决方案(属性转换)
在 中添加属性转换:
vendor/etc/init/vendor.xxx.hardware.hwtv@1.0-service.rc
on property:dev.bootcomplete=1
setprop third.get.bootcomplete 1
在 中声明自定义属性类型:
property_contexts
third.get.bootcomplete u:object_r:vendor_xxx_default_prop:s0
在 中添加
vendor_init.te 对属性的访问权限:
vendor_init
get_prop(vendor_init, vendor_xxx_default_prop);
set_prop(vendor_init, vendor_xxx_default_prop);
注意事项
若自定义属性无法触发 动作,需在
on property 的
system/core/init/stable_properties.h 数组中添加白名单:
kExportedActionableProperties
static const std::set<std::string> kExportedActionableProperties = {
"dev.bootcomplete",
"init.svc.console",
// ... 其他默认属性
"third.get.bootcomplete", // 新增自定义属性
};
3.3 配置进程的上下文及用户
问题现象
查看 进程上下文,发现以
hwtvservice 用户运行,出现
root 权限拒绝警告:
dac_override
ps -AZ | grep hwtv
u:r:hal_hwtvservice:s0 root 2507 1 54768 17748 binder_thread_read 0 S vendor.xxx.hardware.hwtv@1.0-service
avc: denied { dac_override } for capability=1 scontext=u:r:hal_hwtvservice:s0 tcontext=u:r:hal_hwtvservice:s0 tclass=capability permissive=1
解决方案
修改 service 的运行用户为 ,在
system 中调整:
vendor/etc/init/vendor.xxx.hardware.hwtv@1.0-service.rc
service hwtv-1-0 /vendor/bin/hw/vendor.skyworth.hardware.hwtv@1.0-service
class early hal
user system # 改为system用户
group root system audio input drmrpc # 根据需求添加用户组
3.4 ioctl权限配置
问题背景
Android Q 增强了对 的审查,除授权
ioctl 操作外,还需指定具体的
ioctl。
ioctlcmd
错误配置示例
直接授权 会导致编译报错:
ioctl
# 错误配置
allowxperm hal_hwtvservice proc_utopia: file ioctl;
ERROR 'syntax error' at token ';' on line 73660:
allowxperm hal_hwtvservice proc_utopia: file ioctl;
正确配置示例
需指定具体的 ID:
ioctlcmd
# 允许 hal_hwtvservice 对 proc_utopia 执行指定 ioctl 命令
allowxperm hal_hwtvservice proc_utopia:{ file } ioctl { 0x5501 0x5503 };
3.5 通用域的配置
问题现象
访问 时出现权限拒绝,直接使用
/proc/apm 通用域配置会触发
proc 规则:
neverallow
avc: denied { read } for comm="fsck" path="/proc/apm" dev="proc" scontext=u:r:fsck:s0 tcontext=u:object_r:proc:s0 tclass=file permissive=1
# 错误配置(触发neverallow)
allow fsck proc:file r_file_perms;
正确配置示例
在 中定义专用类型:
file.te
type proc_apm, fs_type, proc_type, mlstrustedobject;
在 中关联路径和类型:
genfs_contexts
genfscon proc /apm u:object_r:proc_apm:s0
在 中添加访问权限:
fsck.te
allow fsck proc_apm:file r_file_perms;
三、 部分编译及验证
1. 部分编译命令
修改 SELinux 配置后,可单独编译 sepolicy 以提高效率:
mmm -j32 system/sepolicy 2>&1 | tee se.txt
MTK平台注意事项
MTK 平台部分编译通过后,完整编译前需执行 ,否则可能报错。建议:
make mtk_clean
第一个工程用于部分编译验证 SELinux 配置。验证通过后,将修改合并到第二个工程,直接完整编译输出升级包。
2. 验证方法
步骤 1:拷贝编译产物
单独编译后,将输出目录的以下文件拷贝到测试设备:
system/etc/selinux/
vendor/etc/selinux/
步骤 2:自动化脚本(参考)
cpselinux.sh(拷贝输出文件到本地目录)
#!/bin/bash
# 拷贝 sepolicy 编译产物到 ~/selinuxOutputFile
OUTPUT_DIR=~/selinuxOutputFile
mkdir -p $OUTPUT_DIR/system/etc/selinux
mkdir -p $OUTPUT_DIR/vendor/etc/selinux
cp -r out/target/product/mt9652/system/etc/selinux/* $OUTPUT_DIR/system/etc/selinux/
cp -r out/target/product/mt9652/vendor/etc/selinux/* $OUTPUT_DIR/vendor/etc/selinux/
echo "SELinux files copied to $OUTPUT_DIR"
updateSe.sh(设备端覆盖selinux文件)
#!/bin/bash
# 从 U 盘拷贝 selinux 文件到设备
U盘路径=/mnt/usb
SYSTEM_SELINUX=/system/etc/selinux
VENDOR_SELINUX=/vendor/etc/selinux
# 挂载为可写
mount -o remount,rw /system
mount -o remount,rw /vendor
# 覆盖文件
cp -r $U盘路径/system/etc/selinux/* $SYSTEM_SELINUX/
cp -r $U盘路径/vendor/etc/selinux/* $VENDOR_SELINUX/
# 恢复只读
mount -o remount,ro /system
mount -o remount,ro /vendor
echo "SELinux files updated successfully"
步骤 3:验证流程
将 目录拷贝到 U 盘。U 盘插入设备,执行
selinuxOutputFile 脚本覆盖文件。重启设备,查看
updateSe.sh 和
dmesg 确认无
logcat 拒绝日志。
avc
四、 参考文档
Google 官方文档:Android 中的安全增强型 Linux