1.DAC机制
在引进SEAndroid安全机制之前,Android系统的安全机制分为应用程序和内核两个级别。应用程序级别的安全机制就是我们通常说的Permission机制。一个应用如果需要访问一些系统敏感或者特权资源,那么就必须要在AndroidManifest.xml配置文件中进行申请,并且在安装的时候由用户决定是否赋予相应的权限。应用安装过后,一般是通过系统服务来间接使用系统敏感或者特权资源的。这样系统服务在代表应用使用这些资源之前,就会先检查应用之前是否已经申请过相应的权限。如果已经申请过,那么就直接放行,否则的话,就拒绝执行。
内核级别的安全机制就是传统的Linux UID/GID机制。在linux中,每一个用户都拥有一个用户ID,并且也有一个用户组ID,分别简称为UID和GID。此外,Linux系统的进程和文件也有UID和GID的概念。Linux就是通过用户、进程、文件的UID/GID属性来进行权限管理的。
1.1 Linux是如何切换用户的?
我们知道,当Linux内核启动完成之后,启动的第一个进程称为init进程。Init进程接着会启动一个login进程,等待用户输入用户名和密码登录。一旦登录成功,就会启动一个shell进程。之后这个shell进程就负责执行用户的命令。注意,在上述启动过程中,init进程是以root用户身份启动的,也就是init进程的UID是0,即root。在默认情况下,由init进程启动的所有进程,也就是fork出来的所有子进程,也同样是以root身份运行的。因此。负责登录的login进程的UID也是0。但是,当用户输入用户名和密码,并且登录成功后,所启动的shell进程就不再是root了,而是成功登入系统的用户。这是如何做到的呢?原来,具有root权限的进程可以通过系统接口setuid来改变自己身份。也就是说,由login进程启动的用户shell进程在开始的时候的身份其实也是root的,不过在它可以执行用户的命令之前,它已经通过setuid将自己的UID修改为登录用户对应的UID了。这样就可以限制每一个成功登入系统的用户的权限。
2.Android沙箱的原理
Android是一个基于Linux内核的系统,但是它不像传统的Linux系统,需要用户登录之后才能使用。然而,Android系统又像传统的Linux系统一样有用户的概念。只不过这些用户不需要登录,也可以使用Android系统。具体来说,就是Android系统将每一个安装在系统的APK都映射为一个不同的Linux用户。也就是说,每一个APK都有一个对应的UID和GID。这些UID和GID是在APK安装的时候由系统安装服务PackageManagerService分配的。
APK所运行在的进程是由另外一个系统服务ActivityManagerService负责启动的。ActivityManagerService在启动APK进程之前,会先向PackageManagerService查询APK安装时分配到的UID和GID。有了APK的UID和GID后,ActivityManagerService就向另外一个以root身份运行的zygote进程发出创建APK进程的请求。Zygote进程收到请求之后,就会fork出一个子进程来作为请求创建的APK进程。
zygote进程是以root身份运行的。因此,它fork出来的子进程,也就是APK进程,在一开始的时候也是以root身份运行的。不过,APK进程在可以执行APK代码之前,会通过系统接口setuid将自己的UID设置为APK安装时分配到的UID。这个过程与传统的Linux系统通过login进程启动用户shell进程的过程非常类似。通过这种方式,就可以保证每一个APK进程都以不同的身份来运行,从而保证了相互之间不会受到干扰。这就是所谓的沙箱了,这完全是建立在Linux的UID和GID基础上的。
原理图如下:
2.1基于Linux UID/GID的安全机制存在什么样的问题呢?
在理想情况下,DAC机制是没有问题的。然而,在现实中,会产生严重的安全问题。例如,一个用户可能会不小心将自己创建的文件的权限位错误地修改为允许其它用户访问。如果这个用户是一个特权用户,并且它错误操作的文件是一个敏感的文件,那么就会产生严重的安全问题。这种误操作的产生方式有三种:
用户执行了错误的命令
负责执行用户命令的程序有BUG
负责执行用户命令的程序受到攻击
由此可见,DAC机制只能在理想情况下没有问题,但是在现实中是防不胜防!例如,GingerBread漏洞就是通过攻击以root身份运行Android磁盘管理守护进程vold来获得root权限,从而实现对设备进行root的。
注意,上面我们说的DAC问题虽然是针对内核级别的Linux UID/GID机制,然而同样适用于应用级别的Permission机制。这个问题通过MasterKey漏洞表现得淋漓尽致。我们知道,MasterKey漏洞可以在不改变签名的情况下对APK进行篡改。这会导致什么后果呢?假如被篡改的APK申请有特殊的Permission,那么就意味着嵌入的恶意代码可以任意地使用这些特殊的Permission。
更为严重的是被篡改的APK是一个系统APK。Android系统的Permission分为两种,一种是所有APK都可以申请的,另一种是系统APK才可以申请的。只有系统APK才可以申请的Permission更为敏感,例如用来安装APK的Permission--android.permission.INSTALL_PACKAGES。这意味着一个具有android.permission.INSTALL_PACKAGES的系统APK被篡改后,恶意代码就可以在设备上安装任意的恶意APK了。
2.2 应用级分配Permission原理
实上,应用级别的Permission机制也是建立在Linux UID/GID基础上的。当我们在APK的AndroidManfest.xml配置文件中申请某一个Permission的时候,Android系统的安装服务PackageManagerService除了会记录它申请有相应的Permission之外(以便APK调用需要权限的API接口时进行验证),还会将APK加入到相应的某个Linux用户组去。这是因为在Android系统中,并不是所有的特权操作都是间接地通过系统服务来执行的,例如网络访问。一旦一个APK申请网络访问的Permission,那么它就会加入到Linux的网络用户组去,这时候APK就可以通过创建socket来访问网络了。
3 MAC机制(Mandatory Access Contro 强制访问控制)
在MAC机制中,用户、进程或者文件的权限是由管理策略决定的,而不是由它们自主决定的。
例如,我们可以设定这样的一个管理策略,不允许用户A将它创建的文件F授予用户B访问。这样无论用户A如何修改文件F的权限位,用户B都是无法访问文件F的。这种安全访问模型可以强有力地保护系统的安全。
在SEAndroid中,每一个进程和文件都会关联有一个安全上下文。这个安全上下文由用户、角色、类型、安全级别四个部分组成,每一部分通过一个冒号来分隔。例如,u:r:t:s0描述的就是一个SEAndroid安全上下文。当每一个进程和文件都关联上一个安全上下文之后,系统管理员就可以基于这些安全上下文制定一个安全访问策略,用来规定什么样的进程可以访问什么样的文件。
SEAndroid安全机制如图:
在图中,红色标注的即为SEAndroid安全上下文。其中,u:r:unstructed_app:s0描述的是用户安装的APK所运行在的进程的安全上下文,而u:object_r:app_data_file:s0描述的是用户安装的APK在运行过程中生成的数据文件的安全上下文。
3.1 SEAndroid安全机制与传图Linux UID/GID安全机制关系
SEAndroid安全机制与传统的Linux UID/GID安全机制是并存关系的,也就是说,它们同时用来约束进程的权限。当一个进程访问一个文件的时候,首先要通过基于UID/GID的DAC安全检查,接着才有资格进入到基于SEAndroid的MAC安全检查。只要其中的一个检查不通过,那么进程访问文件的请求就会被拒绝。上述的安全检查过程如图所示:
3.2 举个例子
举个例子说明上述的安全访问流程:
- 我们想从手机上下载一个文件到电脑上时,我们使用adb pull命令来实现。当我们执行adb pull命令的时候,实际上是由手机上的守护进程adbd来读出指定的文件,并且将读出来的内容发送给在电脑上运行的adb进程的。接下来,我们就按照以下步骤尝试从启用了SEAndroid的三星Note II上下载文件/system/bin/gpsd到电脑上来。
- 执行ls -l命令检查手机上存在/system/bin/gpsd文件,以及它基于传统的Linux UID/GID的权限位:
$ ./adb shell ls -l /system/bin/gpsd
-rwxr-xr-x root shell 2822268 2014-02-11 03:27 gpsd
从命令的输出可以看到,如果只考虑传统的Linux UID/GID安全机制,手机上的/system/bin/gpsd文件是所有用户均可以读取的。
- 执行adb pull命令下载手机上的/system/bin/gpsd文件:
$ ./adb pull /system/bin/gpsd ./gpsd
failed to copy '/system/bin/gpsd' to './gpsd': Permission denied
从命令的输出可以看到,我们没有按照预期那样将手机上的/system/bin/gpsd文件下载电脑上来,原因是“Permission denied”,也就是权限不够。
- 分别通过ls -Z和ps -Z命令检查文件/system/bin/gpsd和进程adbd的安全上下文:
./adb shell ls -Z /system/bin/gpsd
-rwxr-xr-x root shell u:object_r:gpsd_exec:s0 gpsd
$ ./adb shell ps -Z | grep 'adbd'
u:r:adbd:s0 shell 1978 1 /sbin/adbd
从命令的输出可以看到,文件/system/bin/gpsd的安全上下文为u:object_r:gpsd_exec:s0,而进程adbd的安全上下文为u:r;adbd:s0,因此我们可以断定,在三星Note II运行的系统上,一定存在一个访问策略不允许安全上下文为u:r;adbd:s0的进程访问安全上下文为u:object_r:gpsd_exec:s0的文件。
结论
从上面这个例子就可以看出,在启用SEAndroid之前,原本可以访问的文件,到启用SEAndroid之后,就变得不可以访问了!如果我们确实是需要访问这些文件,例如我们需要将这些文件打包在我们自己制作ROM里面,那么有没有其它办法访问呢?当读完SEAndroid安全机制这个系列的文章之后,你就会发现答案是肯定的,并且是在遵循SEAndroid安全策略的前提下实现的!
3.3SEAndroid包含的对象
分布在各个子系统中的资源、进程、socket、IPC等等
3.4 SEAndroid安全机制的整体框架
以SELinux文件系统接口为边界,SEAndroid安全机制包含有内核空间和用户空间两部分支持。在内核空间,主要涉及到一个称为SELinux LSM的模块。而在用户空间中,涉及到Security Context、Security Server和SEAndroid Policy等模块。这些内核空间模块和用户空间模块的作用以及交互如下所示:
1. 内核空间的SELinux LSM模块负责内核资源的安全访问控制。
2. 用户空间的SEAndroid Policy描述的是资源安全访问策略。系统在启动的时候,用户空间的Security Server需要将这些安全访问策略加载内核空间的SELinux LSM模块中去。这是通过SELinux文件系统接口实现的。
3. 用户空间的Security Context描述的是资源安全上下文。SEAndroid的安全访问策略就是在资源的安全上下文基础上实现的。
4. 用户空间的Security Server一方面需要到用户空间的Security Context去检索对象的安全上下文,另一方面也需要到内核空间去操作对象的安全上下文。
5. 用户空间的selinux库封装了对SELinux文件系统接口的读写操作。用户空间的Security Server访问内核空间的SELinux LSM模块时,都是间接地通过selinux进行的。这样可以将对SELinux文件系统接口的读写操作封装成更有意义的函数调用。
6. 用户空间的Security Server到用户空间的Security Context去检索对象的安全上下文时,同样也是通过selinux库来进行的。
总结
关于SEAndroid安全机制,还有一个关键点是值得提及的。那就是SEAndroid安全机制的目的不是为了完全杜绝别人攻击我们的设备,而是为了保证我们的设备受到攻击时,受到的损害减少到最少的程度。例如,SEAndroid安全机制并不能完全阻止我们的设备被root,但是它能保证我们的设备被root之后,一些敏感的文件仍然是不可访问,这样就可以最大程度地保护我们的设备。这是因为只要程序是由人类来编写的,就或多或少地存在BUG,或者说漏洞,特别是复杂的程序,进而就会被黑客利用,并且成功地侵入到我们的系统中来。这是防不胜防的。当然,我们并不是说SEAndroid对阻止设备被侵入毫无用处,它在一定程度上还是能加大侵入的技术难度的。