目录
前言
网络上关于SELinux的介绍基本遵循国外SELinux教材的思路,虽然全面细致,但读起来十分枯燥,味同嚼蜡。特别要指出的是一些术语的翻译,可以说是晦涩难懂,比如将DAC翻译成“任意访问控制”,将MAC翻译成“强制访问控制”,这种翻译即不能准确地表达意思,也难于传播和解释。本文的目的,既是为了更加通俗地介绍SELinux,也为了将其中一些术语进行本地化,让它们更容易被我们本土的程序员所理解。
一.SELinux理论基础
SELinux的目标是打造更加安全的系统,这个目标的涵义可进一步解释为提升漏洞利用的难度,以及减小漏洞的危害范围,实现目标的主要技术手段就是访问控制。
1.1.理解SELinux的目标
假设如下场景:
进程A、进程B、文件F
对应的访问控制目标:
允许进程A读写F,不允许进程B读写F
解决思路:通过归属划分所有权
给进程和文件分配身份,划分文件的归属。A以u_A的身份运行,B以身份u_B运行,文件F的身份是u_A,即u_A是F的属主,除属主外的其他人无法读写F(root除外)。
归属的方式的局限性
当进程B利用漏洞以身份u_A或root身份运行时,可获取对F的读写权限
eg:存在全局可执行文件G,其属主为u_A,且设置了setuid位。过程如下图所示:
第一步:进程B执行文件G获取u_A身份
第二步:进程B以u_A身份读写F
归属的方式的局限性
1、进程身份存在不完全可控的转换的途径(setuid、漏洞等)
2、控制粒度只包含读、写、执行
进而我们考虑使用增强的手段:通过规则的方式实现访问控制。实现方式如下:
给进程和文件打标签,基于规则确定对文件的访问权限。A的标签为A_t,B的标签为B_t,F的标签为F_t,访问权限定义read、write。制定规则“allow A_t F_t { read write };”
当进程B利用漏洞以u_A的身份运行时,因为其标签身份依然是B_t,因此无法通过规则的检查,因为规则只允许A_t读写文件F。
两种访问控制方式总结:
第一种:DAC——任意访问控制
DAC的方式可以看作是“身份本位”,进程的权限由运行时的身份(user)决定,但Linux系统中进程的身份可利用漏洞进行转换。
第二种:MAC(SELinux)——强制访问控制
MAC的方式可以看作是“规则本位”,进程的权限由规则确定,一切访问行为都需要规则进行明确授权,默认不授予权限。
DAC 与 MAC/SELinux对比:
- DAC
- 权限类型少(读写执行)
- 控制粒度粗
- 对研发人员友好,但不够安全
- MAC/SELinux
- 权限类型丰富(覆盖几乎所有文件操作类型、网络类型的操作等等)
- 控制粒度比较细
- 对研发人员不太友好,但安全性强于DAC
1.2.理解SELinux的规则和策略
1.2.1.SELinux的本质
SELinux本质上是基于规则进行访问控制决策的引擎,引擎的输入为主体、客体、行为类型、规则。引擎的输出为对行为的控制决策,如允许访问、拒绝访问。如下为SELinux输出的日志,包含了主客体、行为等信息,而输出日志本身就是一种决策结果。
1.2.2.主客体的表示
通过安全上下文进行标识 ,具体格式为user:role:type:mls_level。
1.2.3.安全上下文的涵义
一方面,安全上下文是规则引擎的输入;
另一方面,安全上下文的合法性构成了一种约束,属于一种默认策略。
- User,代表用户,系统级进程和文件一般为system_u 。
- Role,代表角色,系统级进程一般为system_r,客体一般为object_r. 。
- Type,进程和文件最基本的标识,由研发人员自定义,一般以_t结尾。
- Security level(Multiple Level Security),XCU中未启用
1.2.4.关联的涵义
只有关联过的user、role和type组成的安全上下文才是合法的,所有类型的转换都在这种合法性的约束下进行。
关联的过程就是role、type的声明过程:
role system_r types initrc_t;
只有经过了上面的声明,system_u:system_r:initrc_t:s0才是一个合法的上下文,下面的转换规则才能成功进行:
type_transition init_t initrc_exec_t : process initrc_t;
1.2.5.规则语法
- 基本格式
allow domains types:classes permissions
- Domain
用于标识单一进程或一系列进程
- Type
用于标识客体(文件、Socket等)或一系列客体
- Class
被访问客体的具体类别
- Permission
可以被允许的操作
- 默认的授权方式
默认不授权,必须通过规则明确授权
- 主客体的相对性
主客体之间的关系是相对的,不是绝对的。例如当进程作为行为主体去读写文件时,进程时主体,但作为收发消息的一端时,就变成了客体。
二.策略配置实践及高级用法
2.1.策略配置实践
2.1.1.理解SELinux的三种模式
- enforcing
- 强制模式,违反SELinux规则的行为将被阻止并记录到日志中。用“1”表示。
- Permissive
- 宽容模式,当违反SELiunx的行为规则时,会发出警告并记录到日志中,但是不会被阻止,一般都是调试用。用“0”表示。一般推荐在宽容模式下获取avc日志,在强制模式下验证。
- Disabled
- 关闭SELinux,不使用SELiunx
- 获取当前模式
- getenforce
- 更改当前模式
- setenforce 0/1
2.1.2.配置策略如何着手
- 推荐的方式是“依葫芦画瓢”,在模仿中进行提升和理解
2.1.3.如何模仿
第一步:找到模仿对象的策略文件
以linux原生进程为例:
- refpolicy/policy/modules/services/rngd.fc
- refpolicy/policy/modules/services/rngd.if
- refpolicy/policy/modules/services/rngd.te
第二步:阅读参考模块的策略文件
第三步:理解后仿写
2.1.4.理解原生进程的策略生效方式
eg:linux原生进程的策略配置内容
两个重点:
1)文件标签生效
2)进程标签生效
2.1.5.理解后的仿写步骤
eg:linux原生进程的策略配置内容总结
1)类型定义
2)文件标签定义
3)类型转换规则定义
4)功能所需权限相关的定义
5)接口定义
2.1.6.策略配置总结
- 类型先定义后使用
- 文件需要明确地定义上下文
- 系统服务一般以模块的形式定义,目录为refpolicy/policy/modules/services/
- Init启动的服务通过宏/接口定义基础策略
- 域间交互行为通过宏/接口进行定义,作为“公有”接口对外开放
- 模块内定义的类型是“私有”类型,对外不可见
2.2.策略的高级用法
2.2.1.neverallow
- 定义禁止行为
refpolicy/modules/kernel/domain.te
2.2.2.安全驱动开发
- 利用neverallow禁止手段来保证研发设计的目标
相关规则可以参考android的实现:
android/system/sepolicy/public/domain.te
android/system/sepolicy/public/attributes
三.策略配置的原则
-
最小权限原则
最小权限原则是要求计算环境中的特定抽象层的每个模组如进程、用户或者计算机程序只能访问当下所必需的信息或者资源。赋予每一个合法动作最小的权限,就是为了保护数据以及功能避免受到错误或者恶意行为的破坏。
- 非必要不修改neverallow
- 非必要不添加新的user和role
- 不要轻易修改系统级别进程和文件的类型
- 在以上原则的基础上,尽量使用类型增强(type enhanced)
四.策略的开发流程
开发流程可以参考下图,要点为策略提交之后必须经过安全人员review,以控制安全风险。
五.参考资料
-
官方wiki:SELinux Wiki
- The purpose of SELinux roles:SELinux/Tutorials/The purpose of SELinux roles - Gentoo Wiki
- How does a process get into a certain context:SELinux/Tutorials/How does a process get into a certain context - Gentoo Wiki
- 《The_SELinux_Notebook》