SELinux策略:域转换与类型继承
一. 域(Domain)转换
域转换是 SELinux 中一个基础且强大的安全概念。它控制着一个进程如何从一种安全上下文(域)切换到另一种,通常是获取更高权限的过程
为什么需要域转换?
在 Android 中,你绝不希望一个普通的应用进程(运行在 untrusted_app
域)直接执行一个位于 /system/bin
的二进制文件并继承 system_server
的权限。这将是巨大的安全漏洞。域转换确保了这种权限提升必须通过明确的、被策略允许的路径发生
域转换发生的条件
一个成功的域转换需要满足三个条件,通常通过 domain_auto_trans
宏来声明,该宏会一次性添加所有三条规则
- 执行权限(Execute Permission):
○ 父进程(在源域source_domain
)必须对要执行的可执行文件(具有目标类型target_type
)有execute
权限
○ 规则类型:allow source_domain target_type:file execute;
- 入口权限(Entrypoint Permission):
○ 新的进程(即将运行在目标域new_domain
)必须能够将其入口点(即它所执行的那个程序文件)标识为被允许的入口
○ 这相当于说:“只有被标记为类型T
的文件,才能作为进入域D
的入口”
○ 规则类型:allow new_domain target_type:file entrypoint;
- 转换权限(Transition Permission):
○ 父进程(源域)必须被允许将子进程转换到新的域
○ 规则类型:allow source_domain new_domain:process transition;
此外,还有一个隐含条件:系统必须知道执行该文件后应该切换到哪个新域。这是通过文件的安全上下文实现的。可执行文件必须本身就被标记为正确的目标类型(target_type
),而这个 target_type
会通过策略规则映射到 new_domain
实际案例:zygote
启动 system_server
这是 Android 中最经典的域转换例子
- 参与者:
○ 源域:zygote
(一个特权进程,负责孵化新进程)
○ 可执行文件:/system/bin/app_process64
(被标记为app_exec_type
,例如system_file
)
○ 目标域:system_server
(Android 核心系统进程的域) - 转换过程:
○zygote
进程 fork 自身
○ 在子进程中,它准备执行/system/bin/app_process64
并指定参数以启动system_server
○ 在执行 (execve
) 之前,SELinux 检查策略
○ 策略中会有一条规则(通常由domain_auto_trans
宏生成):# 这通常隐藏在宏中,但效果如下: allow zygote app_exec_type:file execute; allow system_server app_exec_type:file entrypoint; allow zygote system_server:process transition; # 最关键的一步:自动转换规则 domain_auto_trans(zygote, app_exec_type, system_server)
○ 由于可执行文件
app_exec_type
是进入system_server
域的有效entrypoint
,并且所有权限都满足,执行后子进程的安全上下文就从zygote
成功转换为了system_server
手动转换:domain_trans
有时转换不是自动的,需要进程主动调用 setcon()
。这时使用 domain_trans
规则,它只包含 transition
权限,而不触发自动转换
二. 类型(Type)继承
类型继承是 SELinux 策略语言中用于代码重用和简化策略管理的重要特性。它允许你创建一个新的类型属性(Attribute),并让它继承现有属性的所有规则
为什么需要类型继承?
想象一下,你有 100 个不同的服务域(service_a_domain
, service_b_domain
, ...),它们都需要访问设备节点(dev_type
)。没有继承,你需要写 100 条 allow
规则:
allow service_a_domain dev_type:chr_file rw;
allow service_b_domain dev_type:chr_file rw;
...
allow service_zz_domain dev_type:chr_file rw;
这非常冗长且难以维护。类型继承解决了这个问题
如何工作:属性(Attributes)
继承的核心是属性。一个属性是一组类型的集合
- 定义属性:在
attributes
文件中声明一个属性# attributes attribute my_custom_domain; attribute my_custom_file_type;
- 将类型与属性关联:在
.te
文件中,使用type
语句并加上attributes
参数# 将 service_a_domain 类型关联到 my_custom_domain 属性 type service_a_domain, domain, my_custom_domain;# 将 service_a_exec 类型关联到 my_custom_file_type 属性 type service_a_exec, exec_type, file_type, my_custom_file_type;
- 为属性编写规则:现在,你可以编写一条针对属性的规则,所有属于该属性的类型都会自动生效
# 一条规则覆盖所有! allow my_custom_domain my_custom_file_type:file entrypoint; allow my_custom_domain dev_type:chr_file rw; allow my_custom_domain kernel:system syslog_read;
这等价于为
service_a_domain
,service_b_domain
等所有标记了my_custom_domain
的类型分别写了上述三条规则
在 Android 中的实际应用
Android 广泛使用属性来管理策略
coredomain
: 几乎所有核心 Android 系统进程域(如system_server
,servicemanager
,surfaceflinger
)都继承了这个属性。一条针对coredomain
的规则会应用到所有这些重要的域上# 允许所有核心域使用系统日志 allow coredomain kernel:system syslog_read;
appdomain
: 所有应用域(platform_app
,untrusted_app
)都继承自此属性。可以用来定义所有应用都应遵守的通用规则net_domain
: 所有需要网络的域继承自此属性。可以用来集中管理网络访问规则
注意:属性是集合的集合,规则中的属性可以出现在源(subject)或目标(object)的位置,极大地增强了策略的灵活性和可维护性
总结与关系
特性 | 域转换(Domain Transition) | 类型继承(Type Inheritance) |
---|---|---|
目的 | 控制进程权限提升,确保安全边界 | 简化策略管理,实现规则重用和集中化管理 |
机制 | 通过 execute , entrypoint , transition 权限和控制可执行文件标签来实现自动或手动的域切换 | 通过属性(Attribute) 将类型分组,允许为整个组编写统一的 allow 规则 |
关键规则 | domain_auto_trans(source_domain, target_type, new_domain) | type my_type, attribute1, attribute2; allow attribute1 attribute2:class perm; |
类比 | 函数调用:有严格的参数和权限检查才能进入 | 面向对象编程中的继承:一个子类(具体类型)继承父类(属性)的所有特性(规则) |
两者如何协同工作:
在复杂的 Android 系统中,这两种机制紧密结合。例如,一个服务的可执行文件类型可能继承自 service_exec_type
属性,这使得所有服务二进制文件自动获得作为 entrypoint
所需的规则。而启动该服务的进程(如 init
或 zygote
)则通过域转换规则,被允许转换到该服务域,而这个服务域本身可能又继承自 coredomain
属性,从而自动拥有核心域的一系列权限