seAndroid
安全机制简介。
基本概念
主体/客体
在一个操作系统中,每一个实体组件都必须是主体或者客体,或者既是主体又是客体。
- 主体
主体是一个主动的实体,包括用户、用户组、进程等;通常指用户或由用户发起运行的进程或用户正在使用的设备。主体主动发起对资源的访问,它是系统中信息流的启动者。 - 客体
客体是一个被动的实体,包括文件、目录、端口、设备、进程等资源;通常是指信息的载体或从其他主体或客体接收信息的实体。主体有时也会成为访问或受控的对象,如一个主体可以向另一个主体授权,一个进程可能控制几个子进程等等,这时受控的主体或子进程也通常被认为是一种客体。
访问控制模式
DAC: Discretionary Access Control
自主访问控制DAC
主要的内容是:权限rwx
、所有权ugo
;在这个模型中,主体是用户的身份,客体是资源或者说是文件(Linux
中一切皆文件)。由客体的属主对自己的客体进行管理,由主体自己决定是否将自己的客体访问权限或部分访问权限授予其他主体,这种控制方式是自主的。也就是说,在自主访问控制下,用户可以按自己的意愿,有选择地与其他用户共享他的文件。DAC
是一种相对比较宽松但是却很有效的保护资源不被非法访问和使用的手段,权限是访问的关键;但Linux
中,root
用户不受任何管制,系统上任何资源都可以无限制地访问。MAC: Mandatory Access Control
强制访问控制MAC
是利用策略将访问控制规则“强加”给访问主体的,即系统强制主体服从访问控制策略。MAC
主要作用的对象是所有主体及其所操作的客体(如:进程、文件等)。MAC
为这些主体及其所操作的客体提供安全标记,这些标记是实施强制访问控制的依据。
系统通过比较主体和客体的安全标记来判断一个主体是否能够访问其要操作的客体。用户发起的进程无法改变其自身及其它客体的安全标记,利用这样的机制,系统可以比较有效地防止特洛伊木马攻击以及root
身份冒用或盗用等安全威胁。MAC
又细分为了两种方式:类别安全MCS
模式;多级安全MLS
模式。
MAC
一般与 DAC
共同使用,两种访问控制机制的过滤结果将累积,以此来达到更佳的访问控制效果。也就是说,一个主体只有通过了 DAC
限制检查与 MAC
限制检查的双重过滤装置之后,才能真正访问某个客体。
SELinux
的工作模式
SELinux: Secure Enhanced Linux
是美国国家安全局 NSA: The National Security Agency
和 SCC:Secure Computing Corporation
在 Linux
社区的帮助下开发了 MAC
强制访问控制的安全模块,Linux
从 2.6 版本后将它集成到了内核中。
主体在访问客体时,SELinux
策略决策过程如下图,Kernel
中的策略执行服务器将检查 AVC: Access Vector Cache
,访问矢量缓存中存储的是访问控制策略:
SELinux
有三种工作模式:
enforcing
强制模式,违反SELinux
规则的行为将被阻止(权限拒绝)并记录到日志中。permissive
宽容模式,违反SELinux
规则的行为(权限不会被拒绝)只会记录到日志中,一般为调试用。disabled
关闭SELinux
。
SEAndroid
是在 Android
系统中基于 SELinux
推出的强制访问控制模型,是 SELinux
的一个子集;可以通过 adb
命令来查看/设置模式:
1 | adb shell getenforce // 查看当前模式 |
在 CTS
兼容性测试时,Android
有以下要求官方要求:
- 必须实现
SELinux
- 必须将
SELinux
设置为全局强制模式enforcing
- 必须将所有域配置为强制模式。不允许使用宽容模式域,包括特定于设备/供应商的域
访问控制策略 policy
访问控制策略 policy
都是在 .te: Type Enforcement
文件中定义的,用来控制主体是否能访问客体,以及能放问客体哪些东西;下面先介绍几个用来描述主客体的几个概念。更多规则和关键字等基本概念,参考The SELinux Notebook, 4th Edition(SELinux 手册第 4 版) 。
classes
定义客体的安全类别,可以在 system/sepolicy/private/security_classes
查看:
1 | # Classes marked as userspace are classes |
permissiones
定义每个客体类别支持哪些操作权限,可以在 system/sepolicy/private/access_vectors
中查看:
1 | # Define common prefixes for access vectors |
attribute
属性
attribute
实际是组的概念,表示一组 group
;有了组的概念后,可以每次对一个组设置策略。attribute
可以是主体,也可以是客体;在 system/sepolicy/public/attributes
文件查看:
1 | # All types used for processes. |
从注释中也可以看出,是 All types
所有的类型,接下来看 type
。
type
表示类型,可以理解为主体或客体的名称,表示这一类主体或这一类客体;所有的 type
都是在 .te
文件中定义的,同时会设置对应的策略。
type
直接定义
命名格式:type type_id [alias alias_id,] [attribute_id]
;[]
是可选项,alias
表示假名,attribute_id
可以是多个,表示属于哪一组。
示例:type shell, domain;
;定义了一个名为shell
的type
,它和名为domain
的属性attribute
关联;换句话说shell
属于domain
组。type
和attribute
位于同一个命名空间,所以不能用type
命令和attribute
命令定义相同名字的东西。typeattribute
添加对应属性typeattribute
是针对已经被定义了的type
,添加属性(即属于哪一组),格式:typeattribute type_id attribute_id
。示例typeattribute mediaserver halclientdomain;
,表示mediaserver
拥有halclientdomain
属性,即属于这一组。
policy
策略规则 policy
也是在 .te
文件中声明的,其语法格式为:RULE_VARIANT SOURCE_TYPES TARGET_TYPES:CLASSES PERMISSIONS
,其中:
RULE_VARIANT
常见的访问规则如下,这里仅仅allow
是授予权限允许操作,其他都是辅助型的(都不会授予权限)。allow
授予权限,允许操作(默认情况下只记录权限检查失败的信息)neverallow
不允许操作(通常用来做检查,检查是否有违反规则)auditallow
记录所有的(成功和失败)权限检查事件dontaudit
不记录权限检查失败的信息
SOURCE_TYPES
主体类型,由type
定义。TARGET_TYPES
客体类型,由type
定义。CLASSES
客体的安全类别,即system/sepolicy/private/security_classes
文件中,关键字class
定义的类别。PERMISSIONS
客体安全类别的操作权限,即system/sepolicy/private/access_vectors
文件中,该安全类别对应的操作权限。
policy
描述的含义实际就是:主体对客体的某种类别拥有的权限;示例:
allow appdomain app_data_file:file rw_file_perms;
主体appdomain
对客体app_data_file
的file
类别,拥有读取和写入权限。其中rw_file_perms
是宏定义的一组权限。allow domain null_device:chr_file { open read};
主体domain
对客体null_device
的chr_file
类别,拥有打开和读的权限。
self
关键字
self
表示客体类型使用的主体类型自身,即客体类型等于主体类型;注意:不能使用 self
代表主体类型。示例:
1 | # 这两条策略是相等的 |
特殊操作符
-
非操作符
表示从attribute
一组类型中,移除特定类型;如:allow vndservicemanager { domain -coredomain -init }:binder transfer;
,表示vndservicemanager
除了coredomain, init
以外,所有domain
的binder
类别,都拥有transfer
操作权限。~
求补操作符
表示除了列出的操作权限外,其他的都包含;如allow init unlabeled:filesystem ~relabelto;
。*
通配符
表示任意的;neverallow * logpersist:process dyntransition
neverallow mediacodec domain:{ tcp_socket udp_socket rawip_socket } *;
示例
以下是一个完整的 DHCP
策略示例:
1 | type dhcp, domain; |
type dhcp, domain;
定义一个dhcp
域,它属于domain
组。permissive dhcp;
声明新建的dhcp
域为宽容域(调试完毕后,必须要移除)。type dhcp_exec, exec_type, file_type;
定义dhcp_exec
,同时属于exec_type, file_type
组。init_daemon_domain(dhcp)
声明dhcp
是从init
衍生而来的,并且可以与其通信。init_daemon_domain
等域操作,详细可以参考system/sepolicy/public/te_macros
中的定义。allow dhcp self:packet_socket create_socket_perms;
允许dhcp
创建socket
,权限类create_socket_perms
等参考system/sepolicy/public/global_macros
中的定义。
目录速查表
selinux
代码目录
位于 Android
源码目录的 external/selinux
目录下,external/selinux/prebuilts/bin
有些工具类文件,方便快速分析权限问题。
policy
文件目录
在 Android 8.0
及更高版本中,policy
文件位于 AOSP
中的以下位置:
system/sepolicy/public
其中包括所导出的用于供应商特定策略的策略;公共策略会保留在不同版本上,可以在自定义策略的/public
中添加任何内容。正因如此,可存放在/public
中的策略类型的限制性更强。将此目录视为相应平台的已导出策略API
:处理/system
与/vendor
之间的接口的所有内容都位于这里。system/sepolicy/private
包括系统映像正常运行所必需(但供应商映像策略应该不知道)的策略。system/sepolicy/vendor
包括位于/vendor
但存在于核心平台树(非设备特定目录)中的组件的相关策略。这是编译系统区分设备和全局组件的软件工件;从概念上讲,这是下述设备专用策略的一部分。device/manufacturer/sepolicy/device-name
包含设备专用策略,以及对策略进行的设备自定义(在Android 8.0
及更高版本中,该策略对应于供应商映像组件的相关策略)。
通常情况下,不能直接修改 system/sepolicy
文件,而是添加或修改自己的设备专用策略文件(位于 /device/manufacturer/device-name/sepolicy
目录中)。
宏定义
系统的策略文件中,定义很多宏,方便共享和快速定义策略文件,路径如下:
1 | system/sepolicy/public/ioctl_macros // ioctl |
标签
定义
标签,也被称为安全上下文 security context
,组成元素为:user:role:type:mls_level
,每个元素的意义:
user
指登录系统的用户类型,比如root, user_u, system_u
;但是在SEAndroid
中user
只有一个,都是u
。role
定义文件、进程和用户的用途。在SEAndroid
中的role
只有两个:object_r
表示文件;r
表示进程。type
指定主体和客体的类型。mls_level
指安全级别,格式为sensitivity[:category list][- sensitivity[:category list]]
,冒号后面的内容是category
,“-”号左右分别标识了安全级别的最低和最高;例如s0 - s15:c0.c1023
,其中s0
之后的内容可以不需要。
在SEAndroid
中只有一个级别即s0
,category
共有 1024 个,因此最低安全级别就是s0
,最高安全级别就是s0:c0.c1023
,通常我们就只会看到s0
。
在 SEAndroid
中,标签的角色仅仅只有两个:进程和文件。进程标签的 type
又称为域;所以 policy
也可以理解为 - 进程访问文件权限的规则:allow domains types:classes permissions;
。
在 AVC
消息中,主体上下文(标签)为 scontext
;客体上下文(标签) tcontext
,客体类别 tclass
。
在
Linux
中一切都是文件,所以我们在打标签时,不可能存在r
角色;所有的进程在系统中都是一个可执行文件,所以对于进程的标签通常是:先在te
文件中通过type
定义一个类型;然后在file_contexts
中为进程对应的可执行文件打上该type
标签。
查看标签
我们通过 ls -Z
和 ps -AZ
分别查看文件和进程(两个角色)的标签,示例如下:
1 | // 查看文件角色的标签 |
type
是整个 SEAndroid
中最重要的参量,所有的 policy
都围绕这一参量展开,所以为系统中每个文件标记上合适的 type
就显得极为重要了。通过 *_contexts
上下文描述文件,来给具体的文件或进程打标签。
file_contexts
用于为文件(分为:进程对应的可执行文件(主体)和常规文件(客体))分配标签,并且可供多种用户空间组件使用。在创建新策略时,请创建或更新该文件,以便为文件分配新标签。要应用新的 file_contexts
,请重新构建文件系统映像,或对要重新添加标签的文件运行 restorecon
。在升级时,对 file_contexts
所做的更改会在升级过程中自动应用于系统和用户数据分区。此外还可以通过以下方式使这些更改在升级过程中自动应用于其他分区:在以允许读写的方式装载相应分区后,将 restorecon_recursive
调用添加到 init.board.rc
文件中。 file_contexts
所在目录为:
1 | system/sepolicy/private/file_contexts |
查询 private/file_contexts
文件中的示例:
1 | // system/sepolicy/private/file_contexts |
file_contexts
中的 type
类型,基本都是在如下 .te
文件中定义的:
1 | // file.te |
查询 system/sepolicy/public/file.te
文件中的示例:
1 | # Filesystem types |
service_contexts
用于为 Android Binder
服务分配标签,以便控制哪些进程可以为相应服务添加(注册)和查找(查询) Binder
引用,在启动期间 servicemanager
进程会读取此配置。service_contexts
所在目录:
1 | system/sepolicy/private/service_contexts |
查看 system/sepolicy/private/service_contexts
中的示例:
1 | ... |
service_contexts
中的 type
类型,基本都是在 service.te
文件中定义的:
1 | system/sepolicy/public/service.te |
查看 system/sepolicy/public/service.te
中的示例:
1 | type audioserver_service, service_manager_type; |
其他上下文标签
genfs_contexts
用于为不支持扩展属性的文件系统(例如proc, vfat
)分配标签。此配置会作为内核策略的一部分进行加载,但更改可能对内核inode
无效。要全面应用更改,需要重新启动设备,或卸载并重新装载文件系统。此外通过使用context=mount
选项,可以为装载的特定系统文件(例如vfat
)分配特定标签。property_contexts
用于为Android
系统属性分配标签,以便控制哪些进程可以设置这些属性。在启动期间init
进程会读取此配置。seapp_contexts
用于为应用进程和/data/data
目录分配标签。在每次应用启动时,zygote
进程都会读取此配置;在启动期间installd
会读取此配置。mac_permissions.xml
用于根据应用签名和应用软件包名称(后者可选)为应用分配seinfo
标记。随后分配的seinfo
标记可在seapp_contexts
文件中用作密钥,以便为带有该seinfo
标记的所有应用分配特定标签。在启动期间system_server
会读取此配置。
添加/修改策略 policy
原则
- 采用最小权限原则,仅针对添加的内容调整
SELinux
策略 - 将各个软件组件拆分成多个负责执行单项任务的模块,创建将这些任务与无关功能隔离开来的
SELinux
策略 - 将这些策略放在
/device/manufacturer/sepolicy/device-name
目录中的*.te
文件,然后使用BOARD_SEPOLICY
变量将它们纳入到版本中 - 先将新域设为宽容域:在该域的 .te 文件中使用宽容声明
permissive ***
;分析结果并优化域定义,当版本中不再出现拒绝事件时,移除宽容声明
编译
策略修改和添加,尽量都放在 /device/manufacturer/sepolicy/device-name
目录中实现,方便后续追溯。同时需要在 /device/manufacturer/device-name/BoardConfig.mk
中指定 sepolicy
子目录和每个新的策略文件。详细请参阅 system/sepolicy/README
文件。
1 | BOARD_SEPOLICY_DIRS += \ |
重新进行编译后,新策略设置会自动内置到最终的内核策略文件中。
示例
以下为 Android
官网上提供的示例:
为新服务添加标签并解决拒绝事件,通过 init
启动的服务需要在各自的 SELinux
域中运行。以下示例会将服务 foo
放入它自己的 SELinux
域中并为其授予权限。
该服务是在设备的 init.device.rc
文件中启动的,如下所示:
1 | // 服务进程对应的可执行文件 |
- 创建一个新域
foo
创建包含以下内容的文件device/manufacturer/sepolicy/device-name/foo.te
:1
2
3
4
5
6# 为进程定义一个类型
# foo service
type foo, domain;
type foo_exec, exec_type, file_type;
init_daemon_domain(foo)
这是 foo SELinux
域的初始模板,可以根据该可执行文件执行的具体操作为该模板添加规则。
- 为
/system/bin/foo
添加标签
将以下内容添加到device/manufacturer/sepolicy/device-name/file_contexts
:1
2
3# 为进程对应可执行文件打标签,类型对应的是给进程分配的类型
# file_contexts
/system/bin/foo u:object_r:foo_exec:s0
这可确保为该可执行文件添加适当的标签,以便 SELinux
在适当的域中运行相应服务。
- 编译并刷写启动映像和系统映像
- 优化相应域的
SELinux
规则
根据拒绝事件确定所需的权限。audit2allow
工具提供了一些实用的指南,但该工具仅适用于提供编写政策时所需的信息。切勿只是复制输出内容。
audit
日志分析
SELinux
有大量的工具记录日志信息,或审核、访问尝试被策略允许或拒绝的信息。审核消息通常叫做 AVC
消息,它提供了详细了关于访问尝试的信息,包括是允许还是拒绝,源和目标的安全上下文,以及其它一些访问尝试涉及到资源信息。avc dennied
的示例 log
:
1 | // a.txt |
可以通过 external/selinux/prebuilts/bin/audit2allow
提供的工具来分析:
1 | xmt@server139:~/external/selinux/prebuilts/bin$ audit2allow -i a.txt |
工具自动生成,通常给的权限范围会过大,需要手动细化,满足最小权限原则。
后续
- 域转换
- 角色转换
- 实例分析