深入浅出SELinux

本文主要描述了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 的整体结构

SELinux 包含五个基本组成:

  • 用于处理文件系统的辅助模块, 即SELinuxFS;
  • 集成Linux Security Modules 的hooks sets;
  • Security Policy Database;
  • Security Label 验证模块;
  • Access Vector Cache (AVC),访问向量缓存,以便提高验证速度。

基本的访问流程如下图所示:

SELinux 的访问流程图

流程如下:

  • 进程通过系统调用(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 上的更新历史

如下图所描述:

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 的同时,还会真正的拦截访问。

SELinux 在Android 启动模式变化图

从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 的核心组件可以参考下面的图:

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

如下表所示:

TEAC-Rule图

  • 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 单独升级成为可能。

android O img调整关系图

注: 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,进行严格的限制。

android O System / Vendor访问限制示意图

比如严格限制了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

Camera课程

Python教程

Java教程

Web教程

数据库教程

图形图像教程

办公软件教程

Linux教程

计算机教程

大数据教程

开发工具教程