OpenHarmony北向开发 SA服务SELinux权限配置一站式傻瓜式教程
SELinux是Security Enhanced Linux 的缩写,也就是安全强化的 Linux,旨在增强传统Linux操作系统的安全性,解决传统Linux系统中自主访问控制(DAC)系统中的各种权限问题(如root权限过高等)。这里举一个例子便于理解,假设系统中某个服务进程出现了一个漏洞,使得某个远程用户可以访问系统的敏感文件(如/etc/dev)。
Selinux权限配置
OpenHarmony中SELinux使用详解
目录
- SELinux简介
- SELinux概念
- SELinux模式
- OH中SELinux使用详解
- 新增SA服务如何配置SELinux权限
SELinux简介
SELinux是Security Enhanced Linux 的缩写,也就是安全强化的 Linux,旨在增强传统Linux操作系统的安全性,解决传统Linux系统中自主访问控制(DAC)系统中的各种权限问题(如root权限过高等)。这里举一个例子便于理解,假设系统中某个服务进程出现了一个漏洞,使得某个远程用户可以访问系统的敏感文件(如/etc/dev)。如果我们的Linux系统启用了SELinux,而查询SELinux策略得知,这个服务进程并不具备访问敏感文件(/etc/dev)的权限,所以这个远程用户通过这个进程访问敏感文件(/etc/dev)就会被SELinux所阻挡,起到保护Linux系统的作用。
SELinux基本概念
Linux操作系统的安全机制其实就是对两样东西做限制:进程和系统资源(文件、socket等)。linux操作系统是通过用户和组的概念来对我们的系统资源进行限制,每个进程都需要一个用户才能执行。在SELinux当中针对这两样东西定义了两个基本概念:域(domin)和上下文(context)。
我们可以通过ps -Z 命令来查看当前进程的域信息,也就是进程的SELinux信息:
**# ps -Z
LABEL PID TTY TIME CMD
u:r:sh:s0 10466 pts/1 00:00:00 sh
u:r:sh:s0 10468 pts/1 00:00:00 ps
通过ls -Z 命令,我们可以查看文件上下文信息,也就是文件的SELinux信息:
**# ls -Z
u:object_r:system_bin_file:s0 bin
u:object_r:rootfs:s0 chip_prod
u:object_r:vendor_file:s0 chipset
u:object_r:configfs:s0 config
u:object_r:data_file:s0 data
u:object_r:dev_file:s0 dev
u:object_r:system_etc_file:s0 etc
u:object_r:init_exec:s0 init
u:object_r:system_lib_file:s0 lib
SELinux的工作模式
下面通过这个图来说明,SELinux工作流程
可以从图中看出,需要关注的关键信息如下:
- 主体(Subject):指主动对其它实体施加动作的实体,例如进程、服务等。
- 策略(policy) :权限策略,通过在te文件中配置对应的权限语句实现,权限策略以type或attribute作为执行对象。
- 安全上下文(Security Context):实际上就是一个附加在主体或客体上的标签。
- 目标(Object):是被动接受其他实体访问的实体,例如文件、系统属性等。
SELinxu工作的核心就是安全上下文。安全上下文是一组和进程或对象有关的安全属性,每一个进程或对象都会记录一条安全上下文,将其作为SELinux判断进程是否能读取对象的依据。安全上下文分为“进程安全上下文”和“文件安全上下文”。一个“进程安全上下文”一般对应多个“文件安全上下文”。
只有两者的安全上下文对应上了,进程才能访问文件。它们的对应关系由策略中的规则决定。
文件安全上下文由文件创建的位置和创建文件的进程所决定。而且系统有一套默认值,用户也可以对默认值进行设定。需要注意的是,单纯的移动文件操作并不会改变文件的安全上下文。
SELinux有3种工作模式:
- 【enforcing】强制模式。违反 SELinux 规则的行为将被阻止并记录到日志中。
- 【permissive】宽容模式。违反 SELinux 规则的行为只会记录到日志中。一般为调试用。
- 【disabled】关闭 SELinux。
模式之间的临时切换,如需(【disabled】关闭)永久有效,还需要修改配置文件。
**# getenforce // 查看SELinux状态
Enforcing
**# setenforce 0 // 设置SELinux状态为宽容模式
**# getenforce
Permissive
**# setenforce 1 // 设置SELinux状态为强制模式
**# getenforce
Enforcing
OH中SELinux使用详解
SELinux在OH中应用整体来说,功能和以上介绍的使用方法并无太大差别,只是在原生SELinux内核中做了部分驱动和文件系统的适配。目前码云社区上master分支以RK3568为例子做了适配并开启了SELinux,其他型号的设备需要自己做适配。
OpenHarmony SELinux的主代码仓:
https://gitee.com/openharmony/security_selinux_adapter
SELinux结构图:
SELinux运行视图:
在OH源码根目录下third_party\selinux是原生的代码;
在base\security\selinux_adapter是OH封装对外提供的接口源码;
OpenHarmony的通用sepolicy策略文件,存放到base\security\selinux_adapter\sepolicy目录下
OpenHarmony中SELinux详情介绍:
https://gitee.com/openharmony/security_selinux_adapter/blob/master/README.md
新增SA服务如何配置SELinux权限
添加构建配置
OpenHarmony的SA服务配置文件(如 : ***.cfg)里需要设置secon字段,该字段对应的就是上文说的主体名字;添加SELinux权限的关键一步是确定你进程的secon字段配置
假使secon字段为 "secon": "u:r:my_service:s0",表示这个sa服务运行后在SELinux主体中的名字就是my_service,下面根据这个主体名字配置SELinux权限.
- 在/base/security/selinux_adapter/sepolicy/base/public/service.te 文件中添加
type sa_my_service, sa_service_attr; 在主体名前面加上sa_ - 在/base/security/selinux_adapter/sepolicy/base/public/service_contexts 文件中添加
68000 u:object_r:sa_my_service:s0
如果按照SA构建的目录标准,你的SA服务目录里面有个sa_profile目录,里面有个 数字.json 格式的json文件,该文件的数字id 替换上面68000数字id - 在/base/security/selinux_adapter/sepolicy/base/public/type.te 文件中添加
type my_service, sadomain, domain; - 在在/base/security/selinux_adapter/sepolicy/base/public/新建一个以服务为名的te文件,例如:my_service.te,后面要做的就是找出权限并添加到my_service.te中
备注:因为上面我们修改了service_contexts文件跟service.te文件,肯定是要把我们的修改加到selinux_adapter的构建中去的,通过selinux_adapter的项目目录树,在BUILD.gn的同级目录有个scripts脚本目录,里面存放的就是构建脚本,有兴趣的可以去看看脚本如何构建te文件
获取权限
从上文SELinux的工作模式段落可知SELinux有三个模式,强制模式,宽容模式,关闭;
一般在SA的开发过程中,可以禁用SELinux进行功能开发,功能开发完毕后再使用宽容模式进行SELinux权限配置
在构建系统的时候关闭SELinux组件 : 在项目源码的vendor仓产品构建的config.json文件中使用"build_selinux": false,禁用SELinux的构建
宽容模式不能完全提示出所有权限,还需切换到强制模式继续获取所需权限,权限配置的验收标准是在强制模式下完全使用功能
切换宽容模式
修改宽容模式使用如下命令,执行之后重启开发板:mount -o remount,rw / && echo "SELINUX=permissive" > /etc/selinux/config
切换强制模式mount -o remount,rw / && echo "SELINUX=enforcing" > /etc/selinux/config
切换宽容模式后重启后查看当前是否开启selinux 策略:getenforce
输出 0 或 Permissive 则表示宽容模式
selinux avc 日志抓取一
在宽容模式下运行你的SA服务,调用功能,测试业务,把全部接口都测试一遍
抓取avc日志可以在过程中执行相关的操作,这样日志会多一点;
改成宽容模式后,将avc日志过滤到data.txt中,脚本之后会解析data.txt,输出selinux 策略;
打开cmd,切换工作目录为D盘根目录,运行下面指令hdc shell "dmesg -w|grep avc" > data.txt
selinux策略自动化提取脚本:selinux_app.py
脚本内容如下:
import re
'''
description: parse the avc log file with command as follow:
mount -o remount,rw / && echo "SELINUX=permissive" > /etc/selinux/config && dmesg -w|grep avc
将avc过滤的日志保存到一个文件中,将日志文件路径填入main函数中, 脚本会自动解析avc日志文件, 并将结果保存到res.txt文件中
可能需要修改的参数:
文本编码模式: windows默认是utf-16,需要根据实际编码修改
'''
pattern = re.compile(r'avc:\s+denied\s+\{(.+)\}\s+\w+\s+pid=(\d+)\s+comm=\"(.+)\"\s+(\w+=.+)?scontext=u:r:(\w+):(\w+)\s+tcontext=u:(\w+):(\w+):(\w+)\s+tclass=(\w+)')
def parse(line: str, res: set) -> set:
if 'avc' in line:
match = pattern.search(line)
if match is None:
print('no match: ')
return res
if match:
if (len(match.groups()) == 10):
# scontext tcontext:tclass{}
sentence = f"allow {match.group(5)} {match.group(8)}:{match.group(10)} {{ { match.group(1).strip() } }};"
res.add(sentence)
# print(sentence)
else:
print("please check the format of the rule......")
sentence = f"allow {match.group(4)} {match.group(6)}:{match.group(8)} {{ { match.group(1).strip() } }};"
res.add(sentence)
return res
def read_file(path: str) -> set:
res = set()
with open(path, 'r', encoding= "utf-8") as f:
lines = f.readlines()
for line in lines:
parse(line, res)
return res
def filter(path: str, flag: str):
with open(path, 'r', encoding= "utf-8") as f:
lines = f.readlines()
filter_ = re.compile(flag)
for line in lines:
if filter_.search(line):
print(line.strip())
print('\n')
return
if __name__ == '__main__':
# file path
res = read_file(r'D:\\ddd.txt')
for val in res:
print(val)
# write res to file
with open(r'./res.txt', 'w') as f:
for val in res:
f.write(val + '\n')
print("====================================\n")
filter(r'./res.txt', 'acs')
print("parse end.....")
将脚本中的 res = read_file(r’D:\ddd.txt’) 路径改为上述data.txt 的绝对路径
修改脚本中 filter(r’./res.txt’, ‘docker’) 的第二个参数;如将docker改为my_service
执行 python selinux_app.py 即可输出结果如下:
====================================
allow su my_service:dir { search };
allow su my_service:dir { getattr };
allow my_service data_file:file { lock };
allow my_service data_file:dir { read };
allow my_service data_file:dir { search };
allow my_service data_file:file { getattr };
allow my_service data_file:dir { open };
allow my_service data_file:file { write };
allow su my_service:file { open };
allow su my_service:file { read };
将上述 形如 allow su my_service:file { read }; 的输出写入/base/security/selinux_adapter/sepolicy/base/public/my_service.te中
修改selinux 策略文件后,可以单独编译该部件,然后替换开发板中的policy.31 文件,重启开发板
编译命令:
- 不使用ninja, 速度慢
./build.sh --product-name 你的产品名 -T selinux_adapter - 使用ninja,速度较快
ninja -C out/arm64/targets/ -w dupbuild=warn selinux_adapter
生成的目录可以参考如下目录:
将该文件替换到板子的/system/etc/selinux/targeted/policy目录,重启开发板,重复执行上述步骤
自动抓取avc
在上述py脚本的同级目录创建一个loop.bat文件,内容如下:
@echo off
:loop
echo cmd /c "python.exe .\selinux_app.py"
cmd /c "python.exe .\selinux_app.py"
timeout /t 1
goto loop
后续只需要执行loop.bat代替执行 python selinux_app.py ,每秒打印一次avc权限
selinux avc 日志抓取二
avc日志会在dmesg 跟hilog中打印,因此只看dmesg无法完全抓取到avc日志,还需要通过hilog查看日志
连接开发板,输入: hilog | grep -e avc
然后重复上面的动作抓取avc日志,hilog中打印的日志如下:
08-08 15:49:14.805 292 350 E C05a03/Selinux: avc: denied { get } for service=9874 pid=5517 scontext=u:r:system_core_hap:s0 tcontext=u:object_r:sa_my_service:s0 tclass=samgr_class permissive=0
上面的日志如何换成形如 allow my_service data_file:file { lock }; 的语句呢?
观察上面的avc语句, 需要关注的点有四处
- denied { get } for service=9874 这里的{ get }
- scontext=u:r:system_core_hap:s0 这里的scontext表示访问者,system_core_hap表示访问者主体
- tcontext=u:object_r:sa_my_service:s0 这里的tcontext表示被访问者,sa_my_service表示访问者主体
- tclass=samgr_class 表示访问的目的是 samgr_class
根据规则组合上述4部分 allow system_core_hap sa_my_service:samgr_class { get };
根据上述规则重复操作
avc日志不打印了还是无法运行SA服务咋办?
很多时候avc跟hilog 人为无法及时抓取SA服务在开机启动时的日志,SA服务在启动的时候也需要一定权限,可通过如下两个思路解决该问题
- 使用hilog落盘
- 手动启动服务
使用hilog落盘
新建一个脚本名为: runHilog.bat,内容如下:
@echo off
set CWD=%~dp0
pushd %CWD%
for /f %%i in ('hdc.exe list targets') do (
echo target: %%i
hdc.exe -t %%i shell "param set hilog.loggable.global d"
hdc.exe -t %%i shell "hilog -Q pidoff"
hdc.exe -t %%i shell "hilog -p off"
hdc.exe -t %%i shell "hilog -w stop"
hdc.exe -t %%i shell "cd /data/log; find hilog faultlog eventlog -type f | xargs rm -f"
hdc.exe -t %%i shell "hilog -w start -t kmsg -f 'kmsg' -l 50M -m zib -n 1000 ; hilog -w start -f 'hilog' -l 50M -m zib -n 1000"
hdc.exe -t %%i shell "cat /dev/null > /data/local/tmp/hdc.log"
)
pause & exit
::hdc -t %%i shell reboot
该脚本启动hilog日志落盘
新建一个脚本名为 getHiLog.bat,内容如下:
@echo off
set HILOG_DIR=/data/log/hilog
if %time:~0,2% LEQ 9 (
set NOW=%date:~0,4%%date:~5,2%%date:~8,2%0%time:~1,1%%time:~3,2%%time:~6,2%
) else (
set NOW=%date:~0,4%%date:~5,2%%date:~8,2%%time:~0,2%%time:~3,2%%time:~6,2%
)
set CWD=%~dp0
set LOG_DIR=%CWD%LOG\%NOW%
if not exist %LOG_DIR% md %LOG_DIR%
pushd %CWD%
@REM del /s/q G:\temp\log
for /f %%i in ('hdc.exe list targets') do (
echo %%i
if not exist %%i md %LOG_DIR%\%%i
hdc.exe -t %%i shell "hilog -w stop"
hdc.exe -t %%i shell "cd /data/log; ps -ef > hilog/ps.log; tar -cf faultlog.tar faultlog eventlog; tar -cf binder.tar -C%BINDER_DIR% binder"
hdc.exe -t %%i file recv /data/log/faultlog.tar %LOG_DIR%\%%i
hdc.exe -t %%i file recv /data/log/binder.tar %LOG_DIR%\%%i
hdc.exe -t %%i shell "rm -rf /data/log/faultlog.tar /data/log/binder.tar"
for /f %%f in ('hdc.exe -t %%i shell "ls %HILOG_DIR%"') do (
echo hdc.exe file recv %HILOG_DIR%/%%f %LOG_DIR%\%%i\%%f
hdc.exe -t %%i file recv %HILOG_DIR%/%%f %LOG_DIR%\%%i\%%f
@REM hdc.exe -t %%i file recv %HILOG_DIR%/%%f G:\temp\log
echo.
)
if exist %LOG_DIR%\%%i\faultlog.tar (
tar -xf %LOG_DIR%\%%i\faultlog.tar -C %LOG_DIR%\%%i
del %LOG_DIR%\%%i\faultlog.tar
)
)
pause & exit
该脚本为拉取hilog落盘日志到当前目录
使用方式为运行runHilog.bat,重启开发板,运行getHiLog.bat拉取日志
进入拉取的日志,寻找名字带有 hilog 跟 kmsg的文件,查找avc日志并根据规则改为allow 语句
手动启动服务
连接开发板,运行 ps -elf | grep my_service
根据进程id 杀掉进程 kill -9 id
手动拉起服务: `service_control start my_service(你的SA服务名)
当然了,上面的杀掉服务得是你处理完启动权限后再进行的操作
如上多次执行捞取avc日志
添加权限后编译selinux_adapter出错咋办?
在添加权限的时候大概率你需要的权限加上去会编译失败,常见的失败有两种使用如下指令处理错误
grep "type su" -rin ./base/security/selinux_adapter/和
find ./base/security/selinux_adapter/ -name "*.te" | xargs grep "data_file" -in搜索错误并按下列范例修改
- ERROR ‘unknown type su’ at token ‘;’
在你的te文件中使用debug_only将su语句包裹,如下所有的su语句 都包裹在debug_only里面
debug_only(`
allow su my_service:dir { search };
allow su my_service:dir { getattr };
')
- neverallow check failed at /home/XXX/XX/XXXX/out/arm64/targets/obj/base/security/selinux_adapter/system.cil:4613 from …/…/…/base/security/selinux_adapter/sepolicy/base/public/domain.te:258

找到domain.te:258行,在最后根据格式加上
-my_service } 注意最后空格
一般这种neverallow都能在编译报错日志里面找到解决方法,就是去报错的文件行数加上你的服务名: 形如 -my_service
更多推荐
所有评论(0)