OpenHarmony 中的 SELinux
OpenHarmony 中的 SELinux
一、为什么需要 SELinux
1.1 DAC 的局限性
类 UNIX 系统使用一种自主访问控制(discretionary access control,DAC)的形式来控制能否访问给定资源。此方法根据用户(用户或程序)身份确定对某个对象的 rwx 访问权限。

例如: 用户 A 执行程序 B 访问文件 C。
程序 B 访问文件 C 的时候使用的 A 的身份来访问的,如果访问 C 的过程中,程序 B 有 bug 去修改了隐私文件 D,而正好用户 A 有 D 的写权限,那么就出问题了。
这样的访问控制都会带来一个问题,因为所用的程序 B 能够继承用户 A 的访问权限,这样该程序 B 就可能执行破坏事件。如果用户用 sudo 借用超级管理员身份,此时 root 权限过大程序可能执行更多破坏事件。
传统 DAC 权限(chmod/chown)仅判断「用户是否拥有该文件的操作权」,控制粒度较大,而 SELinux 判断「该进程是否被策略允许执行此操作」。
1.2 解决方案
SELinux 更能遵从最小权限的理念。在缺省的 enforcing 模式下,一切均被拒绝,接着有一系列例外的政策allow来允许系统的每个元素(服务、程序、用户)运作时所需的访问权,这种控制类型称为强制访问控制(MAC)。当一项服务、程序或用户尝试访问或修改一个它不须用的文件或资源时,它的请求会遭拒绝,而这个行动会被记录下来。
例如上面的程序 B 只被允许访问文件 C,那么程序 B 访问隐私文件 D 就会被拦截。
二、SELinux 简要说明
SELinux(安全增强式 Linux,Security-Enhanced Linux)是 Linux 的安全组件,包含一组内核修改和用户空间工具,并提供了基于安全策略的强制访问控制机制(Mandatory Access Control,MAC)。SELinux 已经被添加到各种 Linux 发行版中。其软件架构力图将软件执行与安全策略设计分离。负责对文件,属性,服务等系统资源提供强制访问控制保护。提供 neverallow 规则限制系统中的高危操作,减少系统安全风险。
软件执行与安全策略设计分离:
LSM = Linux Security Module,是 Linux 内核提供的一套安全框架,允许不同的安全模块以"插件"方式注册到内核中,在 DAC 检查之后、实际访问资源之前插入额外的安全检查。
• OpenHarmony 使用 SELinux,Ubuntu 使用 AppArmor,多个 LSM 模块可以同时注册
• SELinux 安全模块内置在内核中,代码在 `kernel/linux/linux-5.10/security/selinux/`,编译归档到 boot 分区
• SELinux 策略在 `base/security/selinux_adapter/sepolicy/`,编译归档到 system 分区,运行的时候加载到内核
• OpenHarmony 对标准 SELinux 做了一些适配,在内核和策略层都有适配,策略层新增了 OH 独有的类 `samgr_class`、`hdf_devmgr_class` 等,对 OH 独有对象的访问控制
SELinux 采用域-标签的访问控制模型。

任意的访问都可以看成主体对于客体的访问。只需要对主体和客体进行适当的标签配置,并对访问操作等进行定义,就可以对访问进行控制。
例如:
allow type_a type_b:file { open read };
该条规则就是声明允许标记为 type_a 的主体,可以对标记为 type_b 的客体,进行 { open read } 的文件访问。SELinux 对所有未被声明的访问动作,默认拒绝。


2.1 SELinux 基本概念
SELinux 运行模式:
| 模式 | 说明 |
| Enforcing | 强制模式:策略被执行,违规操作被拒绝并记录 |
| Permissive | 宽容模式:策略违规只记录,不阻止 |
| Disabled | 关闭 SELinux |
常用命令:
getenforce # 查看当前模式
setenforce 0 # 切换为 Permissive
setenforce 1 # 切换为 Enforcing
sestatus # 查看 SELinux 状态(需安装 policycoreutils)
# 永久配置
/etc/selinux/config # SELINUX=enforcing|permissive|disabled
sed -i 's/enforcing/permissive/g' /system/etc/selinux/config
有些工具是 Linux 的三方工具需要安装,OpenHarmony 不一定有。
核心三要素:
| 要素 | 说明 |
| 类型(Type) | 每个进程和文件都有类型(type),SELinux 主要基于类型强制访问控制 |
| 策略(Policy) | 定义哪些类型可以访问哪些对象 |
| 上下文(Context) | 格式如 user:role:type:level,可以理解为一种电子标签或身份令牌 |
上下文示例:
system_u:object_r:httpd_sys_content_t
标签就定义了该对象的 SELinux 用户名(不同于 Linux 用户名)和 SELinux 角色、类型等。在缺省的针对型政策 targeted 里,类型是用来实施强制类型的重要字段,在这里它是 httpd_sys_content_t。
SELinux 三种访问控制方法:
| 方法 | 说明 | 常用程度 |
| 强制类型(TE) | 定义"哪种类型的进程"可访问"哪种类型的资源",是主要访问控制机制 | 常用 |
| 基于角色的访问控制(RBAC) | 以 SELinux 用户(未必等同 Linux 用户)为基础,缺省策略未采用 | 不常用 |
| 多层保障(MLS) | 普遍不获采用,而且经常隐藏在缺省策略内 | 不常用 |
上下文中的 4 个数据,我们主要用类型来控制访问。

