1. DAC和MAC
在了解SELinux之前,我们先来了解一下Linux的两种访问控制策略:DAC和MAC
1.1 Linux DAC
DAC,自主访问控制(Discretionary Access control)。系统只提供基本的验证, 完整的访问控制由开发者自己控制。
DAC将资源访问者分成三类:Owner、Group、Other 。
将访问权限也分成三类:read、write、execute
资源针对资源访问者设置不同的访问权限。访问者通常是各个用户的进程,有自己的uid/gid,通过uid/gid 和文件权限匹配, 来确定是否可以访问。DAC机制下,每一个用户进程默认都拥有该用户的所有权限。
DAC 有两个严重问题:
问题一:
因为Root用户是拥有所有权限的,所以DAC对Root用户的限制是无效的。并且在Linux Kernel 2.1以后,Linux将Root权限根据不同的应用场景划分成许多的Root Capabilities, 普通用户也可以被设置某个Root Capability。普通用户如果被设置了CAP_DAC_OVERRIDE, 也可以绕过 DAC 限制。
问题二:
用户进程拥有该用户的所有权限,可以修改/删除该用户的所有文件资源, 难以防止恶意软件。
可见,DAC 有明显的缺陷,一旦被入侵,取得Root权限的用户进程就可以无法无天,胡作非为,早期android版本就深受其害。
1.2 Linux MAC
MAC, 强制性访问控制(Mandatory Access control)。 系统针对每一项访问都进行严格的限制, 具体的限制策略由开发者给出。
Linux MAC 针对DAC 的不足, 要求系统对每一项访问, 每访问一个文件资源都需要根据已经定义好了的策略进行针对性的验证。系统可以针对特定的进程与特定的文件资源来进行权限的控制。即使是root用户,它所属的不同的进程,并不一定能取得root权限,而得要看事先为该进程定义的访问限制策略。如果不能通过MAC 验证,一样无法执行相关的操作。
与DAC相比,MAC访问控制的“主体”变成了“进程”而不是用户。这样可以限制了Root 权限的滥用,另外要求对每一项权限进行了更加完整的细化, 可以限制用户对资源的访问行为。
SELinux就是目前最好的MAC机制,也是目前的行业标准。
2. SELinux概述
2.1 SELinux简介
SELinux,安全增强Linux(Security-Enhanced Linux),是由美国国家安全局(NSA)发起, 多个非营利组织和高校参与开发的强制性安全审查机制(Mandatory Access control,简称MAC)。SELinux最早于2000年12月采用GPL许可发布。目前,Linux Kernel 2.6 及以上的版本都已经集成了SELinux。
2.2 SELinux模式
SELinux 分成三种模式:
- Disabled(关闭),Selinux并没有实际运行。
- Permissve(宽容模式),宽容模式只通过Kernel Audit System 记录LOG, 但不拦截访问。
- Enfocing mode(强制模式),强制模式在记录LOG 的同时,还会拦截访问。
Android 5.x及以上强制开启,因此,disabled(关闭)模式并没有什么用了。 通常在调试时,我们会启用Permissve(宽容模式), 以便尽可能的发现多的问题, 然后一次修正。 在量产时启用Enfocing mode(强制模式)来保护系统。
查看SELinux模式:adb shell getenforce
设置SELinux模式:adb shell setenforce 1 //0是Permissve,1是Enfocing
2.3 SELinux 在Android上的主要变更历史
- Android 4.3 引入SELinux,但采用宽容模式(Permissive)
- Android 4.4 部分强制模式(Enforcing), 针对netd, installd, zygote, vold 四个原本具有root 权限的process, 以及它们fork 出的子进程启用强制模式(Enforcing)。
- Android 5.x 及更高版本,所有域均处于强制模式(Enforcing)。
- Android 8.0 Google 为了实现vendor.img、system.img可独立升级,不相互影响,将Vendor和System分离,大幅度的增强了SElinux 的限制, 限制System/Vendor 之间的交叉使用。
3. SELinux 基础
3.1 SELinux 基本概念
SELinux 的访问控制示意图:
- Subject:访问主体,通常是指能够产生访问行为的对象, Linux 中通常是一个process。
- Object :访问对象, Linux 中通常是文件,但process 也可能是一个访问对象, 比如另外一个进程对它发送Signal, 比如去对它进行Ptrace 操作。
- Object Manager 对象管理器, 即可以知道Subject 需要访问哪些资源,并且触发验证机制
- Security Server 即安全服务器, 用来验证某个Subject 是否可以真正的访问某个Object, 而这个验证机制是基于定义好的Security Policy.
- Security Policy 是一种描述SELinux Policy 的语言.
- Access Vector Cache (AVC) 是访问缓存, 用来记录以往的访问验证情况, 以便提供效率,快速处理.
通常我们开发的过程中,就是配置Subject、Object、Security Policy。
3.2 Security Context
SELinux 给Linux 的所有对象都分配一个安全上下文(Security Context), 描述成一个标准的字符串。
3.2.1 安全上下文基本概念
安全上下文的标准格式: user:role:type[:range]
u:object_r:sky_script_exec:s0
- User: 用户, 非Linux UID,在SEAndroid中,只定义了一个SElinux用户那就是:u
- Role: 角色,一个user可以属于多个role,不同的role具有不同的权限。在SEAndroid中,定义了两个role: r 和 object_r。文件或属性的角色通常是:object_r,而进程的角色通常是:r。
- Type: Subject或者Object的类型。每一个对象都有一个类型(Class),每个类型(Class)都会根据实际情况定义权限类型项, 如: read, write, exec, ioctl, append 等等。对于作为Object的文件, 它是File类型(Class)。 而对于作为Subject的进程来说,它的类型就是Domain。
- Range: Multi-Level Security(MLS)的级别。MLS将系统的进程和文件进行了分级,MLS的low_level 是s0, high level 是s0:c0.c1023。
3.2.2 Security Label
Security Label 用来绑定被访问资源和安全上下文,描述它们的对应关系。标准格式为:resource security_context。即:res user:role:type[:range]。这里也可以使用通配符,例如 net.就可以绑定所有以net.开头的属性,除此之外,还有类似正则表达式的*、?等等通配符。Security Label 都定义在type_contexts当中,例如file的定义在file_contexts中,service定义在service_contexts中,property定义在property_contexts中。
举例:
system\sepolicy\private\file_contexts:
# Root / u:object_r:rootfs:s0 # Data files /adb_keys u:object_r:adb_keys_file:s0 /build\.prop u:object_r:rootfs:s0 /default\.prop u:object_r:rootfs:s0 /fstab\..* u:object_r:rootfs:s0
3.2.3 查看安全上下文
查看进程安全上下文:ps -AZ。例如,查看Settings进程的安全上下文,ps -AZ | grep settings:
u:r:system_app:s0 system 1381 585 4234504 201072 0 0 S com.android.settings
查看文件安全上下文:ls -Z。例如,查看文件build.prop的安全上下文:
u:object_r:system_file:s0 build.prop
3.3 Type Enforcement Access Control
Type Enforcement (TE) 是根据Security Context中的 type 进行权限审查, 审查 subject type 对 object type 的某个class 类型中某种permission 是否具有访问权限,是目前使用最为广泛的MAC 审查机制, 简单易用。
3.3.1 Type Enforcement基本概念
TE控制语句格式 : rule_name source_type target_type : class perm_set
例子:allow system_app tcpdump_file:file { create_file_perms };
- rule: 控制类型, 分为:allow 、auditallow 、neverallow
- source_type:也叫subject,通常是domain,可以是一组subject,用{}包含起来。
- target_type: 代表请求的资源的类型,可以是一组target,用{}包含起来。
- class 类型 一般有file 和 dir
- perm_set: 代表对资源访问的操作,perm_set可以是一组,用{}包含起来。
3.3.2 Type Enforcement规则
Type Enforcement规则说明:
- allow:赋予某项权限。
- auditallow:audit含义就是记录某项操作。默认SELinux只记录那些权限检查失败的操作。 auditallow则使得权限检查成功的操作也被记录。注意,allowaudit只是允许记录,它和赋予权限没关系。赋予权限必须且只能使用allow语句。
- dontaudit:对那些权限检查失败的操作不做记录。
- neverallow:用来检查安全策略文件中是否有违反该项规则的allow语句
举个例子,logd.te、tombstoned.te中定义的TE规则:
allow logd runtime_event_log_tags_file:file rw_file_perms;
dontaudit domain runtime_event_log_tags_file:file { open read };
auditallow tombstoned anr_data_file:file { append write };
neverallow logd { app_data_file system_data_file }:dir_file_class_set write;
3.3.3 Attribute and Type
SELinux 中每一个进程或者文件都对应一个type, 而每一个type 都对应有一个或几个attribute。所有常见的attribute定义在以下文件中:
system/sepolicy/public/attributes
system/sepolicy/prebuilts/api/[build version]/public/attributes
system/sepolicy/prebuilts/api/[build version]/private/attributes
其中的[build version]即为android版本号,例如android O为28.0。常见的attribute定义:
# Attribute declarations # # All types used for devices. # On change, update CHECK_FC_ASSERT_ATTRS # in tools/checkfc.c attribute dev_type; # Attribute for block devices. attribute bdev_type; # All types used for processes. attribute domain; # All types used for filesystems. # On change, update CHECK_FC_ASSERT_ATTRS # definition in tools/checkfc.c. attribute fs_type;
Type对应一个或者几个attribute,Type的定义格式:
type type_name, attribute1, attribute2;
Type的定义通常分散在各个te文件中。例如,常用普通文件的type定义在file.te中
type tcpdump_file, file_type, data_file_type, core_data_file_type;
3.3.4 Classes and Permissions
SEAndroid对于不同的资源类型,定义了不同的Class。比如普通的file、socket等等,比如SELinux 使用的security, 比如针对每个process 参数的process 等定义相关的class。这些class,每一个class 都有相对应的permissions。 比如file 就有 read, write, create, getattr, setattr, lock, ioctl 等等. 比如process 就有fork, sigchld, sigkill, ptrace, getpgid, setpgid 等等。这些相关的class, 以及他们具有那些Permissions都定义在以下文件中:
system/sepolicy/private/access_vectors
system/sepolicy/reqd_mask/access_vectors
system/sepolicy/prebuilts/api/版本号/private/access_vectors
定义完之后,在以下对应的security_classes 文件中声明定义的classes。
system/sepolicy/private/security_classes
system/sepolicy/reqd_mask/security_classes
system/sepolicy/prebuilts/api/版本号/private/security_classes
3.4 Domain and Object Transitions
3.4.1 Domain Transitions
在SELinux 中, 我们通常称一个进程是一个domain, 一个进程fork 另外一个进程并执行(exec) 一个进程时, 我们往往会涉及到domain 的切换. 比如init 进程, SELinux 给予了它很大的权限, 而它拉起的服务, 我们要限制这个服务的权限,于是就涉及到从一个domain 切换到另外一个domain, 不然默认就使用init 进程的domain.
在SELinux 里面有专门的一条语法: type_transition statement.
在准备切换前我们先要确保有相关的权限操作:
- 源domain 必须有权限切换到这个目标domain。(The source domain has permission to transition into the target domain.)
- 源domain 必须要有这个执行档的执行权限。(The application binary file needs to be executable in the source domain.)
- 这个执行档必须是目标domain 的入口。(The application binary file needs an entry point into the target domain.)
如下面的demo, init 拉起apache 并且切换到 apache 的domain.
(1). 首先,你得让init_t域中的进程能够执行type为apache_exec_t的文件
allow init_t apache_exec_t : file {read getattr execute};
(2). 然后,你还得告诉SELinux,允许init_t做DT切换以进入apache_t域
allow init_t apache_t : process transition;
(3). 然后,你还得告诉SELinux,切换入口(对应为entrypoint权限)为执行apache_exec_t类型 的文件
allow apache_t apache_exec_t : file entrypoint;
(4).最后,Domain Transition
type_transition init_t apache_exec_t : process apache_t;
可以看到,整个domain切换过程写起来非常麻烦。因此,Google 为了使用方便, 在system/sepolicy/public/te_macros 文件中定义了宏:
我们可以使用这些宏来完成domain切换。
举例:
kernel启动init进程切换domain:
domain_auto_trans(kernel, init_exec, init)
init启动netd、vold、zygote、installd切换domain:
init_daemon_domain(netd)
init_daemon_domain(vold)
init_daemon_domain(zygote)
init_daemon_domain(installd)
3.4.2 Target Transitions
一个进程在一个目录下创建文件时, 默认是沿用父目录的Security Context, 如果要设置成特定的Label, 就必须进行Object Transitions.
同样是使用:type_transition statement.
对应的必须有两个前提条件:
- 源domain 必须有在这个目录下添加文件的权限。(The source domain needs permission to add file entries into the directory)
- 源domain 必须有在这个目录下创建以这个Security Context 为Label 的文件权限。(The source domain needs permission to create file entries)
下面是一个demo, ext_gateway_t 这个domain 在类型为in_queue_t 的目录下,创建类型为 in_file_t 的文件.
(1). 首先,你得让ext_gateway_t 对in_queue_t 目录具备访问权限
allow ext_gateway_t in_queue_t : dir { write search add_name };
(2). 然后,你还得告诉SELinux,允许ext_gateway_t 访问in_file_t的文件
allow ext_gateway_t in_file_t : file { write create getattr };
(3).最后,Object Transition
type_transition ext_gateway_t in_queue_t : file in_file_t;
同样的,为了方便使用,Google 也在system/sepolicy/public/te_macros 文件中定义了宏:
使用举例:
file_type_auto_trans(factory, system_data_file, factory_data_file)