一、SE 概述

SELinux 是由美国NSA(国安局)和 SCC 开发的 Linux的一个扩张强制访问控制安全模块。原先是在Fluke上开发的,2000年以 GNU GPL 发布。从 fedora core 2开始, 2.6内核的版本都支持SELinux。

 

1.1、DAC 与 MAC

在 SELinux 出现之前,Linux 上的使用的安全模型是 DAC( Discretionary Access Control 自主访问控制)。

DAC 的核心思想很简单:进程理论上所拥有的权限与执行它的用户的权限相同。例如,以 root 用户启动 Browser,那么 Browser 就有 root 用户的权限,在 Linux 系统上能干任何事情。

显然,DAD 管理太过宽松,只要想办法在系统上获取到 root 权限就可以了。所以后面有了 SELinux,在 DAC 之外,SELinux设计了一种新的安全模型,叫 MAC(Mandatory Access Control 强制访问控制)。

MAC 的核心思想也很简单:任何进程想在 SELinux 系统上干任何事情,都必须在《安全策略文件》中赋予权限。

实际使用中,Linux 系统先做 DAC 检查。如果没有通过 DAC 权限检查,则操作直接失败。通过 DAC 检查之后,再做 MAC 权限检查。关于 MAC 的安全策略文件,SELinux 有自己的一套规则来编写,这套规则被称之为 SELinux Policy 语言。

 

1.2、SEAndroid

Google在 Android 4.4 上正式添加以 SELinux 为基础的系统安全机制,命名为SEAndroid。SEAndroid 在架构和机制上与 SELinux 完全一样,基于移动设备的特点,SEAndroid 的只是所以移植 SELinux 的一个子集。SEAndroid 的安全检查覆盖了包括域转换、类型转换、进程相关操作、内核相关操作、文件目录相关操作、文件系统相关操作、对设备相关操作、对app相关操作、对网络相关操作、对IPC相关操作等内容。

 

1.3、SELinux Policy

Linux 中有两种东西,一种死的(Inactive),一种活的(Active)。死的东西就是文件(Linux哲学,万物皆文件。注意,万不可狭义解释为File);而活的东西就是进程。进程能发起动作,操作文件;而文件只能被进程操作。 根据 SELinux 规范,进程就相当于主体,文件就相当于客体。主体作为操作者,客体作为被操作者。

在 SELinux 中,每种东西都会被 Policy 赋予一个安全属性,官方说法叫做 Security Context,Security Context 是一个字符串。根据 SELinux 规范,完整的 Secure Context 字符串主要由三个部分组成,如下:

user:role:type[:range]

进程的 Security Context 可通过 ps -(A)Z 命令查看:

# ps -Z
LABEL                          USER           PID  PPID     VSZ    RSS WCHAN            ADDR S NAME
u:r:su:s0                      root          4017  3958   10040   2664 SyS_rt_si+          0 S sh
u:r:su:s0                      root          4113  4017   12184   3244 0                   0 R ps
u:r:platform_app:s0:c512,c768  u0_a32        3296   588 4144276  83536 SyS_epoll+          0 S com.android.mms

 文件的 Secure Context 可以通过 ls (file) -Z 来查看:

# ls ./data/ -Z
u:object_r:adb_data_file:s0           adb    
u:object_r:aee_exp_data_file:s0       aee_exp
  • u :user,SEAndroid 中定义了一个用户
  • r : role,一个 u 可以属于多个 role,不同的 role 具有不同的权限。在SEAndroid中的role有两个,分别为 r 和 object_r
  • object_r:role,文件是死的东西,在 SELinux 中,死的东西都用 object_r 来表示它的 role
  • platform_app :type,表示该进程所属的 type 为 platform_app
  • adb_data_file :type,表示 adb 文件夹所属的 type 为 adb_data_file
  • s0:c512 : sensitivity,category ,是 SELinux 为了满足军用和教育行业而设计的 Multi-Level Security(MLS)机制。MLS 将系统的进程和文件进行了分级,不同级别的资源需要对应级别的进程才能访问

Policy 是整个安全机制的核心之一,在 SEAndroid 中主要采用 Policy 中实现的两种强制访问方法:

  • TE
  • MLS

 

1.4、TE(Type Enforcement)

TE 是 SEAndroid 中的最主要的安全手段。MAC 的管控策略其实也就是 Type Enforcement Access Control(TE 强制访问控制)。所有关于TE的强制访问规则都被定义在了后缀为te的文件中,比如 platform_app 进程拥有什么权限,都通过在 te 文件中添加 allow 语句进行说明。

SEAndroid 在 te 文件中定义了安全策略中最基本的参量 type,同时将具有共性的 type 归在一起构成一个称为 attribute 的集合,policy 的规则执行就能以 attribute 作为执行对象。

Android 9.0上,SEAndroid 为所有 type 共定义了71个 attribute,定义文件路径:

system/sepolicy/public/attributes

里面对每一种 attribute 都注释说明了,这样我们就知道我们的 type 是属于哪一种 attribute 。

例举几种常见的:

