OpenHarmony 6.1 LTS SELinux 原理、配置体系与约束边界源码分析

本文基于 ~/code/ohos/source/oh_6.1_lts 源码树分析。重点不是罗列策略语法,而是回答三个问题:SELinux 是什么;OpenHarmony 如何把策略配置、编译、装载和运行时检查串起来;它具体约束了哪些系统对象,以及 OpenHarmony 在标准 SELinux 之上开发了哪些适配能力。

目录


术语表

缩写 英文 本文含义
DAC Discretionary Access Control 基于 UID、GID 和文件 mode 位的自主访问控制
MAC Mandatory Access Control 由系统策略强制执行、进程自身不能绕过的访问控制
LSM Linux Security Modules Linux 内核安全钩子框架,SELinux 通过它参与内核访问判定
SID Security Identifier 内核内部表示安全上下文的标识符
AVC Access Vector Cache 缓存 SELinux 访问判定结果的内核缓存
TE Type Enforcement 以主体类型、客体类型、对象类别和权限为核心的策略模型
CIL Common Intermediate Language SELinux 策略中间表示
MCS Multi-Category Security 利用 category 对同类进程和资源进一步隔离的机制
SA System Ability OpenHarmony 系统能力服务
HDF Hardware Driver Foundation OpenHarmony 硬件驱动框架
HAP Harmony Ability Package OpenHarmony 应用安装包
APL Ability Privilege Level OpenHarmony 应用权限等级
FD File Descriptor 文件描述符,可通过 Binder 等 IPC 传递

注:术语按在本文中出现的先后顺序排列。


1. SELinux 是什么

1.1 从 DAC 到 MAC

Linux 传统权限首先是 DAC:内核根据进程 UID、GID、文件属主以及 rwx mode 位判断访问是否合法。它能回答“这个 UID 是否能读这个文件”,但存在两个结构性限制:

  • 高权限进程,尤其是 root,通常拥有过大的资源访问面。
  • 一个进程一旦被攻破,攻击者通常可以使用该进程原本拥有的全部 DAC 权限。

SELinux 在 DAC 之后增加 MAC。即使 UID、GID 和 mode 位允许访问,只要 SELinux 策略没有允许,访问仍会被拒绝。反过来,SELinux 的 allow 也不能绕过 DAC;两套检查必须同时通过。

因此,SELinux 的核心价值不是再做一套“用户权限”,而是把每个进程限制在完成职责所需的最小资源集合内。服务以 root 身份运行,不再等于它可以读取任意文件、打开任意设备或调用任意系统服务。

1.2 SELinux 的判定模型

一次典型访问可抽象为:

主体进程
  -> 发起 open / ioctl / binder call / mount / signal 等操作
  -> Linux 内核到达 LSM 安全钩子
  -> 将主体 SID、客体 SID、对象类别和请求权限交给 SELinux
  -> AVC 查询缓存;未命中时由安全服务器按已加载策略计算
  -> allow:继续后续内核流程
  -> deny:返回 EACCES/EPERM,并按配置输出 AVC 日志

一个判定至少包含四个维度:

维度 示例 说明
主体类型 codec_host 谁发起访问
客体类型 av_codec_service 被访问对象是什么安全类型
对象类别 binderfilechr_file 访问的是哪一类内核对象
权限 callreadwriteioctl 要执行什么操作

源码中的规则:

allow codec_host av_codec_service:binder { call transfer };

位于 base/security/selinux_adapter/sepolicy/ohos_policy/multimedia/av_codec/system/codec_host.te:14-15。它只允许 codec_hostav_codec_service 执行指定 Binder 操作,并不自动授予文件读写、设备访问或任意 FD 使用权。

1.3 安全上下文与类型强制

OpenHarmony 文档定义的安全上下文形式为:

user:role:type:sensitivity[:category,...]

常见进程标签:

u:r:codec_host:s0

常见文件或服务标签:

u:object_r:system_lib_file:s0
u:object_r:sa_render_service:s0

在工程实践中,最关键的是 type。大多数 TE 规则围绕主体 type 和客体 type 编写。userrole 在当前 OHOS 策略中相对固定;s0 是基础安全级别;应用隔离还可以通过 MCS category 扩展。

1.4 Enforcing 与 Permissive

OpenHarmony 支持两种运行模式:

模式 未授权访问 日志 适用场景
Enforcing 真正拦截 输出 AVC denied 产品运行和最终验证
Permissive 放行 输出 AVC denied,带 permissive=1 策略初期调试

Permissive 不是“策略正确”,只是“策略未执行拦截”。因此功能在 Permissive 下正常,不能证明 Enforcing 下也正常。


2. SELinux 在 OpenHarmony 安全体系中的位置

2.1 它保护的是内核可见资源

base/security/selinux_adapter/README.md:3-5 将该部件定位为对文件、参数、服务等系统资源提供 MAC 保护,并通过 neverallow 限制高危操作。OpenHarmony 官方概述进一步列出参数、SA、HDF、应用标签、策略与 contexts 编译加载等能力,见 docs/zh-cn/device-dev/subsystems/subsys-security-selinux-overview.md:3-15

