本文主要描述了SELinux的原理,以及在android中的使用。第一部分首先介绍了SELinux的基础框架;第二部分介绍了SELinux的基础理论;第三部分简单介绍了SELinux Policy。
本文主要基于android系统的SELinux讲解,水平有限,如果有错误,敬请指正。
SELinux Overview
SELinux 来源
SELinux 即Security-Enhanced Linux,由美国国家安全局(NSA)发起,Secure Computing Corporation (SCC) 和 MITRE 直接参与开发,以及很多研究机构(如犹他大学)一起参与的强制性安全审查机制,该系统最初是作为一款通用访问软件,发布于 2000 年 12 月(代码采用 GPL 许可发布)。并在Linux Kernel 2.6 版本后,有直接整合进入SELinux,搭建在Linux Security Module(LSM)基础上,目前已经成为最受欢迎,使用最广泛的安全方案。
SELinux 基本架构与原理
SELinux 是典型的MAC-Mandatory Access Controls实现,对系统中每个对象都生成一个安全上下文(Security Context),每一个对象访问系统的资源都要进行安全上下文审查。审查的规则包括类型强制检测(type enforcement),多层安全审查(Multi-Level Security),以及基于角色的访问控制(RBAC: Role Based Access Control)。
SELinux 搭建在Linux Security Module(LSM)基础上,关于 LSM 架构的详细描述请参见文章 “Linux Security Modules: General Security Support for the Linux Kernel”,该文章在 2002 年的 USENIX Security 会议上发表。有完整的实现LSM 的所有hook function。
SELinux 的整体结构如下图所示:
SELinux 包含五个基本组成:
- 用于处理文件系统的辅助模块, 即SELinuxFS;
- 集成Linux Security Modules 的hooks sets;
- Security Policy Database;
- Security Label 验证模块;
- Access Vector Cache (AVC),访问向量缓存,以便提高验证速度。
基本的访问流程如下图所示:
流程如下:
- 进程通过系统调用(System Call) 访问某个资源,进入Kernel 后,先会做基本的检测,如果异常则直接返回;
- Linux Kernel DAC 审查,如果异常则直接返回;
- 调用Linux Kernel Modules 的相关hooks,对接到SELinux 的hooks,进而进行MAC 验证,如果异常则直接返回;
- 访问真正的系统资源;
- 返回用户态,将结果反馈。
SELinux and SEAndroid
Android 是建立在标准的Linux Kernel 基础上,自然也可以开启SELinux,通常在通用移动平台上,很少开启这样的安全服务,Google 为了进一步增强Android 的安全性,经过长期的准备,目前已经在Android 5.0(L) 之后有完整的开启SELinux,并对SELinux 进行深入整合形成了SEAndroid。
SELinux 在Android 上的更新历史
如下图所描述:
- KK 4.4 针对netd、installd、zygote、vold 四个原本具有root 权限的process,以及它们fork 出的子进程启用Enforce 模式;
- L 版本开始普遍性开启SELinux Enforce mode;
- SELinux 分成两种模式,即Permissve Mode(宽容模式)和 Enfocing mode(强制模式)。
Permissive Mode 只通过Kernel Audit System 记录LOG,但不真正拦截访问。
Enforcing Mode 在记录LOG 的同时,还会真正的拦截访问。
从Android N 版本的后续版本Google都会要求强制性开启SELinux,以保证手机安全。
从Android O 版本后,Google大幅度的增强了selinux的限制,特别是限制System/Vendor 之间的交叉使用,如VNDK 的权限控制就主要由selinux来控制。
总体来说, Selinux 在android上的使用过程是一个从具体启用,到整体启用,从粗粒度限制,到精细控制的过程。
SELinux 给Android 带来的影响
- 严格限制了ROOT 权限,以往ROOT “无法无天” 的情况将得到极大的改善;
- 通过SELinux保护,降低系统关键进程受攻击的风险,普通进程将没有权限直接连接到系统关键进程;
- 进一步强化APP的沙箱机制,确保APP难以做出异常行为或者攻击行为;
- 将改变APP一旦安装,权限就已经顶死的历史,APP权限动态调整将成为可能。
SELinux 基础原理
DAC and MAC
DAC 即 Discretionary Access control,自主访问控制,即系统只提供基本的验证,完整的访问控制由开发者自己控制。
MAC 即 Mandatory Access control,强制性访问控制,即系统针对每一项访问都进行严格限制,具体的限制策略由开发者给出。
Linux DAC
Linux DAC 采用了一种非常简单的策略,将资源访问者分成三类,分别是Owner、Group、Other。资源针对这三类访问者设置不同的访问权限。而访问权限又分成 read、write、execute。
访问者通常是进程,有自己的uid/gid,通过uid/gid和文件权限匹配,来确定是否可以访问。
将Root权限根据不同的应用场景划分成许多的Root Capabilities,其中如果有CAP_DAC_OVERRIDE 这项的话,可以直接绕过Linux DAC 限制。
Linux DAC 有明显的不足,其中一个重要点就是Root 权限 “无法无天”,几乎可以做任意事情,一旦入侵者拿到root 权限,即已经完全掌控了系统。另外,每一个进程默认都拿到对应这个用户的所有权限,可以改动/删除这个用户的所有文件资源。很明显,这个难以防止恶意软件。
Linux MAC
Linux MAC 针对DAC 的不足,要求系统对每一项访问进行检查,每访问一个文件资源都需要进行针对性的验证。而这个针对性的验证是根据已经定义好了的策略进行的。在Linux Kernel,所有的MAC机制都是搭建在Linux Security Modules (LSM) 基础上,包括有:SELinux、Apparmor、Smack 和 TOMOYO Linux等。目前SELinux已经成了事实上的行业标准。
针对Linux DAC,MAC 可以明显弥补DAC 的缺陷,一方面限制Root 权限,即使你有root 权限,如果无法通过MAC 验证,那么一样的无法真正执行相关的操作.。另外对每一项权限进行了更加完整的细化,可限制用户对资源的访问行为。
Core SELinux Components
SELinux 的核心组件可以参考下面的图:
- Subject 通常是指触发访问行为的对象,在Linux 里面通常是一个进程(Process);
- Object Manager 即是对象访问管理器,即可以知道Subject 需要访问哪些资源,并且触发验证机制;
- Security Server 即安全服务器,用来验证某个Subject 是否可以真正的访问某个Object,而这个验证机制是基于定义好的Security Policy;
- Security Policy 是一种描述SELinux Policy 的语言;
- Access Vector Cache (AVC) 是访问缓存,用来记录以往的访问验证情况,以便提高效率,快速处理。
Security Context
SELinux 给Linux 的所有对象都分配一个安全上下文(Security Context),描述成一个标准的字符串。
两种类型:
- Subject 主体,linux通常以进程为单位
- Object 访问对象,linux 通常以文件为单位
标准格式:user:role:type[:range]
- User:用户,非Linux UID;
- Role:角色,一个user可以属于多个role,不同的role具有不同的权限。它是SELinux中一种比较高层次,更方便的权限管理思路,即Role Based Access Control(基于角色的访问控制,简称为RBAC,SELinux 不推荐使用);
- Type:Subject或者Object的类型。MAC的基础管理思路其实不是针对上面的RBAC,而是所谓的Type Enforcement Access Control(简称TEAC,一般用TE表示)。对进程来说,Type就是Domain。
- Range:Multi-Level Security(MLS)的级别。MLS将系统的进程和文件进行了分级,不同级别的资源需要对应级别的进程才能访问。
每一个对象都有一个Class,比如一个文件,它是File Class 类型,每个Class 类型都会根据实际情况定义权限类型项,如:read、write、exec、ioctl、append 等等。
Subject 即前面提到的主体,它能够产生访问行为,Linux 里面通常是一个process,但process 本身也可能是一个Object,比如另外一个进程对它发送Signal,去对它进行Ptrace 操作。
SELinux supports two forms of MAC
(1) Type Enforcement (TE)
顾名思义,Type Enforcement 是根据Security Label 中的 type 进行权限审查,审查 subject type 对 object type 的某个class 类型中某种permission 是否具有访问权限,是目前使用最为广泛的MAC 审查机制,简单易用。
(2) Multi-Level Security (MLS)
多层安全机制,是基于Bell-La Padula (BLP) 模型,将Subject 和 Object 定义成多层次的安全等级,不同安全等级之间有相关的访问约束,常见的访问约束是 “no write down” 和 “no read up”。它是根据Security Label 里面的最后一个字段label 进行确认的。
目前在Android 中,重点启用了Type Enforcement 机制,Multi-Level Security (MLS) 虽然有定义,但没有深入使用,这里暂时略过。
Type Enforcement Access Control
- 控制语句格式:rule_name source_type target_type:class perm_set;
- rule:控制类型,分成两方面 allow 以及 audit。
- source_type:也叫subject,通常是domain。
- Target_type:代表请求的资源的类型。
- class perm_set:代表对资源访问的操作。
Rule of Type Enforcement
如下表所示:
- allow:赋予某项权限。
-
auditallow:audit含义就是记录某项操作。默认SELinux只记录那些权限检查失败的操作。auditallow则使得权限检查成功的操作也被记录。注意,allowaudit只是允许记录,它和赋予权限没关系。赋予权限必须且只能使用allow语句。
- dontaudit:对那些权限检查失败的操作不做记录。
- neverallow:前面讲过,用来检查安全策略文件中是否有违反该项规则的allow语句。
Domain Transitions
在SELinux 中,我们通常称一个进程是一个domain,一个进程fork 另外一个进程并执行(exec) 一个执行档时,我们往往会涉及到domain 的切换。比如init 进程,SELinux 给予了它很大的权限,而它拉起的服务,我们要限制这个服务的权限,于是就涉及到从一个domain 切换到另外一个domain,不然默认就使用init 进程的domain。
在SELinux 里面有专门的一条语法:type_transition statement。
在准备切换前我们先要确保有相关的权限操作:
- The source domain has permission to transition into the target domain.
源domain 必须有权限切换到这个目标domain;
- The application binary file needs to be executable in the source domain.
源doamin 必须要有执行这个执行档的权限;
- The application binary file needs an entry point into the target domain.
这个执行档必须是目标domain 的入口(Entry Point)。
如下面的demo,init 拉起apache 并且切换到 apache 的domain。
#首先,你得让init_t域中的进程能够执行type为apache_exec_t的文件
allow init_t apache_exec_t : file {read getattr execute};
#然后,你还得告诉SELinux,允许init_t做DT切换以进入apache_t域
allow init_t apache_t : process transition;
#最后,你还得告诉SELinux,切换入口(对应为entrypoint权限)为执行apache_exec_t类型 的文件
allow apache_t apache_exec_t : file entrypoint;
#Domain Transition
type_transition init_t apache_exec_t : process apache_t;
Google 为了方便操作, 定义了专门的 transition 宏, 方便大家操作, 可以参考如:
system/sepolicy/public/te_macros 文件。
Target Transitions
一个进程在一个目录下创建文件时,默认是沿用父目录的Security Context,如果要设置成特定的Label,就必须进行Object Transitions。
同样是使用type_transition statement。
对应的必须有两个前提条件:
1)The source domain needs permission to add file entries into the directory
这个process 必须有在这个目录下添加文件的权限。
2)The source domain needs permission to create file entries
这个process 必须有在这个目录下创建以这个Security Context 为Label 的文件权限。
下面是一个demo,ext_gateway_t 这个domain 在类型为in_queue_t 的目录下,创建类型为 in_file_t 的文件。
#首先,你得让ext_gateway_t 对in_queue_t 目录具备访问权限
allow ext_gateway_t in_queue_t : dir { write search add_name };
#然后,你还得告诉SELinux,允许ext_gateway_t 访问in_file_t的文件
allow ext_gateway_t in_file_t : file { write create getattr };
#Object Transition
type_transition ext_gateway_t in_queue_t : file in_file_t;
同样Google 也为此定义了专门的target transition 宏,以方便大家。参考system/sepolicy/public/te_macros 文件。
SELinux File System
SELinux 在Kernel 中实现,统一对外的接口都集中在SELinux File System 当中,即通过读写相关文件的方式来达成。
SELinux File System 在比较新的Linux Kernel 中被mount 在/sys/fs/selinux。老的版本就直接是/selinuxfs。
具体每一只文件的说明,请参考:The SELinux Notebook: 2.19.2.3 SELinux Filesystem
SELinux Lib
所有SELinux Lib 几乎都是对selinuxfs 的封装,具体的API 可以参考:The SELinux Notebook: 9. APPENDIX B – LIBSELINUX LIBRARY FUNCTIONS
在android 上,Google 有进一步对这些API 进行封装,具体都在/external/libselinux 下面。(参考: http://selinuxproject.org/page/NB_SEforAndroid_1)
SELinux Java API
Google 进一步将部分重要的SELinxu API 包装成了Java API。
对应的代码是:
Java API:frameworks/base/core/java/android/os/SELinux.java
JNI Code:frameworks/base/core/jni/android_os_SELinux.cpp
对应的API 描述 (参考: http://selinuxproject.org/page/NB_SEforAndroid_1)。
SELinux Commands
SELinux Commands可以参考(http://selinuxproject.org/page/NB_SEforAndroid_1)。
SELinux Policy
SELinux Policy Path
路径:Google Original,android/external/sepolicy/XXX.te (通常不建议修改)
所有的Policy 最终采用Union(合并) 的方式编译在一起。
SELinux Files in Phone
SELinux Policy 手机上关键性的配置文件包括:
- /sepolicy:所有SELinux Policy 编译后的sepolicy 文件
- /file_contexts:系统文件以及device 所对应的security context
- /seapp_contexts:zygote 设置app 的security context 的配置项
- /service_contexts:service 所绑定的security context
- /property_contexts:property 所绑定的security context
- /system/etc/security/mac_permissions.xml:定义app 的security context 类型
- /selinux_version:对应system property: ro.build.fingerprint
- /selinux_network.sh:设置selinux 对网络端的访问
- 临时性设置/data/security/current/
KK 上SELinux 是实验性质的,相关如file_contexts, seapp_contexts 等都优选/data/security/current 下面的特别文件。
L 版本上,在android.c 中使用了特别的判定函数 set_policy_index用来比较
/selinux_version
/data/security/current/selinux_version
当两只文件都存在,并且完全一致,才会使用/data/security/current 下面的文件,其它情况下都会使用根目录的配置文件。
selinux_version 中存储的是build 的BUILD_FINGERPRINT 即system property: ro.build.fingerprint
SELinux Update in O overview
Google android O 8.0 开始,对android 架构进行大幅度的更新,一个最为重要的方面是切割Google/SoC Vendor/ODM 三者的复杂关联性,让android 单独升级成为可能。
注: Google: Android O LKM feature is disabled due to ODM partition OTA not ready. Expect to ready in P. 所以实际odm.img 还和vendor.img 结合。
而为了防止System.img / Vendor.img 中数据、服务、应用等相互的依赖、调用、引用。Google 大幅度的更新了selinux policy,进行严格的限制。
比如严格限制了system image 和 vendor image 之间的socket,binder 通讯。只能通过HIDL,HwBinder 进行交互。下面我们详细的讲到具体的更新变化。
sepolicy 分离
伴随着system image 和 vendor image 的分离,自然也会伴随着sepolicy 的分离,从以往集中放在bootimage,到分离存放到system image 以及 vendor image。原则上与system 相关的sepolicy 就存放system image,与SoC vendor 相关的sepolicy 就存放在vendor image。
对应sepolicy,Google 也设定了不同的存放目录,以便进行分离,以Google 默认的sepolicy 为例:/system/sepolicy
- Public:android 和 vendor 共享的sepolicy 定义,通常情况下,意味着vendor 开发者可能为此新增一些权限。一般system/vendor 共用的一些类型和属性的定义,neverallow 限制等会存放于此。
- Private:通常意义上的仅限于system image 内部的使用,不对vendor 开放。这个只会编译到system image 中。
- Vendor:它仅仅能引用public 目录下的相关定义,这个只会编译到vendor image 中。但它依旧可以对system image 里面的module 设定sepolicy(对应module 需要在public 下进行声明);在很大程度上绕过了Google GTS 约束测试。
- Mapping:为兼容老版本的sepolicy 而导入,只有在system image version > vendor version 的时候,才可能被用到。即包括两方面,新版本增加的type,新版本移除的type,以及老版本public,新版本private 等变化的设定。以兼容老版本。
对应Google 的描述, 可以参考: /system/sepolicy/android.mk
最后对于手机来说生成的selinux policy 位于:
- System android sepolicy: system/etc/selinux
- Vendor ODM sepolicy: vendor/etc/selinux
注意没有如以往一样直接编译成sepolicy binary,而是使用selinux CIL 描述。具体可以参考:https://github.com/SELinuxProject/cil