domain所有的进程 type
fs_type所有属于文件系统的 type,例如 sys 目录下创建的节点文件
file_type所有带标签的文件 type,包括data目录下文件及 bin 文件
exec_type可执行文件 type
data_file_type/data 目录下文件 type
core_data_file_type/data 目录下且不在 /data/vendor 目录下的文件 type
vendor_file_type/vendor 目录下的文件 type
proc_type所有procfs文件 type
sysfs_type所有sysfs文件 type

 

1.5、MLS(Multi-Level Security)

MLS (多级别安全)是另一种强制访问控制方法,特别适合于政府机密数据的访问控制,早期对计算机安全的研究大多数都是以在操作系统内实现 MLS 访问控制为目标的。所有 MLS 的使用都是建立在 TE 安全的基础之上。在SELinux中MLS是一个可选访问控制方式,而在SEAndroid中则是被作为安全访问方式的其中之一。 

1.5.1、MLS中的相关参量

在 SEAndroid 中 Secure Context 的第四列称为 security level,在安全上下文第四列中可以有一个或者两个security level,第一个表示低安全级别,第二个表示高安全级别。

每个security level由两个字段组成:

  • sensitivity

sensitivity有着严格的分级,它反应了一个有序的数据灵敏度模型,如政府分类控制中的绝密,机密和无密级。

  • category

category是无序的,它反应的是数据划分的需要。

要访问的数据你同时需要足够的sensivity和正确的category。在SEAndroid中 sensitivity 只有一个级别即s0,category共有1024个,因此最低安全级别就是s0,最高安全级别就是s0:c0.c1023。

security level之间的三种运算关系:

  • dom

需要主体sensitiviety大于客体,同时客体的category是主体的一个子集。

  • domby

与dom完全相反

  • eq

主体客体的sensitivity和category分别相同。

高的security level对低的security level拥有dom,低的security level对高的security level关系为domby(与dom相反),同级的security关系是eq,这三种关系运算符是SEAndroid中特有的。

1.5.2、MLS对进程的约束

  • 限制进程的domain转换

对于从一个domain转换到另一个domain的操作要求两个domain的高级别security level和低级别security level同时相等才能许可转换,除非是待转换的domain属于对mls无限权限的type。

  • 限制进程读操作

只有当主体domain的低级别security level对客体domain的低级别security level具有dom关系时,或者主体domian是属于对mls无限权限的type,主体才能对客体具有读操作的许可。

这一读操作具体是指:

  1. 能获取进程优先权
  2. 能获取另一进程的session id
  3. 能获取到进程的group id
  4. 能获取到系统的capabilities
  5. 能获取文件的属性信息
  6. 能追踪父程序或子程序的执行
  7. 允许通过clone或fork进程实现状态信息共享

总结一下就是MLS限制了低级别进程向高级别进程进行读的操作,即不能上读。

  • 限制进程写操作

只有当主体domain的低级别security level对客体domain的低级别security level具有domby关系时,或者主体domain是属于对mls无限权限的type,主体才能对客体具有写操作的许可。

写操作具体是指:

  1. 能发送SIGKILL信号
  2. 能发送SIGSTOP信号
  3. 能发送一个非SIGKILL、SIGSTOP、SIGCHLD的信号
  4. 能设置进程优先级
  5. 能设置进程group id
  6. 能设置系统的capabilities
  7. 能改变进程hard limits
  8. 能追踪父程序或子程序的执行
  9. 允许通过clone或fork进程实现状态信息共享

总结一下就是MLS限制了高级别进程对低级别进程写的操作,即不能下写。

1.5.3、MLS对socket的约束

  • 进程对local socket的访问限制

只有当主体进程的domain的高级别security level和低级别security level分别与客体local socket的type的security level相同时即满足eq关系时,或者主体客体任何一个具有对mls无限权限的type时,主体进程才对local socket拥有了某些访问权限。

这些访问权限是指:

  1. 读操作
  2. 写操作
  3. 新建操作
  4. 能获取对象属性信息
  5. 能设置对象的属性信息
  6. 能对对象重新标记安全上下文
  7. 能进行bind操作
  8. 能发起一个连接请求
  9. 能监听连接事件
  10. 能接受一个连接请求
  11. 能获取到socket options
  12. 能关闭socket连接
  • 对socket的datagram发送的限制

只有当发送方的低级别security level与接受方的低级别security level满足domby关系时,或者主体客体任何一个具有对mls无限权限的type时,发送方才对接受方拥有了发送权限。

  • 对客户端socket和服务端socket建立连接的限制

只有当客户端的低级别security level与服务端的低级别security level满足eq关系时,或者主体客体任何一个具有对mls无限权限的type时,客户端就能获得连接服务端的权限。

1.5.4、MLS对文件和目录的约束

  • 对文件的创建和重新标记的限制

对文件操作时,要求客体的文件安全上下文只有一个security level即没有低级别和高级别security level或者说是这两个级别相同。 当主体domain的低级别security level对客体文件的低级别security level相同时,或者主体具有对mls有无限权限的type时,主体对客体文件拥有创建、和重新标记安全上下文的权限。

  • 对目录的读操作的限制