从源码看,SELinux 的覆盖面包括:

  • 进程、线程和进程域转换;
  • 文件、目录、符号链接、设备节点;
  • 文件系统挂载、卸载、重挂载和 relabel;
  • Binder 调用、Binder 管理者和 FD 使用;
  • Socket、网络协议族和 ioctl;
  • SA 注册、查询、远端服务操作;
  • HDF 服务注册与查询;
  • 系统参数设置;
  • 应用进程与应用数据目录;
  • ptrace、signal、capability、可执行内存等高危能力。

2.2 它与 UID、文件权限和 AccessToken 的关系

三者不是替代关系,而是不同层次的联合约束:

机制 主要身份 主要检查位置 典型问题
UID/GID/DAC Linux 用户和组 VFS、IPC 等传统内核路径 该 UID 是否有文件 mode 权限
AccessToken 应用/服务的权限令牌 系统服务接口入口 调用方是否声明并获授某个 OHOS 权限
SELinux 安全上下文中的 type/category 内核 LSM + OHOS 服务检查适配层 该安全域是否能接触目标资源或执行该操作

例如,一个应用即使通过 AccessToken 获得了摄像头业务权限,也不意味着它能直接打开所有摄像头设备节点。正常路径仍应经过 Camera SA/HDF,由 SELinux 限制应用直接接触底层设备的能力。

2.3 OpenHarmony 的主体分类

官方概述把进程主体分为 Native、应用、SA、HDF 四类。策略中又通过 attribute 建立类别约束:

主体类别 常用 attribute 典型孵化者 配置入口
Native 系统进程 native_system_domain init 服务 cfg 的 secon + type.te
Native 芯片进程 native_chipset_domain chipset_init 芯片侧 cfg + vendor/public 策略
SA 进程 sadomain init / SA 主进程 secon + SA 策略
HDF 进程 hdfdomain init / chipset_init secon + HDF 策略
HAP 应用进程 hap_domain 及 APL attribute appspawn sehap_contexts + type.te

这种分类不是文档标签。base/security/selinux_adapter/sepolicy/base/public/domain.te:146-149neverallow 强制进程转换目标必须属于认可的域类别,避免开发者创建一个脱离全局约束体系的孤立 domain。


3. OpenHarmony SELinux 的源码组成

3.1 上游 SELinux 基础能力

third_party/selinux/ 提供标准 SELinux 用户空间基础:

模块 作用
third_party/selinux/libselinux/ 上下文查询、setconrestorecon、策略装载和访问检查 API
third_party/selinux/libsepol/ 策略数据库及 CIL 处理基础
third_party/selinux/checkpolicy/ 将传统 TE 配置编译为 CIL
third_party/selinux/secilc/ 将 CIL 链接、检查并生成二进制策略

Linux 内核侧实现位于 kernel/linux/linux-5.10/security/selinux/kernel/linux/linux-6.6/security/selinux/,具体取决于产品内核版本。

3.2 OpenHarmony 适配层

base/security/selinux_adapter/ 是 OHOS 侧的核心仓。其职责可分为四层:

层次 关键路径 作用
策略 base/security/selinux_adapter/sepolicy/ 定义 OHOS 的 domain、type、attribute、allow 和 neverallow
构建 base/security/selinux_adapter/scripts/ 聚合多来源策略,生成 CIL、二进制策略和 contexts
运行库 base/security/selinux_adapter/framework/policycoreutils/ 策略加载、文件 restorecon、HAP 标签、参数和服务权限检查
工具/API base/security/selinux_adapter/framework/tools/interfaces/ restorecon、检查工具和其他部件使用的 inner API

base/startup/init/services/modules/selinux/ 则把适配层接到 PID 1 的启动流程中,负责早期策略加载、服务进程上下文和文件标签恢复。

3.3 策略目录的分层

构建脚本按目录名识别 publicsystemvendor

  • public:System 与 Vendor 都可见的稳定策略接口;
  • system:系统侧私有实现策略;
  • vendor:芯片/厂商侧私有策略;
  • base/te:全局基础 type、class 和宏定义;
  • min:生成策略所需的最小公共集合。

源码依据在 base/security/selinux_adapter/scripts/build_policy_api.py:315-348:脚本扫描各策略根目录中的 public/system/vendor,然后分别构造 system、vendor 和 public 文件集合。

这意味着目录位置本身具有接口边界语义。Vendor 策略若依赖 System 私有 type,会破坏分区独立演进能力;跨分区共享的标识应放在 public 层。

3.4 产品和芯片策略如何接入

默认策略根包括:

base/security/selinux_adapter/sepolicy/base
base/security/selinux_adapter/sepolicy/ohos_policy

产品通过 selinux_adapter_build_path 追加策略根,多个路径以冒号分隔。以源码中的 wukong100 为例:

"selinux_adapter_build_path = base/security/selinux_adapter/sepolicy/ohos_product:device/soc/unisoc/p7885/sepolicy/base:device/soc/unisoc/p7885/sepolicy/ext_policy:device/soc/unisoc/p7885/sepolicy/ohos_policy"

位置:vendor/revoview/wukong100/config.json:18-25

另一产品 bq3588 追加 vendor/bearkey/bq3588/sepolicy/basevendor/bearkey/bq3588/sepolicy/ohos_policy,见 vendor/bearkey/bq3588/config.json:17-23

因此,OHOS 的策略不是只集中在一个目录,而是由基础策略、通用子系统策略、产品策略、SoC 策略和 Vendor 策略共同聚合。分析某个最终权限时,必须检查产品实际配置的全部策略根。


