SELinux on Android

Wesly大发神威,连夜翻译了一些文档,加上他本人的知识和理解,呕心沥血写出这部作品。不过需要润色,先放前面一些概述了。


  • 引言

作为安卓安全模型的一部分,安卓使用了SELinux的访 问控制策略。SELinux的出现大幅度增强了安卓的安全性。此外,还有许多公司和组织也为增强安卓的安全性做出了不懈的努力。你可以在 android.googlesource.com站点看到所有安卓源代码及其贡献者。

在SELinux的强力保护下,安卓可以更好的对应用程序数据和系统日志进行访问控制。这不仅减轻了恶意程序对系统的影响力,而且保护了用户不受移动设备上隐藏的恶意代码所攻击。

在安卓的最新版本中,SELinux是以“enforcing mode”形式存在的,并且具有一套相应的默认的安全策略,该策略遍布于整个安卓开源工程。

在“enforcingmode”中,非法的操作是被禁止 的,并且所有的攻击行为会被Linux内核记录下来,保存在dmesg里面。针对SELinux在安卓中的应用,安卓设备生产商应该做的是:在将 SELinux的状态置为enforcing mode之前,不断收集SELinux所报出的系统错误信息,并依此改善自己的软件,更新安卓系统的SELinux策略。


  • 背景


注意,安卓可以更新自己的SELinux策略版本,并允许SELinux基于domain来设置到底处于那一种模式(disable、permissive、enforcing)。

例如,如果你让自己开发的所有应用程序运行在一个单一的domain中,你可以设置该domain为permissive,然后设置其他其他功能和他们的domains为enforcing状态。

Domains通过密钥(用于对应用进行签名)和应用程序建立关联。这是在SELinux策略源文件*.te的顶部进行设置的。

安卓遵循此模型: 即将各个不同应用程序分别孤立在一个单一的domain中。基于此,只有root domain和 root级别的进程(例如initd、installd和vold)现在被设置为enforcing模式。

普通应用程序的domain仍然保持在permissive模式,这一举动允许对应用程序的SELinux策略做进一步的评估,阻止没必要的系统错误。但是,恶意程序仍可以在root domain激发一个行为(该行为是不被允许的),随之而来的便是导致应用程序的崩溃。

因此,设备生产商应该保留安卓默认的设置,并且只让root domain 保持enforcing状态,直到完全解决了dmesg中报告的问题。

也就是说,设备生产商可能需要不断改善他们的SELinux的策略实现来满足对操作系统的诸多添加和修改所带来的问题。参见:客制化段落。


  • 强制访问控制


与安卓的其他安全措施协同工作,安卓的访问控制策略很大程 度上限制了目前脆弱的设备和账户受到潜在的破坏。安卓的DAC(自主访问控制)和MAC(强制访问控制)可以提供一个强有力的方法,确保你的软件运行在最 低权限级别。这不仅削弱了攻击行为对系统的影响,而且减小了恶意进程重写系统数据,甚至偷窃数据的可能性。

从安卓4.3版本开始,在传统DAC的基础 上,SELinux为安卓提供了MAC保护伞。举例说明:有些进程必须以root身份运行来向原始块设备写入数据。我们知道,在传统的基于DAC的 Linux环境中,如果root用户被攻陷,那么拥有root权限的进程则可以向任何原始快设备中写入数据。然而,现在安卓有了SELinux的帮 助,SELinux可以用来标记这些设备,以实现即使获取到root权限,该进程也仅可以向策略中指定的设备写入数据,其他的设备想都别想。

参见“用例段落”中更多地例子,来理解SELinux是如何抵御威胁的。

 

  • 如何把SELinux特性加入到安卓中


安卓中的SELinux目前是以enforcing mode存在的,而不是disable状态或者permissive状态(permissive状态可以作为指引,使测试和开发变得更加容易)。

尽管enforcingmode是全局的,请牢记着这种情况在基于单个domain的这种情况可是例外,就像在应用domain中一样。

SEAndroid拥有一切SELinux的特征。你只需要把最新的安卓内核整合进来,然后把~platform/external/sepolicy(你可以在该路径下找到许多例子)路径下的文件包含进来:

 https://android.googlesource.com/kernel/common/

 https://android.googlesource.com/platform/external/sepolicy/