三种策略范围(SELINUXTYPE):
从小到大可以理解为 minimum < targeted < mls。 不同Linux发行版本的范围值枚举
| 策略 | 保护范围 | 说明 |
| minimum | 最小 | 只保护选定的进程,是对 targeted 策略的一种修改 |
| targeted | 中等 | 针对特定系统进程保护,其他进程运行在 DAC 下(默认策略) |
| mls | 最大 | 为所有系统进程提供多级别安全保护 |
不同Linux发行版本的范围值枚举,具体查看本地config文件。
配置文件 /etc/sysconfig/selinux 还包含了 SELinux 运行策略的信息,通过改变变量 SELINUXTYPE 的值实现。
| 策略类型 | 说明 |
| targeted | 仅针对预制的几种网络服务apache、sendmail、bind、postgresql和访问请求使用 SELinux 保护 |
| strict | 所有访问请求都要经过 SELinux |
常用查看命令:
ps -Z # 查看进程上下文
# 示例输出:
# LABEL PID TTY TIME CMD
# u:r:sh:s0 10466 pts/1 00:00:00 sh
ls -Z # 查看文件上下文
ls -Z /path/to/file
chcon -t httpd_sys_content_t /var/www/html/index.html # 修改上下文
restorecon -v /var/www/html/index.html # 还原默认上下文
semanage boolean -l # 查询当前策略布尔值
setsebool httpd_can_network_connect on # 临时修改
setsebool -P httpd_can_network_connect on # 永久修改
2.2 SID(Security Identifier)
SID 是 SELinux 在内核启动时预定义的「初始标签」,在策略加载之前就用 SID 来标记内核线程和关键资源。策略没加载就没有类型(type),所以 SELinux 设计了 SID 作为临时标签,等策略加载后再映射为正式的类型。
内核使用 SID,用户空间使用类型,两者通过策略映射关联。SID 是内核内部用的整数编号,type 是策略中定义的可读名称。SID 仅在策略加载前和内核内存中有效,用户接触到的都是 type。
三、SELinux 语法说明
官方文档:https://github.com/SELinuxProject/selinux-notebook
当前主要用到的关键字有 type、attribute、allow、neverallow 等。
3.1 主要关键字速查
| 关键字 | 用途 | 示例 |
| type | 定义类型 | type httpd_t, domain; —— 声明 httpd_t 类型并加入 domain 属性 |
| attribute | 定义属性(类型的集合) | attribute file_type; |
| typeattribute | 将类型加入属性集合 | typeattribute httpd_t domain; |
| typealias | 给类型起别名 | typealias httpd_t alias myhttp_t; |
| type_transition | 定义域转换规则 | type_transition init_t httpd_exec_t:process httpd_t; |
| allow | 添加访问权限 | allow type_a type_b:file { open read }; |
| allowxperm | 允许特定扩展权限(如 ioctl 命令号) | allowxperm httpd_t device_t:chr_file ioctl 0x5413; |
| neverallow | 编译时抑制指定访问 | 违反会导致编译失败 |
`allowxperm` 说明:`allow` 只能允许调用 ioctl,而 `allowxperm` 可以粒度更小控制到 ioctl 的哪个命令号,需要和 `allow` 配合使用,两者是 AND 关系。
口语中: 进程的 type 叫 domain(域),文件的 type 叫 type(类型),语法上都是 type 关键字声明的。type httpd_t, domain; 中的 domain 是属性名叫 domain 的 attribute。
3.2 域转换(Domain Transition)
域转换指进程在执行某个可执行文件时,从一个 SELinux 域自动切换到另一个域的规则。
不同程序运行在不同域里,不同域的权限不同。但系统启动时只有 init_t 在运行,子进程继承 init_t 域拥有 init 的全部权限安全隔离失效。其他进程要域转换进入各自的域。
init (init_t) ──fork──▶ 子进程 (仍为 init_t) ──exec──▶ /system/bin/wifi_service ─▶ 自动转换为 wifi_t
域转换的四条规则(缺一不可):
转换规则定义「什么情况下、从哪个域、转到哪个域」的 SELinux 规则:
# 1. type_transition:声明「执行A文件时,自动转到B域」
type_transition init_t wifi_exec_t:process wifi_t;
# 2. allow 规则:允许 init_t 把进程交给 wifi_t(init 被允许「转让控制权」)
allow init_t wifi_t:process transition;
# 3. allow 规则:允许 init_t 执行 wifi_exec_t 文件(且必须用 exec 方式执行)
allow init_t wifi_exec_t:file { execute read open getattr };
# 4. 标记可执行文件入口点
allow wifi_t wifi_exec_t:file entrypoint;
OpenHarmony 中的简化写法
OH 封装了宏,一条宏等价于上面四条规则:
init_daemon_domain(my_service);
domain_auto_transition_pattern(init, my_service_exec, my_service);
3.3 策略宏隔离
debug_only(`
allow ueventd init:fd use;
')
上面语法使用方法可以参考 `/base/security/selinux_adapter/sepolicy/` 目录已有的策略文件。
SELinux 策略采用 CIL(Common Intermediate Language)编写,其编译流程包括词法分析、语法解析和抽象语法树构建等阶段。
────────────────────────────────────────────────────────────
四、AVC 日志解读
4.1 日志格式
audit: type=1400 audit(1502458430.566:4): avc: denied { open } for
pid=1658 comm="setenforce" path="/sys/fs/selinux/enforce"
dev="selinuxfs" ino=4 scontext=u:r:hdcd:s0
tcontext=u:object_r:selinuxfs:s0 tclass=file permissive=1
字段解读:
| 字段 | 含义 | 示例值 |
| { open } | 被拒绝的操作 | open |
| pid | 访问主体进程号 | 1658 |
| comm | 访问主体进程名 | setenforce |
| path | 被访问客体路径 | /sys/fs/selinux/enforce |
| dev | 被访问文件所属文件系统 | selinuxfs |
| ino | 文件节点编号 | 4 |
| scontext | 访问主体 SELinux 标签 | u:r:hdcd:s0 |
| tcontext | 被访问客体 SELinux 标签 | u:object_r:selinuxfs:s0 |
| tclass | 当前告警所属操作类别 | file |
| permissive | 1 = 仅告警不拦截,0 = 实际拦截 | 1 |
4.2 AVC 日志转化策略
根据 AVC 告警,获取访问信息。
示例 AVC 告警:
avc: denied { open } ... scontext=u:r:hdcd:s0
tcontext=u:object_r:selinuxfs:s0 tclass=file permissive=1
对应策略规则:
allow hdcd selinuxfs:file open;
4.3 AVC 缓存机制
进程请求 → 查询 AVC 缓存 (key: 源域, 目标类型, 类别)
├── 缓存命中 → 直接返回决策 → 不产生新的 audit 日志
└── 缓存未命中 → 全量策略查询 → 缓存结果 → 产生 audit 日志
同一个 (scontext, tcontext, tclass) 的拒绝只会在首次产生 AVC 日志。后续相同访问命中缓存,不再打印。
调整缓存大小:
sudo sysctl -w kernel.selinux_avc_cache_threshold=8192
五、SELinux 配置开发
5.1 TE 文件上库要求
命名规则: 由主体名或进程名确定。
存放路径: ohos_policy/子系统/部件/(system | vendor | public)
| 目录 | 存放内容 |
| system | 非芯片或驱动相关的策略 |
| vendor | 与芯片或驱动相关的策略 |
| public | 公共部分 |
若缺少子系统或部件目录结构请自行建立。
TE 文件四部分结构:
第一部分:LICENSE 信息开源协议说明
第二部分:标签定义信息及转换规则
第三部分:allow 规则配置
第四部分:neverallow 规则配置
编写策略步骤:
Step 1 → .te 文件中 type 定义类型
Step 2 → context 中贴标签
Step 3 → .te 文件中 allow 授权
Step 4 → 如果需要在基线和白名单中添加新定义的 type
5.2 适配进程域标签
进程的执行域(类型),代表了进程在 SELinux 系统中的身份,要求业务程序独立配置进程域。
须满足以下规定:
禁止非 init 进程采用 `u:r:init:s0` 这个域运行
所有进程需要尽可能配置独立的运行域,保证权限授予的最小化
所有运行域定义需要加入 `domain` 属性,还应视情况加入 `sadomain`、`hdfdomain` 以及 `hap_domain`
有两种方法适配进程标签:
方法一:SA 服务配置 secon 字段
适用于由 init 启动的 SA 服务程序。
在适当的 te 文件中,新增标签定义。例如 ohos_policy/xxx/xxx/system/new_process.te 中添加:
type new_process, sadomain, domain;
配置进程 cfg 文件添加 secon 字段:
{
"services": [{
"name": "samgr",
"path": ["/system/bin/sample"],
"secon": "u:r:new_process:s0"
}]
}
由 init 拉起的服务,未配置 secon 字段的,默认加入 limit_domain,该域仅能在调试用。根据告警添加相应 te 规则。
方法二:配置域转换规则
适用于非 init 启动的程序或者非 SA 服务程序。
在适当的 te 文件中,新增标签定义。例如 ohos_policy/xxx/xxx/system/new_process.te 中添加:
type new_process, sadomain, domain;
type new_process_exec, exec_attr, file_attr, system_file_attr;
在 file_contexts 文件中添加可执行文件标签:
/system/bin/new_process u:object_r:new_process_exec:s0
配置域转换规则:
init_daemon_domain(new_process);
# 或者
domain_auto_transition_pattern(init, new_process_exec, new_process);
验证命令:
ps -AZ
5.3 资源文件标签
当前支持配置标签的客体有 file、parameter、sa_service、hdf_service 等。
两步流程:
| 步骤 | 操作 | 说明 |
| 第一步 | type 语句定义相关标签 | type appspawn_exec, system_file_attr, exec_attr, file_attr; |
| 第二步 | 配置对应文件贴标签 | /system/bin/appspawn u:object_r:appspawn_exec:s0 |
第一步只是 type 声明了标签("存在一个叫 xxx 的类型"),第二步才是把标签贴到具体资源上(告诉内核这个文件/参数/服务归什么标签管)。
各类资源配置参考:
| 资源类型 | 配置文件 | 参考路径 |
| 文件标签 | file_contexts | $PRJ/base/security/selinux/sepolicy/base/system/file_contexts(/system、/vendor、/data、/dev) |
| 虚拟文件标签 | virtfs_contexts | $PRJ/base/security/selinux/sepolicy/base/system/virtfs_contexts(/proc、/sys) |
| parameter 标签 | parameter_contexts | $PRJ/base/security/selinux/sepolicy/parameter_contexts |
| sa_service 标签 | service_contexts | $PRJ/base/security/selinux/sepolicy/service_contexts |
| hdf_service 标签 | hdf_service_contexts | $PRJ/base/security/selinux/sepolicy/hdf_service_contexts |
5.4 Data 分区 restorecon 适配
SELinux 的文件标签本质上是文件的拓展属性。系统在创建文件过程中会默认继承父目录的标签配置。场景有点类似域转换
| 分区类型 | 标签配置方式 |
| /system、/vendor 等只读镜像 | 标签信息在编译阶段配置 |
| /data 目录中动态生成的文件 | 创建完文件后,需要重新 restorecon 配置文件标签 |
当前已在 init 提供的 mkdir 命令中嵌入 restorecon 动作,因此在进程 cfg 文件中,使用 mkdir 命令创建的文件夹,会自动更新标签。其他情况,需要业务调用 restorecon 接口刷新标签。
接口:
int Restorecon(const char *path);
int RestoreconRecurse(const char *path);
GN 配置: 参考 base/security/selinux/bundle.json → selinux:librestorecon
5.5 neverallow 适配
SELinux 是一种基于规则的安全机制,因此规则的质量至关重要。如果有开发者无视基本的安全红线,配置有风险的规则,那将对系统安全性产生很大的危害。
neverallow 可以用来杜绝危险的 allow 语句配置。
举例:
neverallow { domain -init } self:capability dac_override;
这一句话代表仅有 init 程序可以给自身配置 dac_override 权限。如果后续有开发尝试添加例如 allow samgr self:capability dac_override;,SELinux 的编译过程便会产生错误。
由提示可以知道,给 samgr 添加 dac_override 的权限配置,违反了 domain.te 中第 160 行的 neverallow 语句。
如果您在开发过程中,遇到需求确实需要违反现有的 neverallow 的场景,请到安全 SIG 组织评审。评审通过则调整 neverallow,禁止直接修改 neverallow 语句。
联系方式见:https://gitee.com/openharmony/community/blob/master/sig/sig-security/sig_security_cn.md
此外,各模块如果有安全性需求,需要保护某类资源不被外部访问或限制访问,也可以自行添加 neverallow。
5.6 AVC 告警收集及策略适配
适配 SELinux 的策略,需要开发者触发相关的 AVC 告警。触发方式就是验证功能模块原有功能,理论上把模块的所有代码都覆盖一遍,就能收集模块所有的 AVC 告警。
告警收集的两种渠道
| 方式 | 记录内容 | 说明 |
| 串口日志 / dmesg | 内核原生支持 + parameter 权限检查(文件、网络等) | 推荐通过连接串口收集 |
| hilog 日志 | SA 服务和 HDF 服务权限检查 | 默认只打一次,可能被冲掉。需要 hilog -w start 开启日志落盘并重启,日志存放在 /data/log/hilog/ |
注意:串口(dmesg)和 hilog 两种日志都需要查看!
收集告警前的准备
关闭 SELinux 后再收集告警:
• SELinux 关闭脚本:https://gitee.com/dapaodexiaoyu2/binary_keep/blob/master/禁用selinux.bat
• 收集告警前,请先打上补丁:https://gitee.com/dapaodexiaoyu2/binary_keep/blob/master/selinux_patch.tgz
• 打上补丁后,删除 out 目录,重编
• 收集告警前请看完附件!!
https://gitee.com/dapaodexiaoyu2/binary_keep
日志转 SELinux 策略工具
• 仓库地址:https://gitee.com/steven-q/selinux_guide(需要把工具里面的 bat 脚本的中文改成英文)

使用方式:
| 选项 | 功能 |
| 选项 1 | 日志落盘 + 禁用 SELinux + 清理现存日志(禁用 SELinux 是改为宽容模式,依然会记录拦截日志) |
| 选项 2 | 有了选项 1 的初始化后,执行用例获取日志 |
| 选项 3 | 如果已经有日志,将日志存放在 log 文件夹中,一键执行转换 |

生成的 te 文件在 format_out 文件夹下。
注意:SELinux 配置文件末尾都要空一格。
日志可能被冲掉了,没取到关键 AVC 日志可以重来一次。

同一个 (scontext, tcontext, tclass) 的拒绝只会在 首次 产生 AVC 日志。后续相同访问命中缓存, 不再打印 。
调整AVC缓存大小:
sudo sysctl -w kernel.selinux_avc_cache_threshold=8192
六、编译与策略产物
单独编译命令:
./build.sh --product-name=rk3568 -T selinux_adapter --ccache
编译产物:
| 产物 | 设备路径 |
| 策略文件 | /etc/selinux/targeted/policy/policy.31 |
| 文件标签规则 | /etc/selinux/targeted/policy/file_contexts |
七、常见报错及解决
7.1 基线检查失败
Check "sadomain" baseline in user mode failed.
Violation list (type):
"voip_sa",
Solution: add the above list to "sadomain" field under "user" field in baseline file domain_baseline.json.
原因: voip_sa 域被声明了 sadomain 属性,但未在基线白名单中注册。
解决: 需要把 voip_sa 添加到 \base\security\selinux_adapter\sepolicy\whitelist\flex\domain_baseline.json 的 sadomain 数组中。报错就按日志提示添加上。
基线 vs 白名单(两者独立,互不替代):
| 文件 | 检查内容 | 用途 |
| domain_baseline.json | sadomain/hdfdomain/hap_domain 属性成员是否与基线一致 | 控制「哪些域属于哪个属性」 |
| domain_whitelist.json | 策略中允许缺失或允许冲突的域名列表 | 控制「哪些域可以不出现」 |
7.2 neverallow 规则冲突
[OHOS ERROR] [NINJA] (neverallow domain default_service (samgr_class (add add_remote get get_remote list)))
[OHOS ERROR] [NINJA] <root>
[OHOS ERROR] [NINJA] allow at .../system.cil:22635
[OHOS ERROR] [NINJA] (allow debug_hap default_service (samgr_class (get)))
[OHOS ERROR] [NINJA]
[OHOS ERROR] [NINJA] neverallow check failed at .../system.cil:6221 from .../domain.te:188
原因:
allow debug_hap default_service:binder { call transfer };
该策略违反了 neverallow 规则。SA 服务没有定义标签的默认是 default_service,此处允许 debug_hap 访问 default_service 这个定位太宽泛了,系统中已经有 neverallow 阻止访问 default_service,所以此策略和系统的 neverallow 冲突。
更多推荐

所有评论(0)