4. 策略如何从源码变成内核运行规则

4.1 构建开关与产品入口

产品首先通过:

"build_selinux": true

启用 SELinux 构建。base/security/selinux_adapter/BUILD.gn:1223-1258 只有在 build_selinux 为真时才准备宿主侧工具链依赖;BUILD.gn:1519-1602selinux_group 再汇总策略、contexts、工具和检查任务。

主要 GN 参数定义于 base/security/selinux_adapter/selinux.gni:18-36

参数 默认值 作用
selinux_adapter_build_path default 追加产品、芯片、厂商策略目录
selinux_adapter_components default 选择单体、system 或 vendor 分离构建
selinux_adapter_vendor_policy_version 40 System/Vendor 策略兼容版本
selinux_adapter_support_developer_mode false 是否安装开发者模式策略产物
selinux_adapter_enforce true 生成 enforcing 或 permissive 配置
selinux_adapter_mcs_enable true 是否编译 HAP MCS 支持

4.2 TE 策略编译链

base/security/selinux_adapter/BUILD.gn:431-536 定义 build_policy,调用 scripts/build_policy.py,并依赖宿主侧 checkpolicysecilc

实际链路为:

security_classes / access_vectors / attributes / *.te / users / virtfs_contexts 等
  -> m4 展开宏和 build_with_debug、build_with_updater、build_with_developer 条件
  -> checkpolicy -M -C -c 31
  -> system.cil / vendor.cil / public.cil
  -> secilc -m -M true -G -c 31 -O
  -> policy.31

关键源码:

  • 策略输入类型列表:scripts/build_policy_api.py:31-47
  • m4 展开:scripts/build_policy_api.py:134-146
  • checkpolicy 生成 CIL:scripts/build_policy_api.py:149-154
  • secilc 生成二进制策略:scripts/build_policy_api.py:290-298

secilc 默认执行 neverallow 检查。构建阶段若某条 allow 与全局 neverallow 冲突,策略无法生成,这比运行后再发现风险更早。

4.3 Contexts 编译链

策略回答“某 type 能做什么”,contexts 回答“现实对象对应哪个 type”。BUILD.gn:619-677build_contextsbuild_policy 之后执行,生成:

产物 映射对象
file_contexts / file_contexts.bin 文件路径到文件 type
sehap_contexts APL、包名、调试属性到 HAP 进程和数据 type
service_contexts SA ID/名称到 SA 服务 type
hdf_service_contexts HDF 服务名到 HDF 服务 type
parameter_contexts 参数名或参数前缀到参数 type

scripts/build_contexts.py:314-375 同样聚合基础策略、OHOS 通用策略和产品追加目录,并按 public/system/vendor 组合。脚本还用 sefcontext_compile -p policy.31 验证 contexts 中引用的上下文确实存在于策略中,见 scripts/build_contexts.py:166-181

4.4 System 与 Vendor 策略分离和兼容

selinux_adapter_components 不是 default 时,构建脚本会产生:

  • system.cil
  • vendor.cil
  • public.cil
  • compatible/<version>.cil
  • System CIL 哈希与 Vendor 预编译策略对应哈希;
  • Vendor 侧可直接装载的预编译 policy.31

build_policy_api.py:431-494 会给 public 类型生成版本化映射,并输出兼容 CIL。其设计目的与单纯“拆文件”不同:System 和 Vendor 可以基于稳定 public 类型接口独立交付,启动时再判断预编译策略是否仍与当前 System CIL 匹配。

4.5 编译期安全门禁

OHOS 构建不仅检查语法,还执行 selinux_checkBUILD.gn:789-846 将普通策略、开发者策略、策略目录和检查配置交给 scripts/selinux_check/selinux_check_main.py

从配置目录和白名单可以看到额外检查维度:

  • permissive domain 白名单;
  • 权限组白名单;
  • ioctl xperm 白名单;
  • domain 基线与白名单;
  • parameter 基线与白名单;
  • socket 和虚拟文件系统规则;
  • file_contexts type attribute 约束。

这说明 OHOS 的策略治理不只依靠 SELinux 编译器,还增加了项目级策略规范和增量基线门禁。


5. 系统启动时如何加载和启用 SELinux

5.1 init 的装载时机

标准系统 init 在读取普通服务 cfg 之前加载策略:

PluginExecCmdByName("preLoadSelinuxPolicy", "");
PluginExecCmdByName("loadSelinuxPolicy", "");

位置:base/startup/init/services/init/standard/init.c:326-361。源码特别标注 Do not move position!,表明这是启动安全边界,而不是可随意后移的普通初始化动作。

SELinux init 插件在 base/startup/init/services/modules/selinux/selinux_adp.c:169-183 注册 loadSelinuxPolicysetServiceContentsetSockCreateCon 和多个 restorecon 命令。

5.2 策略文件选择与回退

base/security/selinux_adapter/framework/policycoreutils/src/load_policy.cpp:384-415 的选择逻辑为:

  1. Updater 模式直接使用 /system/etc/selinux/targeted/policy/policy.31
  2. 若没有 System CIL,使用 System 内置普通或开发者二进制策略;
  3. 若 System CIL 哈希与 Vendor 预编译策略记录的哈希一致,加载 Vendor 预编译策略;
  4. 否则在启动阶段调用 /system/bin/secilc,将 System、Vendor、Public 和兼容 CIL 重新链接为 /dev/policy.31
  5. mmap 策略文件并通过 security_load_policy() 送入内核。