这些文件在编译时会包含SELinux内核安全策略并兼容以前老版本的安卓操作系统。

把这些文件放置在 /device/manufacturer/device-name/sepolicy路径下。

然后修改BoardConfig.mk文件(该文件位于包含sepolicy的子路径),来引用sepolicy策略文件,方法如下:

BOARD_SEPOLICY_DIRS:= \ 
        <root>/device/manufacturer/device-name/sepolicy
BOARD_SEPOLICY_UNION:= \ 
        genfs_contexts \  
        file_contexts \  
        sepolicy.te


在重新编译整个工程之后,你的设备就具备SELinux功能了。

你现在可以定制自己的SELinux策略,来适应你自己对安卓系统的修改(这将会在定制段落说明),或者验证你的当前存在的配置(后面有介绍)。

 

 标签,规则和域

SELinux是由与动作或者策略匹配的标签所决定,而标签取决于你允许了什么。socket,file和process 都有SELinux各自对应的标签。SELinux的决定取决于对象所关联的标签和相互的策略。在SELinux中,标签是由user:role:type:mls_level 这样的形式定义的,其中的type(类型)是影响决策的重要组成部分,而这些对象(objects)则被他所属于的类和类型去匹配其他类的权限检查

策略规则的格式是allow domains types:classes permissions

·        Domain – 进程或者进程集合的标签

·        Type – 对象(e.g. file, socket)或者对象集合的标签

·        Class – 对象(e.g. file, socket)被允许(授权)访问的种类