只有当主体的低级别security level对客体目录的低级别security level满足dom关系时,或者主体客体任何一个具有对mls无限权限的type时,主体能对目录拥有如下权限:

  1. 能读目录
  2. 能获得目录的属性信息
  3. 能获得某个正在被访问文件的所有上层目录访问权(search权限)

总结一下就是对目录的访问不能上读。

  • 对文件的读操作的限制

只有当主体的低级别security level对客体文件的低级别security level满足dom关系时,或者主体客体任何一个具有对mls无限权限的type时,主体能对文件拥有如下权限:

  1. 能读文件
  2. 能获得文件的属性信息
  3. 能执行该文件

总结一下就是对文件的访问不能上读。

  • 对目录的写操作的限制

只有当主体的低级别security level对客体目录的低级别security level满足domby关系时,或者主体客体任何一个具有对mls无限权限的type时,主体能对目录拥有如下权限:

  1. 能对目录写操作
  2. 能设置属性信息
  3. 能重命名目录
  4. 能添加一个文件到目录中
  5. 能从目录中删除一个文件
  6. 能改变父目录
  7. 能删除一个空目录

总结一下就是对目录访问不能下写。

  • 对文件的写操作的限制

只有当主体的低级别security level对客体文件的低级别security level满足domby关系时,或者主体客体任何一个具有对mls无限权限的type时,主体能对文件拥有如下权限:

  1. 能对文件进行写操作
  2. 能设置文件属性信息
  3. 能对文件内容作append操作
  4. 能对文件创建链接
  5. 能删除一个文件的链接
  6. 能对文件重命名

总结一下就是对文件访问不能下写。

1.5.5、MLS对IPC的约束

  • 对IPC创建和销毁的限制

要求客体的IPC对象只有一个security level。

只有当主体的低级别security level与客体的低级别security level满足eq关系时,或者主体具有对mls无限权限的type时,主体能对客体IPC拥有创建和销毁的权限。

  • 对IPC读操作的限制

只有当主体的低级别security level对客体IPC的低级别security level满足dom关系时,或者主体具有对mls无限权限的type时,主体能对客体IPC拥有如下权限:

  1. 获取文件属性信息
  2. 能对IPC文件执行读操作
  3. 能关联一个key
  4. 能执行由IPC操作要求的读操作

总结一下就是对IPC访问不能上读。

  • 对IPC写操作的限制

只有当主体的低级别security level对客体IPC的低级别security level满足domby关系时,或者主体具有对mls无限权限的type时,主体能对客体IPC拥有如下权限:

  1. 能对文件执行write和append操作
  2. 能执行由IPC操作要求的write和append操作

总结一下就是对IPC访问不能下写。

 

二、新增自定义 type

Android 对各种 type 在 file_contexts 进行了定义,并在 file.te 中对 type attribute 进行声明和注册。系统自带的 file_contexts 和 file.te 在 system/sepolicy/ 目录下几个文件夹中,包含所有默认的type。

但在实际使用中,我们可能会遇到需要在 /data 目录下新增一个自定义文件,那么如果使用系统默认的 type ,/data 目录对应 system_data_file ,如果直接对 system_data_file 授 te 权限,可能给的太多了,不符合最小权限安全理念了。这个时候就需要我们再细分一下,例如 /data/adb 文件 type 是 adb_data_file 。

2.1、file_contexts 中添加 type

# device/sepolicy/private/file_contexts 

/data/test(/.*)?                                         u:object_r:test_data_file:s0

再文件中新建 test_data_file type,注意因为是文件(死的),所以 role 是 object_r 。这样就表示 /data/test 目录下文件就都属于 test_data_file 的。

2.2、file.te 中对新建 type 进行关联说明

# device/sepolicy/public/file.te
type test_data_file, file_type, data_file_type, core_data_file_type;

data 目录下文件,就是对应的 file_type data_file_type core_data_file_type 这三种 attribute ,不知道为啥的同学往上面翻翻。如果少写了也没啥事,编译时系统会去check的,会有 error 提示。然后自己加就行。

2.3、修改相关 te allow 权限

一般这一步应该是在看到访问我们新增 data/test/ 目录文件报错时候再根据 logcat 中的 avc 信息去添加。不过我们也可以偷懒,在知道会报啥错情况下,比如 SystemServer 中访问我们新建目录文件,那就去 system_server.te 中添加权限

allow system_server test_data_file:dir { relabelto search };
allow system_server test_data_file:file rw_file_perms;

rw_file_perms 这个宏表示读写所有权限。这样我们就完成一个自定义目录的 te 权限流程。

需要注意,系统的 file_context 加载会在 init 的时候进行,所以这个目录文件创建一定要在加载之前,等到系统服务起来再创建就已经晚了, SELinux 的 context 会因为找不到目录就过滤新建的规则了。

那如果必须要在java层去创建呢?我们可以在创建完成后,执行下如下指令,重新加载下 SELinux Context

restorecon /data/test/test.txt

参考链接

SEAndroid策略

android 8.1 安全机制 — SEAndroid & SELinux

Selinux小结

Logo

更多推荐