这个机制兼顾启动速度和分区升级兼容:匹配时走预编译快路径,不匹配时用 CIL 现场重编译避免装载过期策略。

5.3 Enforcing 配置优先级

load_policy.cpp:147-205 给出的优先级是:

内核命令行 /proc/cmdline 中的 enforcing=
  > /system/etc/selinux/config
  > 都不可用时回退为 0,即 permissive

构建时,BUILD.gn:848-855 根据 selinux_adapter_enforce 选择 config.enforceconfig.permissive;Updater 固定使用 enforcing 配置,见 BUILD.gn:857-864

策略加载前,load_policy.cpp:207-219 先设置 selinuxfs 路径和 enforcing 状态,再调用 security_load_policy()

5.4 服务进程域切换

init 解析服务 cfg 时读取 secon 字段:

  • selinux_static.c:30-37secon 保存到服务扩展数据;
  • 服务启动前,init_common_service.c:437-449 调用 setServiceContent
  • selinux_adp.c:71-90 使用 setexeccon(label) 设置下一次 exec 的安全上下文。

如果 cfg 没有 secon,插件使用:

u:r:limit_domain:s0

domain.te:192-194 又用 neverallow 禁止 limit_domain 访问文件和 Binder。因此“忘记配置进程标签”不是让进程继承一个宽松默认域,而是进入一个基本不可工作的限制域。

5.5 文件标签恢复

策略装载完成后,init 插件会递归恢复 /dev 标签,见 selinux_adp.c:44-68。init 的 mkdir 也集成 restorecon:init_common_cmds.c:342-372 在创建目录后调用 restoreContentRecurse

这体现了 SELinux 配置的两个阶段:

  1. file_contexts 定义路径应当对应什么标签;
  2. 镜像制作、init 或业务代码通过 xattr/restorecon 把标签真正写到 inode 上。

只有第一步而没有第二步,读写分区中新建对象仍可能继承父目录标签,无法获得预期的独立 type。


6. OpenHarmony 中如何配置 SELinux

6.1 配置一个 Native、SA 或 HDF 进程域

完整配置不是只写一条 allow,而是先建立合法身份。

第一步,在服务 cfg 中指定安全上下文:

{
  "services": [{
    "name": "demo",
    "path": ["/system/bin/demo"],
    "uid": "demo",
    "gid": ["demo"],
    "secon": "u:r:demo:s0"
  }]
}

第二步,在 type.te 中定义 type 并归入正确 attribute:

# init 孵化的 Native 系统进程
type demo, native_system_domain, domain;

# chipset_init 孵化的芯片进程
type demo, native_chipset_domain, domain;

# SA 进程
type demo, sadomain, domain;

# HDF 进程
type demo, hdfdomain, domain;

第三步,按真实业务调用链添加最小 allow,而不是先授予通用目录、设备和 Binder 全权限。

官方配置依据:docs/zh-cn/device-dev/subsystems/subsys-security-selinux-sample-domain.md:3-86

6.2 配置普通文件和动态数据文件

只读镜像中的文件通常在镜像构建阶段标记:

/system/lib(/.*)?    u:object_r:system_lib_file:s0

同时定义 type:

type system_lib_file, system_file_attr, file_attr;

读写分区的动态文件同样先在 file_contexts 建立映射:

/data/service/el0(/.*)?    u:object_r:data_service_el0_file:s0
type data_service_el0_file, file_attr, data_file_attr;

然后必须在创建后调用 restorecon,或让 init 的 mkdir 路径触发恢复。官方说明位于 subsys-security-selinux-sample-file.md:19-47

6.3 配置虚拟文件系统对象

/proc/sys 等虚拟文件系统对象不适合按普通磁盘路径写 xattr,使用 virtfs_contexts 中的 genfscon

genfscon proc /iomem u:object_r:proc_iomem_file:s0

并定义:

type proc_iomem_file, fs_attr, proc_attr;

该配置会进入策略编译输入,因为 virtfs_contexts 已列入 SEPOLICY_TYPE_LIST,见 build_policy_api.py:31-47

6.4 配置系统参数

parameter_contexts 支持固定名和前缀匹配:

# 前缀匹配
init.svc. u:object_r:init_svc_param:s0

# 精确匹配
const.secure u:object_r:secure_param:s0

再定义参数 type:

type init_svc_param, parameter_attr;

前缀匹配使用最合适的最长前缀。未标记参数会落到 default_param,而 domain.te:183-184 禁止 domain 对 default_param:parameter_service 执行任何操作,迫使新增参数显式分类。

6.5 配置 SA 和 HDF 服务

SA 服务在 service_contexts 中建立映射:

10 u:object_r:sa_render_service:s0
type sa_render_service, sa_service_attr;

HDF 服务在 hdf_service_contexts 中建立映射:

thermal_interface_service u:object_r:hdf_thermal_interface_service:s0
type hdf_thermal_interface_service, hdf_service_attr;

未配置 SA 会落入 default_service;未配置 HDF 会落入 default_hdf_servicedomain.te:175-190 通过 neverallow 禁止访问未正确分类的服务,也强制 SA/HDF 服务 type 分别归属 sa_service_attrhdf_service_attr

