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 冲突。

Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