Android安全模型简介之SEAndroid
Android平台的基础是Linux内核,android每个应用都运行在自己的沙盒中,在Android4.3之前的版本中,android会给每一个应用分配一个独一无二的ID,所以每个应用都有自己的权限边界,从Android4.3开始,android引入了SELinux,并加以适配,构成了SEAndroid,进一步定义Android应用沙盒的边界,运行在单独的进程中,所以每个应用都有自己的权限边界,系统负责管理Android应用对资源的访问权限。作为Android安全模型的一部分,Android使用SELinux的强制访问控制(MAC) 来管理所有的进程,即使是进程具有root(超级用户权限)的能力,SELinux通过创建安全策略(sepolicy)来限制特权进程来增强Android的安全性。从Android4.4开始Android打开了SELinux的Enforcing模式,使其工作在默认的AOSP代码库定义的安全策略(sepolicy)下。在Enforcing模式下,违反SELinux安全策略的的行为都会被阻止,所有不合法的访问都会记录在dmesg和logcat中。
1 访问控制机制
1.1 DAC
全称是Discretionary Access Control,翻译为自主访问控制。DAC的核心思想很简单,其原理就是:进程理论上所拥有的权限与执行它的用户的权限相同。比如,以root用户启动Browser,那么Browser就有root用户的权限,在Linux系统上能干任何事情。DAC是传统的Linux的访问控制方式,DAC可以对文件、文件夹、共享资源等进行访问控制。 在DAC这种模型中,文件客体的所有者(或者管理员)负责管理访问控制。DAC使用了ACL(Access Control List,访问控制列表)来给非管理者用户提供不同的权限,而root用户对文件系统有完全自由的控制权。
1.2 MAC
由于DAC的管理太过宽松,所以NSA设计了一种新的安全模型,叫MAC(Mandatory Access Control),翻译为强制访问控制。MAC的理论也很简单,MAC核心思想:即任何进程想在SELinux系统中干任何事情,都必须先在安全策略配置文件中赋予权限。凡是没有出现在安全策略配置文件中的权限,进程就没有该权限。这个机制相当于一个白名单,这个白名单上配置了所有进程的权限,进程只能做白名单上权限内的事情,一旦它想做一个不属于它权限的操作就会被拒绝。
MAC不再像DAC一样简单的把进程分为root others等,而是每个进程(Subject,主体)和文件(Object,客体)都配置了一个类型(Type),当一个进程去操控(读写等)一个文件时,系统会检测该进程类型是否有对该文件类型的操作权限。
通过命令ps -Z 可以查看进程的安全label,比如进程安全上下文中的com.mediatek.ims进程,他的type为radio,下面的进程的type为platform_app。(其实进程的type也被称为Domain )
图1 ps -Z 输出
ls -Z 可以看到文件的label(安全上下文),文件安全上下文中的bugreports的type为rootfs,
图2 ls -Z 输出
1.3 总结
关于DAC和MAC,可以总结几个知识点:
- Linux系统先做DAC检查。如果没有通过DAC权限检查,则操作直接失败。通过DAC检查之后,再做MAC权限检查。
- SELinux有自己的一套规则来编写安全策略文件,这套规则被称之为SELinux Policy语言。
2 SEPolicy语言
Linux中有两种东西,一种死的(Inactive),我们称之为客体(Object),一种活的(Active),我们称之为主体(Subject)。死的东西就是文件(Linux哲学,万物皆文件。注意,万不可狭义解释为File),而活的东西就是进程。此处的 死 和 活 是一种比喻,映射到软件层面的意思是:进程能发起动作,例如它能打开文件并操作它。而文件只能被进程操作。
3 安全策略
3.1 SecurityContext
SEAndroid通过type(Security Context)来决定应用的访问权限,Security Context格式:
User:Role:Type:SecurityLevel 比如 u:r:system_app:s0 system 2656 501 0 20:59:02 ? 00:00:00 com.mediatek.providers.drm
User
参考以上案例,u为user的意思,SEAndroid中定义了一个SELinux用户,值为u。
Role
r为role的意思,role是角色之意,它是SELinux 中一个比较高层次,更方便的权限管理思路。简单点说,一个u可以属于多个role,不同的role具有不同的权限。Android中也只有一个role,值为r。
Type
代表该进程所属的Domain为system_app。MAC(Mandatory Access Control)强制访问控制的基础管理思路其实是Type Enforcement Access Control(简称TEAC,一般用TE表示),对进程来说,Type就是Domain,比如system_app需要什么权限,都需要通过allow语句在te文件中进行说明。
SecurityLevel
s0是SELinux为了满足军用和教育行业而设计的Multi-Level Security(MLS)机制有关。简单点说,MLS将系统的进程和文件进行了分级,不同级别的资源需要对应级别的进程才能访问。Android下默认通常为s0。
3.2 te
SELinux使用类型强制来改进强制访问控制。所有的主体(程序进程)对客体(文件/socket等资源)的访问都有一条TE规则来许可。在TE中,所有的东西都被抽象成类型。进程抽象成类型;资源抽象成类型。属性是类型的集合。所以TE规则中的最小单位就是类型。当程序访问一个资源的时候,系统会搜索所有的TE规则集,并根据结果进行处理。这个规则集是由访问向量规则(AV,Access Vector)来描述的。
3.2.1 语法
SELinux语法如下图所示,下图的语句表示允许mediaserver进程对app_data_file类型的文件(file)进行rw_file_perms操作。
图3 selinux语法
- 属性和类型
声明类型用type关键字:
type 类型名称 [alias 别名集] [,属性集]; type XXXXX; type alias {aa,bb,cc} XX,YY; 属性用逗号分开
我们还可以为类型添加别名:
# 这两条语句等同于 type mozilla_t, domain; typealias mozilla_t alias netscape_t; # 下面这一条语句 type mozilla_t alias netscape_t, domain;
声明属性用,android属性定义文件在android/external/sepolicy/attributes文件中,这里定义了系统所需的几乎全部的属性。
# All types used for processes. attribute domain; # All types used for files that can exist on a labeled fs. # Do not use for pseudo file types. attribute file_type; # All types used for domain entry points. attribute exec_type; # All types used for /data files. attribute data_file_type; # All domains used for apps. attribute appdomain; # All domains used for apps with network access. attribute netdomain;
- 关联属性和类型
有两种方法可以将某个类型跟某个属性关联起来。第一,在使用type声明类型的时候就关联已经定义的属性;第二,使用typeattribute进行属性关联。
# /data/data subdirectories - app sandboxes type app_data_file, file_type, data_file_type; # /data/data subdirectory for system UID apps. type system_app_data_file, file_type, data_file_type, mlstrustedobject; # Compatibility with type name used in Android 4.3 and 4.4. typealias app_data_file alias platform_app_data_file; typealias app_data_file alias download_file;
type keystore, domain; type keystore_exec, exec_type, file_type; init_daemon_domain(keystore) //////////////////////////////////////// typeattribute keystore mlstrustedsubject;
- 访问向量(AV)规则
allow
表示允许主体对客体执行许可的操作。
neverallow
表示不允许主体对客体执行制定的操作。
dontaudit
表示允许操作并记录访问决策信息。
auditallow
表示不记录违反规则的决策信息,切违反规则不影响运行。
3.2.2 案例1
例如在***.te中书写主体的SecurityContext规则,其具体格式如下:
type subject, attributes; allow/neverallow/dontaudit/auditallow domains types:classes permissions; 比如 allow appdomain app_data_file:file rw_file_perms; 这表示域为appdomain的进程都可以读取和写入带有app_data_file标签的文件
domain
一个进程或一组进程的标签。也称为域类型,因为它只是指进程的类型。
type
一个对象(例如,文件、套接字)或一组对象的标签。
class
要访问的对象(例如,文件、套接字)的类型。
permissions
要执行的操作(例如,读取、写入)。
3.2.3 案例2
以下是init进程的策略配置文件,其中详细描述了init的权限范围。
# 将init关联到domain,即将domain设置为init类型的属性 type init, domain; # 允许init类型对unlabeled类型的filesystem进行mount的操作 allow init unlabeled:filesystem mount; # 允许init类型对fotad类型的unix_stream_socket 进行bind和create的操作 allow init fotad:unix_stream_socket { bind create }; # appdomain是定义在te_macros里面的一个宏,很多的app规则会使用类似app_domain(shell)的命令将其添加进去 # 允许app去对anr_data_file类型的目录进行查找的操作 allow appdomain anr_data_file:dir search; # 允许app对anr_data_file类型的file进行打开和添加操作 allow appdomain anr_data_file:file { open append }; #绝不允许app(除了有unconfineddomain属性的app)对kmem_device类型的字符设备进行读写的操作 neverallow { appdomain -unconfineddomain } kmem_device:chr_file { read write }; # 绝不允许除了unconfineddomain以外的app对self类型的capability2进行任何的操作 neverallow { appdomain -unconfineddomain } self:capability2 *; # 声明一个httpd_user_content_t的类型,具有file_type和httpdcontent的属性 type httpd_user_content_t, file_type, httpdcontent; # 声明一个httpd_user_content_t的类型 type httpd_user_content_t; # 定义httpd_user_content_t具有file_type, httpdcontent的属性 typeattribute httpd_user_content_t file_type, httpdcontent; # 允许所有具有app属性的内容可以去对self属性的rawip_socket进行create的操作 allow appdomain self:rawip_socket create_socket_perms; # 允许user_t和domain属性的类对bin_t, file_type, sbin_t类型的file进行可执行的操作 allow {user_t domain} {bin_t file_type sbin_t}:file execute ; # 这两条语句的表述其实是一致的,其实self指的是目标的类型和发起人的类型是一致的 allow user_t user_t:process signal; allow user_t self:process signal; # 允许user_t对bin_t类型的file进行除了write setattr ioctl相关的操作 allow user_t bin_t:file ~{ write setattr ioctl };
4 SELinux的工作模式
SELinux提供了3种工作模式:Disabled、Permissive和Enforcing,而每种模式都为Linux系统安全提供了不同的好处。
4.1 Disabled
在Disable模式中,SELinux被关闭,默认的DAC访问控制方式被使用。对于那些不需要增强安全性的环境来说,该模式是非常有用的。
4.2 Permissive
在Permissive模式中,SELinux被启用,但安全策略规则并没有被强制执行。当安全策略规则应该拒绝访问时,访问仍然被允许。但是此时会向日志文件发送一条消息,表示该访问应该被拒绝。SELinux Permissive 模式主要用于以下几种情况:审核当前的SELinux策略规则、测试新应用程序,看看将SELinux策略规则应用到这些程序时会有什么效果、调试解决某一特定服务或应用程序在SELinux下不再正常工作的故障。
4.3 Enforcing
从此模式的名称就可以看出,在Enforcing模式中,SELinux被启动,并强制执行所有的安全策略规则,开始限制domain/type 了。
5 如何快速生成策略文件
设置SELinux为宽容模式
在init.rc中会去检查判断kernel_cmdline里面是否有androidboot.seliux变量,当androidboot.seliux值为enforcing为打开,permissive为关上。所以我们在Boardconfig.mk里面对BOARD_KERNEL_CMDLINE进行添加androidboot.selinux=permissive即可将selinux设置为permissive模式用于调试。
static selinux_enforcing_status selinux_status_from_cmdline() { selinux_enforcing_status status = SELINUX_ENFORCING; import_kernel_cmdline(false, [&](const std::string& key, const std::string& value, bool in_qemu) { if (key == "androidboot.selinux" && value == "permissive") { status = SELINUX_PERMISSIVE; } }); return status; }
使用audit2allow自动生成
在permissive模式下,会在logcat以及kmsg中打印avc信息。通过adb shell dmesg | grep avc | grep "pid=***" > avc.txt可以抓取avc信息。
03-25 22:56:15.009 499 499 I httpshell: type=1400 audit(0.0:134): avc: denied { net_raw } for capability=13 scontext=u:r:shell:s0 tcontext=u:r:shell:s0 tclass=capability permissive=1 03-25 22:56:20.869 2833 2833 I re-initialized>: type=1400 audit(0.0:135): avc: denied { read } for name="device.json" dev="dm-0" ino=1785859 scontext=u:r:radio:s0 tcontext=u:object_r:shell_data_file:s0 tclass=file permissive=1 03-25 22:56:20.869 2833 2833 I re-initialized>: type=1400 audit(0.0:136): avc: denied { open } for path="/data/local/tmp/device.json" dev="dm-0" ino=1785859 scontext=u:r:radio:s0 tcontext=u:object_r:shell_data_file:s0 tclass=file permissive=1
然后通过audit2allow指定日志文件自动生成te代码。
audit2allow -i avc.txt > avc.te