6.6 配置应用进程与应用数据目录

系统应用若需要独立域,在 sehap_contexts 中按 APL 和包名映射:

apl=normal name=com.ohos.permissionmanager domain=permissionmanager_hap type=permissionmanager_hap_data_file

并定义进程和数据 type:

type permissionmanager_hap, normal_hap_attr, hap_domain, domain;
type permissionmanager_hap_data_file, normal_hap_data_file_attr, hap_file_attr, data_file_attr, file_attr;

APL 与 attribute 的对应关系为:

APL 进程 attribute 数据 attribute
normal normal_hap_attr normal_hap_data_file_attr
system_basic system_basic_hap_attr system_basic_hap_data_file_attr
system_core system_core_hap_attr system_core_hap_data_file_attr

源码中的 hap_restorecon.cpp:363-400 加载 sehap_contexts 并建立查找树;hap_restorecon.cpp:676-731 为应用进程执行 setconhap_restorecon.cpp:434-530581-604 负责应用数据文件 relabel。

6.7 编写 allow 规则的正确方法

规则语法:

allow <source_type> <target_type>:<class> { <permission...> };

例如:

allow codec_host av_codec_service:fd use;
allow codec_host av_codec_service:binder { call transfer };

正确过程是从真实拒绝和调用链拆解权限:

  1. 确认主体域是否正确;
  2. 确认客体标签是否正确,不能先对 unlabeleddefault_servicedev_file 放权;
  3. 确认对象类别;
  4. 确认权限是否为业务必需;
  5. 检查是否存在更合适的公共宏,如 binder_call()
  6. 确认规则不与 neverallow 冲突;
  7. Enforcing 下执行真实路径验证。

7. SELinux 在 OpenHarmony 中约束了哪些东西

7.1 进程启动、域转换和进程间影响

SELinux 决定服务能否进入目标域,以及域之间是否能执行 transition、dyntransition、signal、ptrace 等操作。

domain.te:143-149 强制所有进程转换目标进入认可的 domain 分类;hap_domain.te:159-164 禁止应用 ptrace 非应用域,也禁止应用向非应用域发送 sigkillsigstop 和普通 signal。

这类约束解决的是:即使某进程拥有高 UID 或拿到了目标 PID,也不能随意调试、终止或接管其他安全域。

7.2 文件、目录、设备节点与文件系统

SELinux 约束:

  • 文件和目录的读、写、创建、删除、重命名、映射、执行;
  • 字符设备和块设备的 open/read/write/ioctl;
  • mount、remount、unmount、relabel、quota;
  • /proc/sys、debugfs、tracefs 等虚拟文件系统;
  • 文件是否可作为程序入口点或动态库映射。

domain.te:131-141 限制只有被归入认可可执行 type 的文件才能 execute/entrypoint;domain.te:228-237 对文件系统挂载和 relabel 做全局限制;domain.te:284-287 禁止普通域写 rootfs、system 和 vendor 只读文件类型。

7.3 Binder 与文件描述符传递

OpenHarmony IPC 使用 Binder 时,至少可能涉及:

对象类别 权限 含义
binder call 向目标进程发起 Binder 调用
binder transfer 传递 Binder 引用
fd use 接收方使用来自目标域的 FD
binder set_context_mgr 成为 Binder context manager

基础策略允许 domain 打开 Binder 设备,见 domain.te:44-49,但具体域之间的 Binder 调用仍需单独授权。domain.te:106-107 规定只有 samgr 能成为 Binder manager。

FD 传输特别容易被误解:允许 Binder call/transfer 不等于允许使用随 Parcel 传来的 FD。真实媒体链路中,若 codec_host 接收 av_codec_service 传来的 FD,需要类似:

allow codec_host av_codec_service:fd use;

这正是 codec_host.te:14 的规则。缺失时,Binder 事务可能在携带 FD 的特定方向失败,而不携带 FD 的普通接口仍然正常。

7.4 SA 服务发现与注册

OHOS 为 SA 定义 samgr_class,常用权限包括:

  • add:注册本地 SA;
  • get:获取本地 SA;
  • list:枚举;
  • add_remote / get_remote:远端 SA 操作。

service_checker.cpp:242-272 将调用方 context、服务 context、samgr_class 和动作交给 selinux_check_access()。因此 SA 权限不是普通文件权限的别名,而是 OHOS 用户空间服务框架主动接入 SELinux 决策的扩展对象类别。

7.5 HDF 服务发现与注册

HDF 使用 hdf_devmgr_class,通过 HdfListServiceCheckHdfGetServiceCheckHdfAddServiceCheck 进入同一套 ServiceChecker,见 service_checker.cpp:62-84194-224

HDF 不支持 SA 的远端服务动作;service_checker.cpp:285-300 对 HDF 的 get_remote/add_remote 直接拒绝。

7.6 系统参数读写

参数服务并非只靠参数命名规范。param_checker.c:82-99 通过连接 socket 获取对端安全上下文,再由 param_checker.c:60-72 检查:

source_context -> target_parameter_context : parameter_service set

因此,调用方即使能连接参数服务,也不一定能设置目标参数。参数必须先在 parameter_contexts 分类,再为合法域授予 set