·        Permission – 允许采取的操作(e.g. read, write

下面我们来看一个例子:

allow appdomain app_data_file:file rw_file_perms

这条策略的意思是说一个app可以去读写有app_data_file标签的file。注意到这条规则依赖于一个已经定义好的全局的宏文件,这些宏可以在 文件te_macros里面找到。宏是用来提供公共的类,权限,规则,是用来帮助减少由于相关权限缺少而导致失败的可能。在编译时这些这些使用宏的地方将会被宏中所使用的语句替换掉。

以上的格式是创建规则的重要组成部分,再来看一遍,一条规则有这么多

<rule variant><source_type><target_type> :<class><permission>

这条rule表明当一个打着source type标签的对象尝试去以一个符合permission的动作去访问属于class类的有着target_type标签的对象的时候这条规则就会被触发。实际应用中,这条规则常常包含着几条permission比如说:

allow domain null_device:chr_file { getattr open read ioctl lock append write};


domain是所有进程都有的标签,null_device是chr_file类/dev/null的标签,这条策略的意思就是允许对/dev/null进行基本的读写操作。

一般说来一个domain都有与它匹配的打了标签的进程

比如一个APP是运行在自己的打untrusted_app标签的进程中的,所以只能获得对应于这个标签的受限的权限。

随着系统安装的原装APP是在另一个标签下的。而系统应用则是在system_app标签下的拥有另外的权限

下面这些标签可能会在以后出现:

·        socket_device

·        device

·        block_device

·        default_service

·        system_data_type

·        tmpfs


  • 客制化


一旦你在安卓系统中整合了这些基础功能,并且彻底地分析了结果,你就可以在你自己的策略设置中添加条目来对安卓系统做一些客制化的工作。

当然,你添加的这些策略必须满足安卓兼容性程序的需求,并且,你不能移除默认的SELinux设置。

设备生产商不应该移除现存的安卓安全设置。否则可能会破坏 SELinux在安卓上的部署,破坏受SELinux保护的应用的良好安全环境。这也包含了第三方应用(需要改进以变得更易用和更具操作性)。在具备 SELinux功能的设备上,如要应用正常工作,则绝不允许自身有任何修改。

参见内核安全特征段落,该段提供了安卓兼容性定义文档(针对特殊要求):

http://source.android.com/compatibility/index.html

SELinux使用白名单的方法,这意味着SELinux会向某些特殊的角色赋予特权。

因为目前安卓默认的SELinux策略已经支持整个AOSP,OEM实际上不需要再去修改SELinux的默认设置。但是,如果OEM做了SELinux策略的修改和定制,就应该特别注意,切勿破坏现存的应用。这里给出一些建议吧:

1.使用最新的安卓内核;

2.采用最少特权原则;

3.你只管处理你自己对安卓系统的添加和修改,其他的不用管。因为默认的安全策略会自动在AOSP的代码库中产生作用。也就是说,你对安卓原生态系统添加了什 么内容,你就写你所添加内容对应的策略配置就OK了,其他的就别管了,否则可能会出问题(这时我的理解);

4.把软件组件划分为不同的模块,由不同的模块分别执行单一的任务;

5.建立SELinux策略,该策略可以将这些任务从无关的功能中孤立开来;

6.将这些策略写入*.te(SELinux策略文件的扩展)文件中,该文件位于: /device/manufacturer/device-name/sepolicy;

7.发布你定制的SELinux版本时,先让其处在permissive mode中(因为还没经过测试噢);

8.分析结果,并不断改善策略配置。

一旦将自己的SELinux定制内容整合到安卓系统中,OEM安卓开发过程应该包含一个步骤来确认SELinux的兼容性。在一个理想的软件开发过程中,SELinux策略只有在软件模型改变时才会发生改变,而不是在实际的实现时(不是很懂)。

当设备生产商开始定制SELinux时,应该首先仔细审核对安卓的添加内容。如果他们添加了一个组件,用以实现一个新的功能,设备生产商必须确保该组件满足安卓安全策略的要求,同时也要(在开启为enforcement之前)满足OEM精心开发的相关安全策略。

为了阻止非必要的问题发生,最好能够做到足够的宽泛和兼 容,而不是过于限制和不兼容,结果是破坏了的设备功能。相反的,设备生产商对安卓系统的改变将会使其他人受益,它们应该提供一个针对默认SELinux策 略的修改补丁。如果该补丁可以直接打给默认安全策略,那么设备生产商将不再需要在每个安卓发布版中做相应的修改。

 

  • 应用案例


下面有几个关于攻击安卓的例子,用来讲解如何精心开发自己的软件和制定相关的SELinux策略:

软链接 —— 因为软链接看似一个文件,他们经常被作为一个文件来读取。这可能会导致系统被攻击。例如,一些特权组件(例如init)改变某个文件的权限,有时被过分的暴漏。

攻击者这时可能会将这些公开的文件用软链接替换掉,用自己写的代码来代替,当然,自己写的代码可能会允许攻击者修改任意文件。但是如果你知道自己写的应用程序肯定不会和软链接打交道,那你就可以使用SELinux来阻止这一切的发生。

系统文件 —— 我们都知道系统文件只有系统服务可以修改,但是,因为netd、init和vold都是(Native service)以root身份运行的,他们可以访问这些系统文件。所以,如果netd被攻陷,它就可以破坏这些系统文件,这势必会危害系统服务本身。

有了SELinux,你就可以标记这些文件为原生的系统数据文件。因此,可以读和写这些文件的domain只有系统服务。即使netd被攻陷,它也不能切换domains到系统服务的domain。尽管它具有root权限,也无法访问这些系统文件。

应用数据 —— 另外一个例子是,一些进程必须以root身份运行,但是我们又不想让其访问应用数据。SELinux在这里就非常有用了,因为可以做声明来阻止某进程去访 问应用数据,例如某一个和应用数据无关的domain,就可以被阻止,而不能去访问internet。

设置文件属性 —— 有一些命令,例如chmod和chown等,我们可以为他们确定一个文件集,在这个文件集里,相关的domain可以执行文件属性设置命令。对于任何该文 件集外部的文件,一旦文件属性变化,则被阻止,即使是root也没门儿。所以,一个应用程序可以针对标记过的app_data_files,执行 chmod或者chown,但是不能直接对未经标记的shell_data_files或者system_data_files进行文件属性设置。

 

  • 相关文件

如果你决定定制SELinux策略的配置,那么本段可以帮助你。参见客制化段落的各个步骤。

建议设备生产商从默认的安卓SELinux策略开始修改,对默认的策略做最小的修改,来解决自己对安卓系统的修改和添加所带来的安全问题。现存的安卓SELinux策略文件在~platform/external/sepolicy目录下。

安卓对SELinux策略版本进行升级,这允许 SELinux模式可以在某个domain设置为permissive(而在其他domain设置为enforcing)。例如,如果你在一个单一的 domain中运行所有你的应用程序,你可以将该domain设置为permissive,然后将所有其他的功能和他们的domains设置为 enforcement。domains通过密钥(用于对应用程序进行签名)和应用发生关联。这些设置是在SELinux策略原文件*.te中进行配置 的。

如果你要定制SELinux,那么你必须创建或者编辑下面这些文件:

新的SELinux策略原文件(*.te) —— 位于<root>/device/manufacturer/device-name/sepolicy路径。这些文件定义domains和 他们的labels。在编译过程中新的策略文件和现存的策略文件会链接起来,成为一个SELinux内核策略文件。

重要提示:不要修改app.te文件,这个文件是AOSP提供的。如果你修改了,那么你就破坏了第三方应用的安全环境,将他们置于危险的境地。

修改BoardConfig.mk文件 —— 该文件位于<device-name>路径下,该路径包含了sepolicy子路径。这个文件必须得倒合适的修改,用于引用sepolicy子路径。

修改file_contexts文件 —— 该文件位于sepolicy子路径。这个文件用于给文件打标签,并且在用户空间进行管理。因为你生成了新的策略,所以你要修改这个文件来引用他们。为了让 新的file_contexts产生作用,你必须在一个将再次打上标签的文件中运行restorecon。

sepolicy路径中剩下的其他文件要么是自动生成的,要么应该保持原封不动。策略文件的形式是:allow,domain和context,为一个动作集合:

Allow —— 给角色权限来在指定的domain中实现具体动作,该动作在context中做具体描述

Domain —— domain代表了规则的范围,并且在内核里面可以转化为一个安全的ID(SID)。

Context —— 规则的标识符,这会被转换为一个内核中的整数

 

  • 标签,规则和域

SELinux是由与动作或者策略匹配的标签所决定,而标签取决于你允许了什么。socket,file和process 都有SELinux各自对应的标签。SELinux的决定取决于对象所关联的标签和相互的策略。在SELinux中,标签是由user:role:type:mls_level 这样的形式定义的,其中的type(类型)是影响决策的重要组成部分,而这些对象(objects)则被他所属于的类和类型去匹配其他类的权限检查

策略规则的格式是allow domains types:classes permissions

·         Domain – 进程或者进程集合的标签

·         Type – 对象(e.g. file, socket)或者对象集合的标签

·         Class – 对象(e.g. file, socket)被允许(授权)访问的种类

·         Permission – 允许采取的操作(e.g. read, write)


下面我们来看一个例子:

allow appdomain app_data_file:file rw_file_perms

这条策略的意思是说一个app可以去读写有app_data_file标签的file。注意到这条规则依赖于一个已经定义好的全局的宏文件,这些宏可以在 文件te_macros里面找到。宏是用来提供公共的类,权限,规则,是用来帮助减少由于相关权限缺少而导致失败的可能。在编译时这些这些使用宏的地方将会被宏中所使用的语句替换掉。

以上的格式是创建规则的重要组成部分,再来看一遍,一条规则有这么多

<rule variant><source_type> <target_type> : <class><permission>

这条rule表明当一个打着source type标签的对象尝试去以一个符合permission的动作去访问属于class类的有着target_type标签的对象的时候这条规则就会被触发。实际应用中,这条规则常常包含着几条permission比如说:


allow domain null_device:chr_file { getattr open read ioctl lock append write};


domain是所有进程都有的标签,null_device是chr_file类/dev/null的标签,这条策略的意思就是允许对/dev/null进行基本的读写操作。

一般说来一个domain都有与它匹配的打了标签的进程

比如一个APP是运行在自己的打untrusted_app标签的进程中的,所以只能获得对应于这个标签的受限的权限。

随着系统安装的原装APP是在另一个标签下的。而系统应用则是在system_app标签下的拥有另外的权限

下面这些标签可能会在以后出现:

·        socket_device

·        device

·        block_device

·        default_service

·        system_data_type

·        tmpfs

SELinux默认情况是采取最小授权机制。这就意味着除非你在策略文件里面允许了特定的权限后才能hook kernel。这意味着一个良好的策略文件必然是由大量的规则信息组成。对SELinux安全策略的完整设计已经超出了本文档的范文。我们现在的着重点是当打算配置一台安全的android系统应该如何去写配置这些文件,可以参看下面这几个网站

http://seandroid.bitbucket.org/PapersandPresentations.html

https://www.codeproject.com/Articles/806904/Android-Security-Customization-with-SEAndroid

https://www.nsa.gov/research/_files/publications/implementing_selinux.pdf

https://events.linuxfoundation.org/sites/events/files/slides/abs2014_seforandroid_smalley.pdf

https://www.internetsociety.org/sites/default/files/02_4.pdf

https://www.gnu.org/software/m4/manual/index.html

http://freecomputerbooks.com/books/The_SELinux_Notebook-4th_Edition.pdf

下面是让你的设备支持SELinux的步骤

1、添加内核对SELinux的支持,这个我也不太了解,但是4.4以后是都有的。

2、为每一个init启动的服务(进程或者后台程序)授予各自的域(domain)

3、通过下面这些来定制服务:

检查init文件寻找要启动的服务

审核内核的输出信息(dmesg)

寻找(grep)在init域中的进程

4、为这些新增的进程,驱动,套接字打标签。所有的上述对象都需要打上一个对应他们自己的合适的标签以方便你的安全策略的编写.AOSP中有例子

5、确保安全策略的稳定性以及授权范围的最小化。

 

  • 验证你当前SELinux的配置

 

安卓强力推荐OEM去测试他们的整个SELinux实现方案。当设备生产商实现自己的SELinux策略时,应该首先发布他们自己的策略在permissive mode状态下。如果可能,可以把新的策略应用到设备列表的测试池。

一旦予以应用,一定要确保SELinux是跑在正确的mode上,这可以通过下面命令实现:

getenforce

这将会打印全局SELinuxmode:或者disabled,或者enforcing或者permissive。请注意,这个命令仅仅显示全局SELinux mode。如果要为每个domain决定SELinux mode,你必须检查相应的文件。

然后检查错误。错误码是以事件日志的方式发送并存储在内核 dmesg中,并且你在本地设备上可以看见它。设备生产商应该在设备商的demsg中认真审查SELinux的输出,并在发布为permissive模式 之前,精炼和改善SELinux的策略配置,并最终使之成为enforcing mode。

利用这个内核dmesg,设备生产商可以轻易的确认系统用户或者系统组件什么时候违反了SELinux策略。设备生产商此时可以处理并解决掉这个恶意的行为:或者对软件做一些修改,或者对SELinux策略做一些修改,或者全都修改。

特别的,这些日志消息暗示着什么样的角色和什么样的进程会在策略加强下失败并且为什么会失败。下面是一个例子:

denied  { connectto } for  pid=2671 comm="ping"path="/dev/socket/dnsproxyd"

scontext=u:r:shell:s0tcontext=u:r:netd:s0 tclass=unix_stream_socket

  • 解释说明:

l  上面的 { connectto } 代表到底做了什么操作。它和最后的tclass(unix_stream_socket)一起,粗略的告诉你“之前到底在谁身上发生了什么事情”。在这种 情况下,我们可以大概了解到,一些什么东西曾试图链接一个unix stream socket。

l  Scontext(u:r:shell:s0)告诉你这个曾发生的行为是在什么上下文。这里,我们可以看到有一个什么东西曾以shell的身份运行。

l  tcontext (u:r:netd:s0)告诉你曾发生过的行为的最终目标是谁。这里,我们可以看到目标是netd所拥有unix_stream_socket。

l  上面的comm="ping" 给你一个额外的提示:在denial日志产生的时候,当时正在运行什么。这里,我们就不多解释了:ping。

安卓会不断把这些信息拿过来仔细分析,并且改善默认的安全策略,以实现对SELinux策略稍加定制,就可以跑在更多地不同的安卓设备上。因为有这种机制,OEM必须让他们自己的修改内容去适应安卓操作系统。

然后,通过安卓兼容性测试(CTS),运行SELinux处于激活状态(使能状态)的设备。如前所述,任何新的策略仍然必须满足安卓兼容程序要求。

最后,如果可能的话,在内部开启为enforcement(在雇员的设备上),目的是提高失败的能见度。确定任何用户问题,并解决它们。

 

帮助

 

安卓强力推荐设备生产商工作在他们的安卓账户管理器上,以便分析SELinux的检测结果并改善策略配置。

 

本文由Hack Blog原创,如需转载注明原文链接 http://www.hackblog.cn/post/42.html


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值