Selinux权限配置

OpenHarmony中SELinux使用详解

目录

  1. SELinux简介
  2. SELinux概念
  3. SELinux模式
  4. OH中SELinux使用详解
  5. 新增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工作流程在这里插入图片描述
可以从图中看出,需要关注的关键信息如下:

  1. 主体(Subject):指主动对其它实体施加动作的实体,例如进程、服务等。
  2. 策略(policy) :权限策略,通过在te文件中配置对应的权限语句实现,权限策略以type或attribute作为执行对象。
  3. 安全上下文(Security Context):实际上就是一个附加在主体或客体上的标签。
  4. 目标(Object):是被动接受其他实体访问的实体,例如文件、系统属性等。

SELinxu工作的核心就是安全上下文。安全上下文是一组和进程或对象有关的安全属性,每一个进程或对象都会记录一条安全上下文,将其作为SELinux判断进程是否能读取对象的依据。安全上下文分为“进程安全上下文”和“文件安全上下文”。一个“进程安全上下文”一般对应多个“文件安全上下文”。
只有两者的安全上下文对应上了,进程才能访问文件。它们的对应关系由策略中的规则决定。
文件安全上下文由文件创建的位置和创建文件的进程所决定。而且系统有一套默认值,用户也可以对默认值进行设定。需要注意的是,单纯的移动文件操作并不会改变文件的安全上下文。
SELinux有3种工作模式:

  1. 【enforcing】强制模式。违反 SELinux 规则的行为将被阻止并记录到日志中。
  2. 【permissive】宽容模式。违反 SELinux 规则的行为只会记录到日志中。一般为调试用。
  3. 【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

OH_Selinux配置说明.docx

新增SA服务如何配置SELinux权限

添加构建配置

OpenHarmony的SA服务配置文件(如 : ***.cfg)里需要设置secon字段,该字段对应的就是上文说的主体名字;添加SELinux权限的关键一步是确定你进程的secon字段配置
假使secon字段为 "secon": "u:r:my_service:s0",表示这个sa服务运行后在SELinux主体中的名字就是my_service,下面根据这个主体名字配置SELinux权限.

  1. 在/base/security/selinux_adapter/sepolicy/base/public/service.te 文件中添加
    type sa_my_service, sa_service_attr; 在主体名前面加上sa_
  2. 在/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
  3. 在/base/security/selinux_adapter/sepolicy/base/public/type.te 文件中添加
    type my_service, sadomain, domain;
  4. 在在/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 文件,重启开发板
编译命令:

  1. 不使用ninja, 速度慢
    ./build.sh --product-name 你的产品名 -T selinux_adapter
  2. 使用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语句, 需要关注的点有四处

  1. denied { get } for service=9874 这里的{ get }
  2. scontext=u:r:system_core_hap:s0 这里的scontext表示访问者,system_core_hap表示访问者主体
  3. tcontext=u:object_r:sa_my_service:s0 这里的tcontext表示被访问者,sa_my_service表示访问者主体
  4. tclass=samgr_class 表示访问的目的是 samgr_class
    根据规则组合上述4部分 allow system_core_hap sa_my_service:samgr_class { get };

根据上述规则重复操作

avc日志不打印了还是无法运行SA服务咋办?

很多时候avc跟hilog 人为无法及时抓取SA服务在开机启动时的日志,SA服务在启动的时候也需要一定权限,可通过如下两个思路解决该问题

  1. 使用hilog落盘
  2. 手动启动服务
使用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搜索错误并按下列范例修改

  1. 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 };
')
  1. 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

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