7.7 Socket、网络与高危 ioctl

策略可以约束:

  • Unix domain socket 的创建、连接、读写和 connectto
  • TCP、UDP、raw IP、Netlink;
  • Socket 文件路径;
  • 具体 ioctl 命令号。

domain.te:117-125 禁止危险的 TIOCSTI ioctl,并禁止 System V IPC;hap_domain.te:111-152 对应用可打开的字符设备和 ioctl 命令范围做细粒度限制。

这比“允许访问某设备节点”更细:即使允许 open,也可以只开放该驱动 ABI 中经过审核的 ioctl 编号。

7.8 应用之间及应用与系统之间的隔离

hap_domain.te 对 HAP 做了系统性约束:

  • 应用默认不能获得 Linux capability,见 hap_domain.te:91-93
  • 不能修改非应用域文件,见 hap_domain.te:95-108
  • 不能任意打开设备节点,见 hap_domain.te:110-153
  • 不能 ptrace 或 signal 非应用域,见 hap_domain.te:159-164
  • 不能写 rootfs 和 system 文件,见 hap_domain.te:166-170
  • normal、system_basic、system_core 应用数据目录之间按 attribute 隔离,见 hap_domain.te:198-219

这使 APL 不只是上层权限等级,也参与底层进程域和数据类型分类。

7.9 调试能力与高危内核能力

全局策略限制:

  • 谁能 setenforce 或重新加载策略;
  • 谁能 ptrace;
  • 谁能使用可执行堆栈、可执行堆、mmap_zero
  • 谁能访问 debugfs、块设备、内核日志;
  • 谁能挂载文件系统;
  • 哪些域能使用 root 调试工具和 /data/local/tmp

domain.te:199-208 禁止普通域修改 SELinux 状态、执行可执行栈/堆;domain.te:294-297 限制 execmem 和 ptrace。


8. OpenHarmony 在标准 SELinux 之上开发了什么

8.1 init 服务 secon 接入

标准 SELinux 提供 setexeccon(),但不会自动理解 OHOS 服务 cfg。OHOS 在 init 中开发了:

  • secon 字段解析;
  • 服务扩展数据保存;
  • fork/exec 前设置进程上下文;
  • 未配置时落入 limit_domain 的 fail-closed 行为;
  • socket 创建上下文和多个 restorecon 命令。

关键实现位于 base/startup/init/services/modules/selinux/selinux_static.cselinux_adp.c

8.2 SA 与 HDF 服务权限类

文件、进程、Socket 是内核天然对象,而 SA/HDF 是 OHOS 框架对象。OHOS 开发了 libservice_checker

  • 读取 System/Vendor 的服务 contexts;
  • 为未标记服务分配 default type;
  • 获取调用方安全上下文;
  • add/get/list/add_remote/get_remote 映射为 SELinux class permission;
  • 输出带服务名和 SID 的审计信息。

构建目标见 base/security/selinux_adapter/BUILD.gn:299-320,实现见 framework/policycoreutils/src/service_checker.cpp

8.3 参数服务权限检查

OHOS 开发了参数 context 查找、共享内存映射和 SetParamCheck 接口。参数服务通过 peer context 对 parameter_service:set 做访问判定,而不是简单依赖 UID。

构建目标 libparaperm_checker 位于 BUILD.gn:277-297;核心检查位于 param_checker.c:60-99

8.4 HAP 动态标签与 APL 映射

普通文件可由路径匹配标签,但应用进程需要综合 APL、包名、debuggable、extension、隔离类型和 UID。OHOS 为此开发 libhap_restorecon

  • 解析 sehap_contexts
  • 用 Trie 按 APL 和多个可选属性匹配;
  • 为 appspawn 子进程选择 domain;
  • 为应用数据目录选择 file type;
  • 支持 DLP、自定义沙箱、输入隔离、GPU/Render 隔离等 extra 标志;
  • 在安装、迁移或恢复时递归 relabel 应用数据。

构建目标见 BUILD.gn:136-172,匹配逻辑见 hap_restorecon.cpp:200-360734-800

8.5 MCS 应用隔离

selinux_adapter_mcs_enable 默认开启,并在 BUILD.gn:165-167libhap_restorecon 增加 MCS_ENABLE

OHOS 可根据 appId、userId 或二者共同生成 category:

  • levelFrom=app
  • levelFrom=user
  • levelFrom=all

相关偏移与计算基础定义于 hap_restorecon.cpp:86-103。这样即使两个应用同属 normal_hap type,也可以通过不同 MCS category 进一步区分应用实例和用户空间。

8.6 System/Vendor 可演进策略机制

OHOS 开发了基于 public CIL 版本化、兼容 CIL、哈希匹配和启动时重编译的分区策略机制:

构建阶段:System/Public/Vendor TE -> 各自 CIL + 兼容映射 + 哈希 + 可选预编译 policy.31
启动阶段:比较 System CIL 哈希
  -> 匹配:加载 Vendor 预编译 policy.31
  -> 不匹配:组合当前 System/Vendor/Public/compatible CIL,现场 secilc

核心实现分别位于 build_policy_api.py:431-494load_policy.cpp:324-415

8.7 开发者模式双策略

