前绪 2
一、Selinux基础概述 2
二、什么是Selinux? 2
三、SELinux Policy语言 3
1、安全属性——SContext 3
2、TE简介 4
1). 客体类别和许可: 4
2). 访问向量规则: 5
3). AV规则 5
四、SElinux策略文件 6
1、定义性文件 7
2、其他te文件 10
五、如何配置SElinux 11
六、SElinux调试方法 12
1、selinux的编译 12
2、如何快速确认是selinux引起? 12
3、修改/添加selinux策略 12
1).简化方法 13
2).按需确认方法 13
3).权限放大情况处理 14
七、案例分析 15
案例一、OTA升级过程中出现/system/etc/ppp/ip-up-vpn无法添加scontext 15
案例二、recovery删除data目录下的文件 16
八、常用指令 17
1、文件操作 17
2、进程domain的确认 18
3、ROLE的确认和变更 18
4、模式切换 18
5、其他重要命令 19
九、参考文献 20
Selinux梳理总结
前绪
该文档只是将selinux中最基础的知识点进行了整理罗列,其他还有很多高级用法及升华拓展部分,如SElinux域转换、角色转换以及SElinux框架等,并未收录,有需要了解的可以参照参考文献部分中的《SElinux详解》文档,后续会继续补充完善。
一、Selinux基础概述
SELinux则是由美国NSA(国安局)和一些公司(RedHat、Tresys)设计的一个针对Linux的安全加强系统。
NSA最初设计的安全模型叫FLASK,全称为Flux Advanced Security Kernel(由Uta大学和美国国防部开发,后来由NSA将其开源),当时这套模型针对DTOS系统。后来,NSA觉得Linux更具发展和普及前景,所以就在Linux系统上重新实现了FLASK,称之为SELinux。
Linux Kernel中,SELinux通过Linux Security Modules实现。在2.6之前,SElinux通过Patch方式发布。从2.6开始,SELinux正式入驻内核,成为标配。后来,鉴于Selinux的安全性和使用性,Google在Android 4.4上正式推出的一套以SELinux为基础于核心的系统安全机制,且命名为SEandroid,自此,selinux便被"移植"到了Android上了。
二、什么是Selinux?
SELinux出现之前,Linux上的安全模型叫DAC,全称是Discretionary Access Control(自主访问控制)。DAC的核心思想很简单,就是:进程理论上所拥有的权限与执行它的用户的权限相同。比如,以root用户启动Browser,那么Browser就有root用户的权限,在Linux系统上能干任何事情。
显然,DAC太过宽松了,所以各路高手想方设法都要在Android系统上搞到root权限。那么SELinux如何解决这个问题呢?原来,它在DAC之外,设计了一个新的安全模型,叫MAC(Mandatory Access Control),翻译为强制访问控制。MAC的处世哲学非常简单:即任何进程想在SELinux系统中干任何事情,都必须先在安全策略配置文件中赋予权限。凡是没有出现在安全策略配置文件中的权限,进程就没有该权限。来看一个SEAndroid中设置权限的例子:
[external/sepolicy/netd.te]
/*
允许(allow )netd域(domain)中的进程写(write)类型为proc的文件
*/
allow netd proc:file write
如果没有在netd.te中使用上例中的权限配置allow语句,则netd就无法往/proc目录下得任何文件中写数据,即使netd具有root权限。
显然,MAC比DAC在权限管理这一块要复杂,要严格,要细致得多。
关于DAC和MAC:
-
Linux系统先做DAC检查。如果没有通过DAC权限检查,则操作直接失败。通过DAC检查之后,再做MAC权限检查。
-
SELinux中也有用户的概念,但它和Linux中原有的user概念不是同一个概念。比如,Linux中的超级用户root在SELinux中可能就是一个没权限,没地位,打打酱油的"路人甲"。当然,这一切都由SELinux安全策略的制定者来决定。
这里只是借用一个例子先对selinux做一个初步的了解,下面将对selinux特有的语法做简单介绍。
三、SELinux Policy语言
在网上看到一种比喻的说法,个人感觉很有道理:Linux中有两种东西,一种死的(Inactive),一种活的(Active)。活的东西就是进程,而死的东西就是资源(eg:文件、套接字等)。他们之间就是一种操作和被操作,或者说使用与被使用的关系。进程能发起动作,例如它能打开文件并操作它。而文件只能被进程操作。SElinux Policy语言就是将死的和活的东西都给打上"标签",通过"标签"将系统内的资源(包括进程)分成不同的角色(比如:用户、客体),进而对整个系统资(包括进程)进行合理安全的管控。
1、安全属性——SContext
在SELinux世界里,每种东西都会被赋予一个安全属性,官方说法叫Security Context,也可以理解为上文所提到的"标签"。通过每个资源的属性,对他们进行归类管理。Security Context(以后用SContext表示)是一个字符串,由四部分组成,被分为两类:进程SContext和资源SContext。根据SELinux规范,完整的SContext字符串为:
user:role:type[:range]
注意,[]中的内容表示可选项。s0属于range中的一部分,表示安全级别(这里不做讨论)。
首先,我们通过下面例子看下进程SContext。如SEAndroid中,进程的SContext可通过ps -Z命令查看,如图1所示:
最左边的那一列就是进程的SContext,以第一个进程/system/bin/logwrapper的SContext为例,其值为u:r:init:s0,其中:
-
u——user,用户,SEAndroid中仅定义了一个SELinux用户,值为u。
-
r——role,role是角色之意,它是SELinux中一种比较高层次,更方便的权限管理思路,即Role Based Access Control(基于角色的访问控制,简称为RBAC)。简单点说,一个u可以属于多个role,不同的role具有不同的权限。RBAC我们到最后再讨论。
-
init——tyoe,代表该进程所属的Domain为init。MAC的基础管理思路其实不是针对上面的RBAC,而是所谓的Type Enforcement Accesc Control(简称TEAC,一般用TE表示)。对进程来说,Type就是Domain。比如init这个Domain有什么权限,都需要通过[例子1]中allow语句来说明。
-
S0——和SELinux为了满足军用和教育行业而设计的Multi-Level Security(MLS)机制有关。简单点说,MLS将系统的进程和文件进行了分级,不同级别的资源需要对应级别的进程才能访问。限于篇幅,这里不做过多探讨。
接着,我们看下文件的SContext,在SEandriod中,文件的SContext可以通过ls -Z查看:
如上图所示,右边数第二列就是所对应的SContext信息。以root目录为例,其对应SContext为:u:object_r:rootfs:s0:
-
u——同样是user之意,它代表创建这个文件的SELinux user。
-
object_r——role,这里指文件,在SELinux中,使用object_r表示它的role。
-
rootfs——死的东西的Type,和进程的Domain其实是一个意思。它表示root目录对应的Type是rootfs。
-
s0——MLS的级别。
2、TE简介
有了SContext就可以对系统内所有资源进行统一管理了。SElinux的安全模型(MAC)有两种管理机制,分别是TEAC(Type Enforcement Accesc Control,后文简称TE)和RBAC(Role Based Accesc Control)。RBAC是更高级的一种管理机制(由于SEandroid涉及不多,这里不予展开谈论),不过也是基于TE的。可见,TE是SElinux的管理核心,下面我们就对TE语法做详细解读。
在没有介绍具体的语法之前,首先需要了解下与TE语句相关的另外几个概念定义:
1). 客体类别和许可:
客体类别及其许可是 SELinux 中访问控制的基础,客体类别代表资源的范畴,如文件和
套接字,许可代表对这些资源的访问权限,如读或发送。一个客体类别代表某个确定类型(如文件或套接字)的所有资源,一个客体类别的实例(如某个特定的文件或套接字)被称为一个客体。客体类别指的是资源(文件)的所有范畴,客体指的是客体类别的某个特定实例(如: /etc/passwd)。
【声明定义方法】
声明许可有两种方法,第一种叫做通用许可(被多个客体类别所共用),它允许我们创建与客体类别一起作为一个组的许可,通用许可在类似的客体类别(如文件和符号连接)共享一套访问许可时很有用;第二种方法叫做特定类别许可(由某个客体类别所独用),它允许我们单独为客体类别声明特定的许可,正如将会看到的,有一些客体类别只有特定的许可,有一些只有通用许可,还有一些是这两者都有。客体类别和许可定义方法如下:
声明客体类别:class 类别名字
通用许可:common 通用名 {许可集}
联合许可和客体类别:class 类别名 [inherits 通用许可集名] [{许可集}]
此外,既然有客体,肯定还有主体一说,即谁去使用、操作客体。主体和客体只是逻辑上的区别,只有放在安全控制语句中才能看出谁是主体谁是客体。但,既然是控制,主体一般来说肯定是"活的"才有可能去控制"死的"东西,所以,在SEandriod中,主体一般指的就是进程,客体可以是文件等资源,也可以是进程(因为进程一样可以被进程操作)。
2). 访问向量规则:
访问向量规则又称AV规则,属于SElinux策略语言的控制部分。常用的通用AV规则有如下4个:
-
allow:表示允许主体对客体执行允许的操作
-
dontaudit:表示不记录违反规则的决策信息,且违反规则不影响运行(允许操作且不记录)
-
auditallow:表示允许操作并记录访问决策信息(允许操作且记录)
-
neverallow:表示不允许主体对客体执行指定的操作
好了,到此,我们就可以组合成最基本的SElinux策略语句了。一般,完整的基本安全控制语句格式为:
AV规则 主体 客体:客体类别 许可
还拿之前的例子做说明:
allow netd proc:file write
其中:
allow——AV规则
netd——主体
proc——客体
file——客体类别
write——许可
当然,后面还有很多其他策略语句,将在介绍系统文件时,一并概述。
3). AV规则
对于AV规则,前文已经有所提及,比如allow。SElinux中主要的有效规则有allow,auditallow,auditdeny,dontaudit和neverallow,下面逐一介绍。
-
允许规则allow
我们使用allow规则指出了所有运行时授予的许可,它们是SELinux策略中允许许可的唯一方法,注意:默认情况下,不允许任何访问,我们指定了两个类型列表(源和目标类型),根据列出的客体类别的许可指定访问权,如:
allow user_t bin_t : file { read execute };
这个规则允许任何安全上下文中类型具有user_t的进程对任何安全上下文中具有类型为bin_t的普通文件所有read和execute访问权。allow规则共享了通用AV规则的的所有语法,并且也没有增加任何额外的语法了。
-
审核规则auditallow/auditdeny/dontaudit
SELinux有大量的工具记录日志信息,或审核、访问尝试被策略允许或拒绝的信息。审核消息通常叫做"AVC消息",它提供了详细了关于访问尝试的信息,包括是允许还是拒绝,源和目标的安全上下文,以及其它一些访问尝试涉及到资源信息。AVC消息与其它内核消息类似,都是存储在/var/log目录下的日志文件中,它是策略开发、系统管理和系统监视不可缺少的工具。在此,我们检查是哪一个访问尝试产生了审核消息。
默认情况下,SELinux不会记录任何允许的访问检查,只会记录被拒绝的访问检查。这并没什么奇怪的,在大多数系统上,每秒会允许成千上万的访问,只有很少的一部分会被拒绝,允许的访问通常是在预料之中的,通常不需要审核,被拒绝的访问通常是(但不总是)非预期的访问,对它们进行审核便于管理员发现策略的bug和可能的入侵尝试。策略语言允许我们取消这些默认的预料之中的拒绝审核消息,改为记录允许的访问尝试审核消息。
SELinux提供两个AV规则允许我们控制审核哪一种访问尝试:dontaudit和auditallow。使用这两条规则我们就可以改变默认的审核类型了,最常用的是dontaudit规则,它指出哪一个访问尝试被拒绝时不审核,这样就覆盖了SELinux默认的审核所有拒绝的访问尝试的行为。
dontaudit httpd_t etc_t : dir search;
注意,审核(audit)规则让我们覆盖了默认的审核设置,allow规则指出了什么访问是允许的,auditallow规则不允许访问,它只审核允许的许可。
-
禁止规则neverallow
最后一个AV规则是neverallow规则,我们使用这个规则来指定永远不会被allow规则执行的访问,你可能会疑惑,为什么会有这个规则?因为默认情况下所有的访问都是被拒绝的,设计这个规则的主要目的是为了帮助编写策略时,可以明确地指出不想要的访问许可,因此可以预防意外发生,回想一下,在一个SELinux策略中可能包含成千上万条规则,可能不小心加入了我们本不想授予的访问权,此时,neverallow规则就可以帮助预防这种情况发生,如:
neverallow user_t shadow_t : file write;
这条neverallow规则可以有效地阻止我们在策略中添加一条允许user_t对类型为shadow_t的文件进行写操作的规则,如果添加了这样的规则在编译时就会报错,这条规则不会移除访问权,它只是会产生编译错误。我们在编写策略时,neverallow规则往往放在allow规则前面,首先声明哪些访问是明确地被拒绝的,然后再声明哪些访问是可以接受的,这样就可以预防我们人为出错了。
neverallow规则支持一些特殊的其它AV规则不支持的语法,在neverallow规则中的源和目标类型列表中可以使用通配符(*)和求补算操作符(~),如:
neverallow * domain : dir ~{ read getattr };
这条规则指出没有哪条allow可以授予任何类型对具有domain属性的类型的目录有任何访问权,除了read和getattr访问权外(即读访问权),这条规则的中通配符意味着所有的类型,在真实的策略中,类似这样的规则很常见,它们用来阻止对/proc/目录适当的访问。
我们从前面这个例子中看出,在源类型列表中需要使用通配符,因为我们想要指出任何类型或所有类型,包括那些还没有创建的类型,使用通配符可以预防我们未来犯错。
另一个常见的neverallow规则是:
neverallow domain ~domain : process transition;
这条neverallow规则增强了domain属性,它指出了进程不能转换到无domain属性的类型,这就使得要为一个类型无doamin属性的进程创建一个有效的策略是不可能的。
四、SElinux策略文件
SElinux的策略文件主要集中在external\sepolicy下,这里也是我们添加修改安全策略的主要地方。这里所有的安全策略在编译后最终都会汇集到out/target/product/generic/obj/ETC/sepolicy_intermediates/policy.conf文件中,然后再经过转化load到linux kernel。下面我们就对external\sepolicy目录下的文件进行逐一解析说明。
打开external\sepolicy目录,我们会发现里面有很多.te文件,这些te文件就是我们定义的策略文件,除了策略文件之外,还有很多没有后缀的,这些没有后缀的文件就是专门定义策略语句元素的。由于所有的te文件中的规则语句都是基于这些语句元素组成的,这里我们先来看下这些定义性文件:
1、定义性文件
users
该文件中定义了SElinux中唯一的用户u:
user u roles { r } level s0 range s0 - mls_systemhigh;
roles
该文件定义了SElinux中唯一的角色r:
role r;
role r types domain;
security_classes
该文件定义了所有通用客体类别:
# FLASK
#
# Define the security object classes
#
# Classes marked as userspace are classes
# for userspace object managers
class security
class process
class system
class capability
....
attributes
该文件定义了所有SContext中的通用type:
######################################
# Attribute declarations
#
# All types used for devices.
attribute dev_type;
# All types used for processes.
attribute domain;
# All types used for filesystems.
attribute fs_type;
....
access_vectors
该文件定义了资源的所有通用许可:
#
# Define common prefixes for access vectors
#
# common common_name { permission_name ... }
#
# Define a common prefix for file access vectors.
#
common file
{
ioctl
read
write
create
getattr
setattr
lock
relabelfrom
relabelto
append
unlink
link
rename
execute
swapon
quotaon
mounton
}
.....
file.te
该文件自定义了file_contexts中的所有文件类型的type:
# Filesystem types
type labeledfs, fs_type;
type pipefs, fs_type;
type sockfs, fs_type;
type rootfs, fs_type;
type proc, fs_type;
....
device.te
该文件自定义了file_contexts中的所有设备节点的type:
# Device types
type device, dev_type, fs_type;
type alarm_device, dev_type, mlstrustedobject;
type adb_device, dev_type;
file_contexts
....
该文件定义了所有文件资源的SContext:
#/##########################################
# Root
/ u:object_r:rootfs:s0
# Data files
/adb_keys u:object_r:adb_keys_file:s0
/default\.prop u:object_r:rootfs:s0
/fstab\..* u:object_r:rootfs:s0
/init\..* u:object_r:rootfs:s0
/res(/.*)? u:object_r:rootfs:s0
/ueventd\..* u:object_r:rootfs:s0
....
genfs_contexts
定义了虚拟文件的SContext:
# Label inodes with the fs label.
genfscon rootfs / u:object_r:rootfs:s0
# proc labeling can be further refined (longest matching prefix).
genfscon proc / u:object_r:proc:s0
genfscon proc /net u:object_r:proc_net:s0
....
fs_use
定义了所有文件系统类型的SContext:
# Label inodes via getxattr.
fs_use_xattr yaffs2 u:object_r:labeledfs:s0;
fs_use_xattr jffs2 u:object_r:labeledfs:s0;
fs_use_xattr ext2 u:object_r:labeledfs:s0;
....
service.te
定义了所有service_contexts中SContext的type:
type bluetooth_service, service_manager_type;
type default_android_service, service_manager_type;
type drmserver_service, service_manager_type;
....
service_contexts
定义了所有所有系统service的SContext:
accessibility u:object_r:accessibility_service:s0
account u:object_r:account_service:s0
activity u:object_r:activity_service:s0
alarm u:object_r:alarm_service:s0
....
mac_permissions.xml
与seapp_contexts一起负责为接下来安装的不同类型的app添加SContext。
<?xml version="1.0" encoding="utf-8"?>
<policy>
....
<!-- Platform dev key in AOSP -->
<signer signature="@PLATFORM" >
<seinfo value="platform" />
</signer>
<!-- All other keys -->
<default>
<seinfo value="default" />
</default>
</policy>
seapp_contexts
与mac_permissions.xml一起负责为接下来安装的不同类型的app添加SContext。
isSystemServer=true domain=system_server
user=system seinfo=platform domain=system_app type=system_app_data_file
user=bluetooth seinfo=platform domain=bluetooth type=bluetooth_data_file
user=nfc seinfo=platform domain=nfc type=nfc_data_file
....
2、其他te文件
剩余的.te文件中主要是各个进程的具体策略控制。这里我们拿recovery.te来举例说明。
recovery.te
# recovery console (used in recovery init.rc for /sbin/recovery)
# Declare the domain unconditionally so we can always reference it
# in neverallow rules.
type recovery, domain;
# But the allow rules are only included in the recovery policy.
# Otherwise recovery is only allowed the domain rules.
recovery_only(`
allow recovery self:capability { chown dac_override fowner fsetid setfcap setuid setgid sys_admin sys_tty_config };
# Set security contexts on files that are not known to the loaded policy.
allow recovery self:capability2 mac_admin;
# Run helpers from / or /system without changing domain.
allow recovery rootfs:file execute_no_trans;
allow recovery system_file:file execute_no_trans;
.....
allow recovery kernel:process setsched;
')
....
neverallow recovery data_file_type:file { no_w_file_perms no_x_file_perms };
neverallow recovery data_file_type:dir no_w_dir_perms;
这里定义了有关recovery的所有策略控制。
首先,文件开头定义了进程域名recovery,继承自domain,说明这里新定义的recovery域为进程域。接下来在recovery_only中制定了很多策略控制。
如:allow recovery system_file:file execute_no_trans;表示允许SContext标志为recovery的进程不可传递域地去执行SContext为system_file的文件。
最后还有neverallow检查语句,
如:neverallow recovery data_file_type:dir no_w_dir_perms;表示不允许SContext标志为recovery的进程去no_w_dir_perms SContext为data_file_type的目录。
五、如何配置SElinux
在支持SElinux的Android版本中,有关selinux策略文件有两处,一个是google原生的,另一个是产品平台配置的,他们分别对应如下目录:
google原生目录:s912/external/sepolicy
平台配置目录:s912/device/amlogic/common/sepolicy
在编译时, 系统会以合并的方式,将平台配置目录下的policy 附加到Google 原生的policy 上,而非替换。一般情况下,不建议修改google原生目录下的策略控制。接下来看下如何配置平台的selinux。
找到平台下的BoardConfig.mk,其相关变量配置就是BOARD_SEPOLICY_DIRS,目的就是指定平台sepolicy的目录。
BoardConfig.mk中直接包含了sepolicy的mkfile文件:
include device/amlogic/common/sepolicy.mk
打开该sepolicy.mk文件可以看到BOARD_SEPOLICY_DIRS变量的配置:
BOARD_SEPOLICY_DIRS := \
device/amlogic/common/sepolicy
六、SElinux调试方法
1、selinux的编译
首先我们关注下selinux的编译,大家知道编译整个Android工程是个很耗时的工作。为了避免耗时,在我们修改或者添加了selinux策略之后,建议先独自对sepolicy模块进行一次编译,如果通过了,然后再进行整个Android的编译。具体指令如下:
如:mmm external/sepolicy
2、如何快速确认是selinux引起?
目前所有的SELinux check 失败,在kernel log 或者android log(L版本后)中都有对应的" avc: denied"的LOG 与之对应。举例如下:
<5>[ 17.285600].(0)[503:idmap]type=1400 audit(1356999072.320:202): avc: denied { create } for pid=503 comm="idmap" name="overlays.list" scontext=u:r:zygote:s0 tcontext=u:object_r:resource_cache_data_file:s0 tclass=file
即表明idmap 这个process, 使用zygote 的source context, 访问/data/resource_cache 目录,并创建文件时,被SELinux 拒绝访问。
但反过来,有此LOG,并非就会直接失败,还需要确认当时SELinux 的模式, 是enforcing mode 还是 permissve mode.这里需要提到的是selinux有3中模式:permissive、enforcing和disabled。
-
enforcing:强制模式,代表 SELinux 运作中,且已经正确的开始限制 domain/type 了;
-
permissive:宽容模式:代表 SELinux 运作中,不过仅会有警告讯息并不会实际限制 domain/type 的存取。这种模式可以运来作为 SELinux 的 debug 之用;
-
disabled:关闭,SELinux 并没有实际运作。
如果问题容易复现,我们可以先将SELinux 模式调整到Permissive mode,然后再测试确认是否与SELinux 约束相关.
指令如下:
查看selinux当前模式:
[root@python bin]# getenforce
Permissive
更改当前的SELINUX值 ,后面可以跟 enforcing,permissive 或者 1, 0。
[root@python bin]# setenforce permissive
[root@python bin]# setenforce enforcing
3、修改/添加selinux策略
首先, 务必确认对应进程访问系统资源是否正常, 是否有必要?如果本身是异常非法访问,那么就要自行消除访问。
其次,如果确认访问是必要,并且正常的,那么就要对对应的process/domain 增加新的policy.
具体步骤大致如下:
1).简化方法
1.1 提取所有的avc LOG. 如 adb shell "cat /proc/kmsg | grep avc" > avc_log.txt
1.2 使用 audit2allow tool 直接生成policy. audit2allow -i avc_log.txt 即可自动输出生成的policy
1.3 将对应的policy 添加到selinux policy 规则中,对应MTK Solution, 您可以将它们添加在KK: mediatek/custom/common/sepolicy, L: device/mediatek/common/sepolicy 下面,如
allow zygote resource_cache_data_file:dir rw_dir_perms;
allow zygote resource_cache_data_file:file create_file_perms;
===> mediatek/custom/common/sepolicy/zygote.te (KK)
===> device/mediatek/common/sepolicy/zygote.te (L)
注意audit2allow 它自动机械的帮您将LOG 转换成policy, 而无法知道你操作的真实意图,有可能出现权限放大问题。
2).按需确认方法
此方法需要工程人员对SELinux 基本原理,以及SELinux Policy Language 有了解.
2.1 确认是哪个进程访问哪个资源,具体需要哪些访问权限,read ? write ? exec ? create ? search ?
2.2 当前进程是否已经创建了policy 文件? 通常是process 的执行档.te,如果没有,并且它的父进程即source context 无须访问对应的资源,则创建新的te 文件.
在L 版本上, Google 要求维护关键 security context 的唯一性, 比如严禁zygote, netd, installd, vold, ueventd 等关键process 与其它process 共享同一个security context.
2.3 创建文件后,关联它的执行档,在file_contexts 中, 关联相关的执行档.
比如 /system/bin/idmap 则是 /system/bin/idmap u:object_r:idmap_exec:s0
2.4 填写policy 到相关的te 文件中
如果沿用原来父进程的te 文件,则直接添加.
如果是新的文件,那么首先:
#==============================================
# Type Declaration
#==============================================
type idmap, domain;
type idmap_exec, exec_type, file_type;
#==============================================
# Android Policy Rule
#==============================================
#permissive idmap;
domain_auto_trans(zygote, idmap_exec, idmap);
然后添加新的policy
# new policy
allow idmap resource_cache_data_file:dir rw_dir_perms;
allow idmap resource_cache_data_file:file create_file_perms;
3).权限放大情况处理
如果直接按照avc: denied 的LOG 转换出SELinux Policy, 往往会产生权限放大问题. 比如因为要访问某个device, 在这个device 没有细化SELinux Label 的情况下, 可能出现:
<7>[11281.586780] avc: denied { read write } for pid=1217 comm="mediaserver" name="tfa9897" dev="tmpfs" ino=4385 scontext=u:r:mediaserver:s0 tcontext=u:object_r:device:s0 tclass=chr_file permissive=0
如果直接按照此LOG 转换出SELinux Policy: allow mediaserver device:chr_file {read write}; 那么就会放开mediaserver 读写所有device 的权限. 而Google 为了防止这样的情况, 使用了neverallow 语句来约束, 这样你编译sepolicy 时就无法编译通过.
为了规避这种权限放大情况, 我们需要细化访问目标(Object) 的SELinux Label, 做到按需申请. 通常会由三步构成
3.1 定义相关的SELinux type.
比如上述案例, 在 device/mediatek/common/sepolicy/device.te 添加
type tfa9897_device, dev_type;
3.2 绑定文件与SELinux type.
比如上述案例, 在 device/mediatek/common/sepolicy/file_contexts 添加
/dev/tfa9897(/.*)? u:object_r:tfa9897_device:s0
3.3 添加对应process/domain 的访问权限.
比如上述案例, 在 device/mediatek/common/sepolicy/mediaserver.te 添加
allow mediaserver tfa9897_device:chr_file { open read write };
那么,哪些访问对象通常会遇到此类呢?(以L 版本为例)
*device类型
-- 类型定义: external/sepolicy/device.te;device/mediatek/common/sepolicy/device.te
-- 类型绑定: external/sepolicy/file_contexts;device/mediatek/common/sepolicy/file_contexts
*File类型:
-- 类型定义: external/sepolicy/file.te;device/mediatek/common/sepolicy/file.te
-- 绑定类型: external/sepolicy/file_contexts;device/mediatek/common/sepolicy/file_contexts
*虚拟File类型:
-- 类型定义: external/sepolicy/file.te;device/mediatek/common/sepolicy/file.te
-- 绑定类型: external/sepolicy/genfs_contexts;device/mediatek/common/sepolicy/genfs_contexts
*Service类型:
-- 类型定义: external/sepolicy/service.te; device/mediatek/common/sepolicy/service.te
-- 绑定类型:external/sepolicyservice_contexts;device/mediatek/common/sepolicy/service_contexts
*Property类型:
-- 类型定义: external/sepolicy/property.te;device/mediatek/common/sepolicy/property.te
-- 绑定类型: external/sepolicy/property_contexts;device/mediatek/common/sepolicy/property_contexts;
七、案例分析
案例一、OTA升级过程中出现/system/etc/ppp/ip-up-vpn无法添加scontext
【问题现象】
制作好的差分包在升级过程中出现如下错误而导致升级失败,且重启后一直处于recovery模式下:
Symlinks and permissions...
ApplyParsedPerms: lsetfilecon of /system/etc/ppp/ip-up-vpn to u:ppp_system_file:s0 failed:Permission denied
set_metadata:some changes failed
E:Error in /udisk/update.zip
(Status 7)
该问题与问题一的提示信息基本一致,为何还是将本问题摘出来?这是因为他们的改动方案有所不同。
【问题分析】
出现上述错误后,根据提示信息可以确定升级包校验以及系统校验已经完成。这里是由于recovery中涉及selinux方面在向/system/etc/ppp/ip-up-vpn文件添加"u: ppp_system_file:s0"上下文(scontext)时因权限问题被拒绝了。
【解决方案】
方案1、3同问题一。
方案2、将META-INF\com\google\android\updater-script中的下列条目删除:
set_metadata_recursive("/system/etc/ppp ", "uid", 0, "gid", 0, "dmode", 0755, "fmode", 0555, "capabilities", 0x0, "selabel", "u:object_r:ppp_system_file:s0");
方案4、根据方案2中所删除的额条目中的function可以看出,该条目目的是要在system/etc/ppp目录下进行递归加scontext,但不知为何没有生效。由于system/etc/ppp目录下只有ip-up-vpn一个文件,该方案仿照问题一中方案4按照直接操作ip-up-vpn文件的思路进行添加修改,具体修改如下:
a、ip-up-vpn也是可执行文件,首先定义出该文件的selinux权限规则:
在device/amlogic/common/sepolicy目录下创建ip-up-vpn.te文件,并添加如下内容:
type ip-up-vpn, domain;
type ip-up-vpn_exec, exec_type, file_type;
init_daemon_domain(ip-up-vpn)
# Third line is important
allow ip-up-vpn input_device:chr_file rw_file_perms;
allow ip-up-vpn ppp_exec:file { execute_no_trans execute getattr read open };
allow ip-up-vpn ip-up-vpn_exec:file { entrypoint read setattr execute };
并更改其传统linux权限为777:
chmod 777 ip-up-vpn.te
b、在file_contexts中定义ip-up-vpn上下文属性(scontext):
/system/etc/ppp/ip-up-vpn u:object_r:ip-up-vpn_exec:s0
c、在recovery.te中添加操控ip-up-vpn的权限:
allow recovery ip-up-vpn_exec:file {create_file_perms relabelfrom relabelto create write setattr};
【结果确认】updater-script中该条目通过selinux操作,ota差分升级成功。
案例二、recovery删除data目录下的文件
在recover升级的时候,我们有个需要从服务器上下载升级包,然后放在data/download,这部分工作是放在一个systemapp中完成的。
然后重启进入recoveyr模式,在recovery升级完之后,我们需要在recovery中删除这个文件,这部分是在recovery中完成。
功能很简单,但是在android6.0上碰到selinux的问题。
我们先来看recovery.te中是不允许recovery操作data下面的目录,也就是下面这个neverallow原则
# Recovery should never touch /data.
#
# In particular, if /data is encrypted, it is not accessible
# to recovery anyway.
#
# For now, we only enforce write/execute restrictions, as domain.te
# contains a number of read-only rules that apply to all
# domains, including recovery.
#
# TODO: tighten this up further.
neverallow recovery data_file_type:file { no_w_file_perms no_x_file_perms };
neverallow recovery data_file_type:dir no_w_dir_perms;
我们再来看下system_data_file在file_contexts.te中的定义
/data(/.*)? u:object_r:system_data_file:s0
而在file.te定义了system_data_file文件属于data_file_type类型,因此recovery也是不能操作data下面的文件
[html] view plain copy 在CODE上查看代码片派生到我的代码片
./file.te:56:type system_data_file, file_type, data_file_type;
在我们可以自己定义自己的file类型,我们可以定义data/download属于download_data_file类型
/data/download(/.*)? u:object_r:download_data_file:s0
然后在file.te中定义download_data_file类型, 注意是只属于file_type类型的。
type download_data_file, file_type;
然后在recovery.te中增加对download_data_file的权限
allow recovery download_data_file:dir { write search remove_name };
allow recovery download_data_file:file { read getattr unlink };
另外我们还需在一个systemapp中先下载升级包,所以需要在system_app.te中增加对如下权限:
allow system_app download_data_file:dir { search write add_name getattr remove_name };
allow system_app download_data_file:file { create read write open getattr unlink };
但是最后还是失败了,为什么呢,因为一开始没有data/download这个目录,而是我们再app中自己下载的时候创建的目录,所以是system_data_file类型的。那么我们就需要在一开始就有data/download目录,所以在init.rc中添加如下代码:
mkdir /data/download 0771 system system
最后我们还需要在uncrypt.te中增加如下,因为data目录有可能需要进行加密处理。
allow uncrypt download_data_file:dir { search getattr };
allow uncrypt download_data_file:file { getattr read open };
八、常用指令
SELinux 是个经过安全强化的Linux操作系统,实际上,基本上原来的运用软件没有必要修改就能在它上面运行。真正做了特别修改的RPM包只要50多个。像文件系统EXT3都是经过了扩展。对于一些原有的命令也进行了扩展,另外还增加了一些新的命令,接下来我们就来看看这些命令。
1、文件操作
1)ls命令
在命令后加个 -Z 或者加 –context
[root@python azureus]# ls -Z
-rwxr-xr-x fu fu user_u:object_r:user_home_t azureus
-rw-r--r-- fu fu user_u:object_r:user_home_t Azureus2.jar+
-rw-r--r-- fu fu user_u:object_r:user_home_t Azureus.png
2)chcon
更改文件的标签
[root@python tmp]# ls --context test.txt
-rw-r--r-- root root root:object_r:staff_tmp_t test.txt
[root@python tmp]# chcon -t etc_t test.txt
[root@python tmp]# ls -lZ test.txt
-rw-r--r-- root root root:object_r:etc_t test.txt
3)restorecon
当这个文件在策略里有定义是,可以恢复原来的 文件标签。
4)setfiles
跟chcon一样可以更改一部分文件的标签,不需要对整个文件系统重新设定标签。
5)fixfiles
一般是对整个文件系统的, 后面一般跟 relabel,对整个系统 relabel后,一般我们都重新启动。如果,在根目录下有.autorelabel空文件的话,每次重新启动时都调用 fixfiles relabel
6)star
就是tar在SELinux下的互换命令,能把文件的标签也一起备份起来。
7)cp
可以跟 -Z, --context=CONTEXT 在拷贝的时候指定目的地文件的security context
8)find
可以跟 –context 查特定的type的文件。
例子:
find /home/fu/ --context fu:fu_r:amule_t -exec ls -Z {} \:
9)run_init
在sysadm_t里手动启动一些如Apache之类的程序,也可以让它正常进行,domain迁移。
2、进程domain的确认
程序现在在那个domain里运行,我们可以在ps 命令后加 -Z
[root@python /]# ps -eZ
LABEL PID TTY TIME CMD
system_u:system_r:init_t 1 ? 00:00:00 init
system_u:system_r:kernel_t 2 ? 00:00:00 ksoftirqd/0
system_u:system_r:kernel_t 3 ? 00:00:00 watchdog/0
3、ROLE的确认和变更
命令id能用来确认自己的 security context
[root@python ~]# id
uid=0(root) gid=0(root) groups=0(root),1(bin),2(daemon),3(sys),4(adm),6(disk),10(wheel) context=root:staff_r:staff_t
这里,虽然是ROOT用户,但也只是在一般的ROLE和staff_t里运行,如果在enforcing模式下,这时的ROOT对于系统管理工作来说什么也干不了。
[root@python ~]# newrole -r sysadm_r
Authenticating root.
口令:
[root@python ~]# id
uid=0(root) gid=0(root) groups=0(root),1(bin),2(daemon),3(sys),4(adm),6(disk),10(wheel) context=root:sysadm_r:sysadm_t
4、模式切换
1)getenforce
得到当前的SELINUX值
[root@python bin]# getenforce
Permissive
2)setenforce
更改当前的SELINUX值 ,后面可以跟 enforcing,permissive 或者 1, 0。
[root@python bin]# setenforce permissive
3)sestatus
显示当前的 SELinux的信息
[root@python bin]# sestatus -v
SELinux status: enabled
SELinuxfs mount: /selinux
Current mode: permissive
Mode from config file: permissive
Policy version: 20
Policy from config file: refpolicy
Process contexts:
Current context: user_u:user_r:user_t
Init context: system_u:system_r:init_t
/sbin/mingetty system_u:system_r:getty_t
/usr/sbin/sshd system_u:system_r:sshd_t
File contexts:
Controlling term: user_u:object_r:user_devpts_t
/etc/passwd system_u:object_r:etc_t
/etc/shadow system_u:object_r:shadow_t
/bin/bash system_u:object_r:shell_exec_t
/bin/login system_u:object_r:login_exec_t
/bin/sh system_u:object_r:bin_t -> system_u:object_r:shell_exec_t
/sbin/agetty system_u:object_r:getty_exec_t
/sbin/init system_u:object_r:init_exec_t
/sbin/mingetty system_u:object_r:getty_exec_t
5、其他重要命令
1)Audit2allow
很重要的一个以python写的命令,主要用来处理日志,把日志中的违反策略的动作的记录,转换成 access vector,对开发安全策略非常有用。在refpolicy里,它的功能比以前有了很大的扩展。
[root@python log]# cat dmesg | audit2allow -m local > local.te
2)checkmodule
编译模块
[root@python log]# checkmodule -m -o local.mod local.te
checkmodule: loading policy configuration from local.te
checkmodule: policy configuration loaded
checkmodule: writing binary representation (version 5) to local.mod
3)semodule_package
创建新的模块
[root@python log]# semodule_package -o local.pp -m local.mod
4)semodule
可以显示,加载,删除 模块
加载的例子:
[root@python log]# semodule -i local.pp
5)semanage
这是一个功能强大的策略管理工具,有了它即使没有策略的源代码,也是可以管理安全策略的。因为我主要是介绍用源代码来修改策略的,详细用法大家可以参考它的man页。
九、参考文献
1. Security-Enhanced Linux in Android
https://source.android.com/security/selinux/index.html
2. 深入理解SELinux SEAndroid
http://blog.csdn.net/innost/article/details/19299937
3.SElinux详解.pdf
4.类型强制(TE编写规则)
http://blog.csdn.net/myarrow/article/details/10105961