9. SELinux基础(一):SELinux发展史、DAC与MAC的区别、安全上下文
好,咱们今天聊聊SELinux。说实话,这玩意儿刚出来的时候,我也觉得它挺烦人的。动不动就"avc: denied",日志里一堆看不懂的报错。但后来我真正吃透它之后,才发现——它是Android安全体系里最硬的一道防线。
你想想看,没有SELinux之前,系统靠什么保护自己?靠权限。但权限这玩意儿,说白了就是个"一锤子买卖"——你给了某个App读取SD卡的权限,它就能读你所有的照片。这合理吗?显然不合理。SELinux要解决的,就是这个"权限滥用"的问题。
9.1 SELinux的发展史:从NSA到Android
SELinux的全称是Security-Enhanced Linux。它最早是美国国家安全局(NSA)搞出来的。嗯,你没听错,就是那个NSA。2000年左右,他们发现Linux内核的权限模型太弱了,于是联合SELinux社区一起开发了一套强制访问控制机制。
我记得最早接触SELinux是在做嵌入式Linux的时候。那时候它还是个可选模块,很多发行版默认都不开。但到了Android 4.3(Jelly Bean),Google开始把它引入Android。到了Android 5.0(Lollipop),就全面强制开启了。
为什么会这样?因为Android的碎片化太严重了。厂商定制、第三方ROM、恶意App……光靠传统的权限模型根本防不住。SELinux的引入,相当于给系统加了一层"兜底"的保险。
关键时间节点:
- 2000年:NSA发布SELinux初始版本
- 2003年:SELinux进入Linux内核主线
- 2013年:Android 4.3开始实验性引入
- 2015年:Android 5.0全面强制开启SELinux
9.2 DAC与MAC的区别:权限模型的进化
要理解SELinux,得先搞清楚两个概念:DAC和MAC。
DAC(自主访问控制),就是咱们平时用的Linux权限模型。每个文件有owner、group、others三组权限。谁创建的文件,谁就能决定谁能访问它。听起来挺合理对吧?但问题在于——root用户不受限制。
我举个例子。你在手机上装了一个App,它申请了"读取联系人"的权限。你同意了。然后这个App被恶意利用了,攻击者拿到了它的进程ID。在DAC模型下,这个进程可以读取任何它有权访问的文件——包括你的短信、照片、甚至系统配置文件。只要它拿到了root权限,整个系统就裸奔了。
MAC(强制访问控制)就不一样了。它由系统管理员(或者安全策略)统一制定规则。每个进程和每个资源都有一个"安全上下文"。能不能访问,不是看你是谁,而是看你的安全上下文和资源的安全上下文是否匹配。
说白了,DAC是"你说了算",MAC是"规则说了算"。在MAC模型下,就算你拿到了root权限,也得遵守安全策略。你想读一个不该读的文件?不行,策略不允许。
| 特性 | DAC | MAC |
|---|---|---|
| 控制主体 | 文件所有者 | 系统安全策略 |
| root权限 | 无限制 | 受策略约束 |
| 灵活性 | 高 | 低(策略严格) |
| 安全性 | 低 | 高 |
| 典型应用 | 普通Linux | Android、Red Hat |
我的经验:在Android系统里,DAC和MAC是共存的。DAC负责基础的权限控制,MAC负责更细粒度的安全策略。两者配合使用,才能达到最佳效果。我曾经在调试一个系统服务时,明明DAC权限都对了,但就是访问不了某个文件。查了半天,才发现是SELinux策略给拦了。
9.3 安全上下文:SELinux的核心概念
安全上下文(Security Context)是SELinux里最基础的概念。每个进程、每个文件、每个套接字……所有系统资源都有一个安全上下文。它长什么样呢?
user:role:type:level
嗯,就是四个字段,用冒号隔开。咱们一个一个来看。
user:SELinux用户。在Android里,通常只有一个用户——u。别跟Linux系统用户搞混了,这是SELinux自己的用户概念。
role:角色。用于基于角色的访问控制(RBAC)。在Android里,角色通常也是固定的,比如r。
type:类型。这是最关键的字段。SELinux的强制访问控制,主要就是靠类型来实现的。每个进程有一个"域"(domain),每个文件有一个"类型"(type)。能不能访问,就看域和类型之间的策略规则。
level:安全级别。用于多级安全(MLS)或多类别安全(MCS)。Android里用得不多,但有些场景会用到。
举个例子。一个典型的Android进程安全上下文:
u:r:untrusted_app:s0:c15,c256,c512,c768
这个上下文表示:这是一个不受信任的App进程,运行在untrusted_app域中,安全级别是s0,带有一些MCS类别。
再看一个系统服务的:
u:r:system_server:s0
这是系统服务进程,运行在system_server域中。它的权限比普通App大得多。
核心逻辑:SELinux的策略就是定义"哪个域可以访问哪个类型"。比如:
allow untrusted_app app_data_file:file read;
这条规则的意思是:允许untrusted_app域中的进程,读取类型为app_data_file的文件。
9.4 安全上下文在Android中的实际应用
咱们来看看实际系统里,安全上下文是怎么用的。我随便找一个文件看看:
$ ls -Z /data/data/com.example.app/
u:object_r:app_data_file:s0:c15,c256,c512,c768 cache
u:object_r:app_data_file:s0:c15,c256,c512,c768 databases
u:object_r:app_data_file:s0:c15,c256,c512,c768 files
注意看,ls -Z会显示SELinux上下文。这里的object_r表示这是一个对象(文件)的角色。类型是app_data_file,跟进程的untrusted_app域是配对的。
再看一个系统文件:
$ ls -Z /system/bin/
u:object_r:system_file:s0 app_process
u:object_r:system_file:s0 sh
u:object_r:system_file:s0 toolbox
系统文件的类型是system_file,只有系统服务域(如system_server)才能访问。普通App想读?门都没有。
注意:我曾经遇到过一个坑。有个App需要读取/proc下的某个文件,DAC权限明明给了,但就是读不到。一查SELinux日志,发现是untrusted_app域没有proc类型的读权限。后来在策略里加了一条allow untrusted_app proc:file read;才解决。嗯,这种问题在调试第三方App时特别常见。
9.5 知识体系总览
下面这张图,把咱们这一章的核心逻辑串起来了。你可以看到SELinux从发展史到核心概念的完整脉络。
嗯,这一章的内容就到这儿。SELinux的基础概念其实不难理解,关键是要把"安全上下文"这个核心概念吃透。后面咱们会深入策略编写和调试技巧,到时候你就知道这东西有多强大了。
避坑指南:刚开始学SELinux,最容易犯的错误就是——把DAC和MAC搞混。记住一句话:DAC管"能不能看到",MAC管"能不能访问"。我曾经有个同事,调试一个文件访问问题,折腾了两天,最后发现DAC权限给了,但SELinux策略没写。嗯,从那以后他再也不敢忽略SELinux了。