构建脚本同时生成普通策略和 developer 策略:build_policy_api.py:497-534 使用两个线程分别编译。若产品开启 selinux_adapter_support_developer_mode,镜像会安装 system_developer.cilvendor_developer.cildeveloper_policy 等产物,见 BUILD.gn:1139-1221

启动时 load_policy.cpp:358-375 读取 /proc/dsmm/developer 判断是否启用 developer 策略。这样调试放宽可以被限定在受控开发者模式中,而不是永久污染产品策略。

8.8 OHOS 策略质量检查器

除上游 checkpolicy/secilc 外,OHOS 还开发了面向工程规范的检查脚本和白名单体系。它约束的不只是语法正确性,还包括策略扩张是否越过项目基线,例如:

  • 新增 permissive 域;
  • 扩大高危权限组;
  • 新增 ioctl 范围;
  • domain 和参数分类是否满足基线;
  • 文件 type 是否加入正确 attribute;
  • 特定 socket、virtfs 规则是否合规。

这层能力的本质是“对 SELinux 策略本身再做一次安全治理”。


9. 开发实践与常见误区

9.1 一次完整的新增权限流程

建议按以下顺序执行:

  1. 明确业务调用链和最小资源集合;
  2. 确认主体进程已有独立且正确的 domain;
  3. 为新文件、参数、SA、HDF 或应用数据定义专用 type;
  4. 将 type 加入正确 attribute;
  5. 在对应 contexts 中建立对象到 type 的映射;
  6. 确保标签真正写入对象或在运行时可被 checker 查到;
  7. 在 Permissive 中执行完整真实路径并收集 AVC;
  8. 对每条 AVC 验证主体、客体、class、permission 是否符合设计;
  9. 添加最小 allow 或复用经过审核的宏;
  10. 运行策略编译、neverallow 和 selinux_check
  11. 切到 Enforcing,重新执行真实场景;
  12. 检查是否出现新的旁路、过宽 attribute 或无关权限。

9.2 AVC 日志应当怎样分析

典型日志:

avc: denied { open } for pid=1658 comm="setenforce"
path="/sys/fs/selinux/enforce"
scontext=u:r:hdcd:s0
tcontext=u:object_r:selinuxfs:s0
tclass=file permissive=1

分析顺序:

字段 要回答的问题
scontext 发起访问的进程是否运行在预期域
tcontext 客体是否被正确标记,还是落入 default/unlabeled/通用 type
tclass 这是 file、fd、binder、samgr_class 还是其他对象
denied permission 业务是否真的需要该动作
调用方向 谁把资源交给谁,尤其关注 Binder FD 的生产者和消费者
permissive 当前是否只是记录而未拦截

只有上述问题都确认后,才能把它翻译为候选 allow

9.3 为什么不能直接照抄 audit2allow

自动生成工具只知道“发生了拒绝”,不知道:

  • 客体是否贴错标签;
  • 调用是否应走受控 SA 而不是直连设备;
  • 该权限是否只在调试路径出现;
  • 是否可以缩小到更具体的 type;
  • 是否违反 OHOS 全局 neverallow 和架构边界;
  • 一条拒绝是否是上游错误造成的二次症状。

如果 tcontextunlabeleddefault_servicedefault_hdf_servicedefault_paramdev_file 或过于宽泛的 data_file,第一选择通常应是修标签,而不是放开该通用 type。

9.4 为什么只写 file_contexts 不会自动生效

file_contexts 是“期望标签数据库”,不是持续监控文件系统的守护进程。只读镜像文件可在制镜像时标记;运行时新建文件通常继承父目录标签,必须:

  • 由 init 的集成逻辑触发 restorecon;
  • 业务创建后调用 restorecon API;
  • 或在调试时执行 restorecon <path>

否则策略写得正确,inode 上的实际 xattr 仍然错误。

9.5 为什么不能把 default 类型放开

OHOS 有意让漏配对象 fail closed:

漏配对象 默认 type 全局约束
init 服务进程 limit_domain 禁止文件与 Binder 访问
SA default_service 禁止 samgr_class 操作
HDF 服务 default_hdf_service 禁止 hdf_devmgr_class 操作
参数 default_param 禁止 parameter_service 操作
未标记文件 unlabeled 原则上禁止普通 domain 访问

放开 default type 会让所有未来漏配对象共享权限,破坏“新增对象必须显式分类”的制度性保障。

9.6 调试和验证命令

# 查看当前模式
getenforce

# 调试切换;产品环境是否允许还受 SELinux 自身规则约束
setenforce 0
setenforce 1

# 查看进程标签
ps -eZ
ps -efZ

# 查看文件标签
ls -lZ /path

# 按 file_contexts 恢复标签
restorecon /path

# 查看内核 AVC
hilog | grep -i "avc:.*denied"
dmesg | grep -i "avc:.*denied"

验证不能只看进程是否存活,还应覆盖真实资源方向。例如 Binder FD 问题必须验证目标方向的真实 FD 传输,并同时记录生产者/消费者安全域、AVC、内核 Binder trace 和持续运行结果。


10. 与 Android SELinux 的关键差异

两者都基于 Linux SELinux、TE、CIL、file_contexts、应用域和 System/Vendor 分离思想,但 OHOS 的框架对象不同。

