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 dyntransitionneverallow 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:s0SELinux在适当的域中运行相应服务。 - 编译并刷写启动映像和系统映像
- 优化相应域的
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 |
工具自动生成,通常给的权限范围会过大,需要手动细化,满足最小权限原则。
后续
- 域转换
- 角色转换
- 实例分析