本文记录一些SELinux和SEAndroid的内容

DAC和MAC访问策略

标准的Linux安全策略被称为DAC(自主访问控制)。主要的策略是根据UID和GID去配置文件的读、写和可执行状态。如:-rwxr-xr-x

但DAC控制策略的颗粒度其实很大,因为它是以文件为单位的。试想如下情况:

  • 程序需要获取高权限资源A\B
  • A函数用于调用资源A
  • B函数用于调用资源B
  • 因此DAC会同时赋予该程序调用资源AB的权限
  • 攻击者在A函数中找到一个可利用漏洞接触到资源B
  • 因为权限校验成功,攻击者成功接触到资源B

所以就引入了下面的强访问控制策略MAC:

  • 它颗粒度更细,精确到某次操作。
  • 上个例子中的函数A调用资源A,那么在系统赋予程序权限时就不会赋予它资源B的权限

SELinux和SEAndroid

这两位大哥是作为Linux标准安全策略的补充来设定。即当我们去访问某个文件时,会先用DAC判断我是否存在目标文件的读/写/执行权限。如果都没问题,就再交付给SELinux或SEAndroid。

SEAndroid是SELinux在Android上的补充,结合了一些Android的特性。所以下面介绍时,如果只讲述了SELinux,那么在SEAndroid上也是一样的

对于Linux系统来说,存在超级用户Root,只要Root想做,那就没什么不可能。

但在SELinux中,则没有这个概念。它属于一种悲观策略,即默认情况瞎任意用户访问任意文件都是非法的。

网上介绍SELinux和SEAndroid的文章很多,我从中截取了一部分再做了些自我理解,尽量把它们精简到几句话之中。

Linux系统为每个进程、文件都设置一个安全上下文,安全上下文的格式为USER:ROLE:TYPE:LEVEL

USER

  • user_u: 普通用户登录系统后的预设
  • system_u:开机过程中系统进程的预设
  • root: Root登录后的预设

在Android中,自定义了一个用户u

ROLE

  • 文件、目录、设备的role通常为object_r

  • 进程的role通常记为r

  • 用户的role根据policy的不同有区别。targeted policy为system_r; strict policy为sysadm_r、staff_r、user_r。

用户的role,类似系统中的GID,不同角色具备不同的的权限;用户可以具备多个role;但是同一时间内只能使用一个role

TYPE

  • type:用来将主体(subject)和客体(object)划分为不同的组,给每个主体和系统中的客体定义了一个类型;为进程运行提供最低的权限环境

  • 当一个类型与执行中的进程相关联时,其type也称为domain

  • type是SElinux security context 中最重要的部位,是 SELinux Type Enforcement 的心脏,预设值以_t结尾

LEVEL

安全等级

Android中的应用

  • 应用程序安装服务PackageManagerService在启动的时候,会在/etc/security目录中mac_permissions.xml文件,然后对它进行解析,得到App签名或者包名与seinfo的对应关系。当PackageManagerService安装App的时候,它就会根据其签名或者包名查找到对应的seinfo,并且将这个seinfo传递给另外一个守护进程installed。
  • 守护进程installd负责创建App数据目录。 在创建App数据目录的时候,需要给它设置安全上下文,使得SEAndroid安全机制可以对它进行安全访问控制。Installd根据PackageManagerService传递过来的seinfo,并且调用libselinux库提供的selabel_lookup函数到seapp_contexts文件中查找到对应的Type。有了这个Type之后,installd就可以给正在安装的App的数据目录设置安全上下文了,这是通过调用libselinux库提供的lsetfilecon函数来实现的。
  • ActivityManagerService在请求Zygote进程创建应用程序进程之前,会到PackageManagerService中去查询对应的seinfo,并且将这个seinfo传递到Zygote进程。于是,Zygote进程在fork一个应用程序进程之后,就会使用ActivityManagerService传递过来的seinfo,并且调用libselinux库提供的selabel_lookup函数到seapp_contexts文件中查找到对应的Domain。有了这个Domain之后,Zygote进程就可以给刚才创建的应用程序进程设置安全上下文了,这是通过调用libselinux库提供的lsetcon函数来实现的。
  • Init进程在启动的时候,会创建一块内存区域来维护系统中的属性以及Property服务。 这个Property服务通过socket提供接口给其它进程访问Android系统中的属性。其它进程通过socket来和Property服务通信时,Property服务可以获得它的安全上下文。有了这个安全上下文之后,Property服务就可以通过libselinux库提供的selabel_lookup函数到property_contexts去查找要访问的属性的安全上下文了。有了这两个安全上下文之后,Property服务就可以决定是否允许一个进程访问它所指定的属性了。

只有当进程和文件都关联安全上下文之后,SEAndroid安全策略才能发挥作用。也就是说,当一个进程试图访问一个文件时,SEAndroid会将进程和文件的安全上下文提取出来,根据安全策略规则,决定是否允许访问

安全策略

那真正校验访问策略的时候是怎么做的呢?

类型强制访问控制(TE)通过指定主体类型(即域)和客体类型使用 allow 规则授予访问权限,allow 规则由四部分组成:

  • 源类型(Source type(s)):通常是尝试访问的进程的域类型
  • 目标类型(Target type(s)): 被进程访问的客体的类型
  • 客体类别(Object class(es)): 指定允许访问的客体的类型
  • 许可(Permission(s)): 象征目标类型允许源类型访问客体类型的访问种类

下面以 Android 中一个例子说明 TE 是如何工作的。

ls -Z | grep proc,可看到 proc 目录的安全上下文如下:

1
dr-xr-xr-x root     root              u:object_r:proc:s0 proc

再运行 ps -Z | grep adbd,看看进程 adbd 的安全上下文:

1
u:r:su:s0                      root      2935  1     /sbin/adbd

目录 proc 和进程 adbd 各自有自己的安全上下文,安全上下文实际上是附加在对象上的一个标签,由用户、角色、类型和安全级别四部分组成,由冒号隔开。在 SEAndroid 里,只定义了一个用户 u,所以文件和进程安全上下文中,所有的用户都为 u。文件的角色一般都为 object_r,而进程的角色固定为 r。用户和角色定义得这么随意,是因为对安全策略来说它们并不重要,在类型强制访问控制的安全上下文中,最重要的就是类型,它才是定义规则时需要的。假如要想通过 adb 读写 proc 目录下的文件节点,则需要定义如下一条规则来授权:

1
allow init proc:dir rw_dir_perms;

引用