维度 OpenHarmony 6.1 LTS Android
系统服务目录 samgr + samgr_class + service_contexts servicemanager + service_manager class
驱动服务目录 HDF + hdf_devmgr_class + hdf_service_contexts 常见为 hwservicemanager/vndservicemanager 等体系
应用标签输入 sehap_contexts,结合 APL、包名、debuggable、extension、extra seapp_contexts,结合 UID、seinfo、包名等
参数控制 OHOS 参数服务 + parameter_contexts + parameter_service:set Android property service + property_contexts
init 服务标签 cfg 中 secon,缺失进入 limit_domain init .rcseclabel 或 domain transition
应用权限上层 AccessToken/APL Android permission/AppOps/SELinux
策略检查扩展 OHOS selinux_check 基线与白名单 Android sepolicy tests、neverallow 等
开发者策略 可选普通/developer 双 CIL 与运行时选择 通常按 build variant 和调试域组织,机制不同

不能直接把 Android type 名、服务类别和应用标签配置复制到 OHOS。可复用的是“最小权限、显式标签、System/Vendor 边界和 neverallow”原则,不是具体策略实体。


11. 注意事项与 FAQ

11.1 注意事项

  1. 不要以旧 README 中“仅支持 RK3568”的描述判断 6.1 LTS 当前产品覆盖。源码中的 wukong100bq3588 等产品已经显式启用 build_selinux;README 该句明显具有历史性,分析应以当前产品配置和构建代码为准。
  2. selinux_adapter_enforce=true 决定镜像内配置,但 /proc/cmdlineenforcing= 优先级更高。
  3. build_variant=root 不等于关闭 SELinux。BUILD.gn:431-438 只是让策略 m4 使用 debug 宏;enforcing 仍由单独参数控制。
  4. System/Vendor 分离构建时,不要让 Vendor 私有策略直接依赖 System 私有 type;跨分区依赖应经过 public/versioned CIL。
  5. allow domain xxx:* *、大范围 attribute 放权和通配 ioctl 会显著扩大所有成员域攻击面。
  6. 应用业务权限通过不等于底层 SELinux 通过;反之 SELinux 放行也不等于 AccessToken 授权通过。
  7. 修改策略后必须重新编译相关策略产物并确认镜像中实际文件已更新,不能只替换源码 .te
  8. AVC 消失不一定表示修复正确,也可能被 dontaudit、错误标签、未执行真实路径或 Permissive 掩盖。

11.2 FAQ

Q1:为什么服务 cfg 没写 secon 后进程启动失败?

因为 init 会把它放入 u:r:limit_domain:s0,全局 neverallow 禁止该域访问文件和 Binder。这是故意的 fail-closed 设计。

Q2:为什么新增 SA 后所有进程都无法获取?

优先检查 service_contexts。未映射 SA 会成为 default_service,全局策略禁止访问。之后再检查调用方是否有目标 SA 的 samgr_class get

Q3:为什么文件路径已经写入 file_contextsls -Z 仍是旧标签?

因为动态文件不会自动刷新 xattr。执行 restorecon,或在创建代码中接入 restorecon。

Q4:为什么 Binder 普通调用成功,但携带 Surface/共享内存 FD 时失败?

Binder call/transferfd use 是不同权限。检查接收方是否有权使用发送方传来的 FD,并确认拒绝发生的真实方向。

Q5:能否为解决问题直接把某个 domain 设为 permissive?

只可作为短期诊断手段,并且会受到 permissive 白名单检查。最终必须在全局 Enforcing 和目标域 Enforcing 下验证。

Q6:为什么新增 allow 在编译时被拒绝?

通常是与 neverallow 冲突。不要绕过 neverallow;应检查架构路径、对象标签和 domain 分类是否设计错误。

Q7:开发者模式是否意味着可以加载任意宽松策略?

不是。developer 策略仍经过同一编译和 neverallow 流程,只是 m4 可包含受控的 developer_only() 规则,并由 DSMM 状态选择。


12. 总结

OpenHarmony 6.1 LTS 的 SELinux 不是一个孤立的内核开关,而是一条贯穿构建、镜像、启动、服务框架和应用生命周期的安全链:

阶段 核心动作 关键产物或接口
身份设计 为进程和资源定义 type/attribute type.te、公共 attribute
对象映射 将现实对象绑定到安全上下文 file_contextssehap_contextsservice_contextshdf_service_contextsparameter_contexts
权限设计 声明最小 allow,设置全局禁止边界 *.te、宏、neverallow
构建 m4、checkpolicy、CIL、secilc、contexts 校验 policy.31、CIL、contexts、哈希和兼容映射
启动 init 选择策略、设置模式并加载内核 LoadPolicy()security_load_policy()
运行 内核 LSM 和 OHOS checker 执行访问判定 AVC、selinux_check_access()、Binder/VFS/Socket 安全钩子
应用隔离 按 APL、包名、UID 和 MCS 选择进程/数据标签 libhap_restoreconsehap_contexts
策略治理 neverallow、基线与白名单阻止权限无序扩张 selinux_check

一句话概括:

SELinux 在 OHOS 中负责把“某个进程理论上能接触什么”变成内核和系统框架共同强制执行的白名单;OHOS 的主要开发工作,则是把 SA、HDF、系统参数、HAP/APL、MCS、init cfg 和 System/Vendor 演进机制接入这套白名单模型。

posted @ 2026-07-17 18:14  getmoon  阅读(20)  评论(0)    收藏  举报