这篇文章是替老婆写的公司内部培训PPT。
自从不搞机后,很少研究Android底层架构了,想想我搞智能机的时侯,android4.0 ICS才刚出来。现在已经是Andorid N 7.0了。时光飞逝呀。
文章的主要目的是介绍SELinux及在Android上的规则,让看人看了之后懂得SELinux安全机制是啥样的,并且知道一些常见的问题怎么入手处理。不涉及它的实现。
文章分四部分: SELinux, SEAndroid,Samples, Debug
SELinux全称Security-Enhanced Linux
由美国国家安全局NSA 和SCC(Secure Computing Corporation)发起的一个强制访问控制安全模块。在linux kernel 2.6版本后,整合到linux的安全模块中。
SElinux想要解决的是安全问题,简单地说就是,指定的进程只能访问特定的资源,执行特定的操作,而在规定之外的则不能进行操作。从而避免越权的操作,进而达到系统安全。
SELinux提供了一种灵活的强制访问控制(MAC)系统,且内嵌于Linux Kernel中。
DAC linux 传统的权限管理机制
全称Discretionary Access control, 自主访问控制
基于用户和分组,进程的权限取决于调起这个进程的用户的权限
按照 Owner Group Other 对访问资源的进程的权限进行定义
Root用户具有所有的访问权限
MAC Mandatory Access control, 强制性访问控制
每一项访问, 每访问一个文件资源都需要根据已定义的策略进行针对性的验证
限制Root 权限, 即使你有root 权限, 如果无法通过MAC 验证, 那么一样的无法真正执行相关的操作
对每一项权限进行了更加完整的细化, 可限制用户对资源的访问行为
每个系统调用(system call)访问资源时:
先DAC审查,通过后过再进行SELinux的MAC审查
所有资源可以分为两类: 主体和客体
主体是活的,可以主动对其他资源进行某些操作的,在linux中指进程。
客体则是死的,被动的。除了主体之外的资源(file,socket,device等)。
所有subject和object都有自己的标签,比如进程,目录,文件,设备,网终端口,主机名等资源都有自己的标签。
通过编写规则来控制某个subject标签对某个object标签的访问,这就是所谓管理政策。
如下面 u:object_r:system_file:s0 表示 用户,角色,类型,安全级别 , 这些字符串都是标签
-rwxr-xr-x root shell u:object_r:system_file:s0 dexdump
对象都有一个安全上下文(Security Context),它是一串字符串,通示标签构建。
标准格式:user:role:type:mls_level
比如u:object_r:system_file:s0
用于角色访问控制,user不是linux uid,是MAC专用的定义,一个user可以属于多个role,不同的role具有不同的权限, android只定义一个用户u和一个subject角色r,一个object角色object_r,即不使用角色访问控制功能,这里不作详述。
主体(subject)和客体(object)都关联有一个安全级别,安全级别较高的主体可以读取安全级别较低的客体,而安全级别较低的主体可以写入安全级别较高的客体,通过这种规则,可以允许数据从安全级别较低的主体流向安全级别较高的主体,而限制数据从安全级别较高的主体流向安全级别较低的主体.Android只定义一种安全级别S0,即不使用安全级别。
type: 标注资源类型。
domain:和type一样,但是只限于标注subject,即进程的类型。
class:客体(object)的类别,本质上是具有相同操作权限集合的资源类型 , 比如file,它有open,read,write等操作,就定义一个file的class,包含所有相关操作权限。
指定处于什么域的subject可以对什么类型的object执行那些权限的操作.
比如:
allow appdomain app_data_file:file rw_file_perms;
表示 所有应用的域都允许读写标记有app_data_file的文件
对象访问系统资源都要进行安全上下文审查,审查规则有:
类型强制检测(type enforcement)
主要的审查机制,根据对像类型和域制定权限策略。
多层安全审查(Multi-Level Security)
android只定义一种权限级别s0,没有启用多层权限。
基于角色的访问控制(RBAC: Role Based Access Control)
android只定一种用户u,没有启用角色控制。
TE根据Type标签进行安全审查
审查 subject type 对 object type 的某个class 类型中某种permission 是否具有访问权限,是目前使用最为广泛的MAC 审查机制
策略格式:
rule subject_type target_type : class perm_set
rule:控制类型,比如allow neverallow.
subject_type:指domain
target_type:请求资源的类型
class perm_set: 对资源的操作
//init进程 创建/data/property and files
allow init property_data_file:dir create_dir_perms;
allow init property_data_file:file create_file_perms;
Domain Transitions
一个进程fork 另外一个进程并执行(exec) 一个执行档时,为了避免新进程权限和源进程一样大,需要进行domain 切换
Object Transitions
process创建文件时, 默认是沿用父目录的Security Context, 如果要设置成特定的Label, 就必须进行Object切换。
Permissve Mode(宽容模式)
通过Audit System 记录LOG, 但不真正拦截访问。
Enfocing mode(强制模式)
在打印LOG 的同时,还会真正的拦截访问.
在调试时,我们会启用Permissive Mode, 以便尽可能的发现多的问题, 然后一次修正. 在真正量产时使用Enforcing mode, 来保护系统.
SEAndroid 是将SELinux 移植到Android 上的产物,可以看成SELinux 辅以一套适用于Android 的策略。
特点:
只定义一个用户u和一个subject角色r,一个object角色object_r.
只定义一种权限级别s0
表明SEAndroid的安全策略只用到了类型强制检测,多层安全审查和角色访问控制都没有使用。
Android的安全模型是基于应用程序沙箱(sandbox)的概念, 每个应用程序都运行在自己的沙箱之中。
Android 4.3之前:
应用程序安装时为每一个应用程序创建一个独立的uid,基于uid来控制进程权限,即DAC机制。
Android 4.3:
开启SELlinux Permissive 模式
Android 4.4:
对netd, installd, zygote, vold 四个原本具有root 权限的process, 以及它们fork 出的子进程启用Enforce 模式
Android 5.0(L):
普遍性开启SELinux Enforce mode
除了init进程,其他进程都不应该在init 域中
android rom中的路径:
/sepolicy
/file_contexts
/seapp_contexts
/service_contexts
/property_contexts
/system/etc/security/mac_permissions.xml
/selinux_version
/selinux_network.sh
android源码中路径:
external/sepolicy (通用)
device/mediatek/common/sepolicy/ 设备商定制
讲解SEAndroid策略文件前,先了解一些基本语法,主要有下面三类
type
class
allow
type用于定义资源类型 即把资源类型和属性关联到一起
属性在后面有详细介绍
type type_id [alias alias_id,] [attribute_id]
将type_id(别名为alias)关联到attribute. 这样的话,方便用attribute来管理不同的type中包含相同的属性的部分。
例子:
type init, domain;
将init关联到domain,即将domain设置为init类型的属性
用于定义资源类别的权限集合
class class_name [ inherits common_name ] { permission_name … }
inherits表示继承了common定义的权限,然后自己额外实现了permission_name的权限
例子:
class dir inherits file
{
add_name
remove_name
reparent
search
rmdir
open
audit_access
execmod
}
dir继承 file的操作权限集合并增加自己的操作
allow:赋予某项权限。
allowaudit:audit含义就是记录某项操作。默认情况下是SELinux只记录那些权限检查失败的操作
dontaudit:对那些权限检查失败的操作不做记录。
neverallow:用来检查安全策略文件中是否有违反该项规则的allow语句。
例子:
allow init unlabeled:filesystem mount;
允许init类型对unlabeled类型的filesystem进行mount的操作
neverallow { appdomain -unconfineddomain } kmem_device:chr_file { read write };
绝对不允许app(除了有unconfineddomain属性的app)对kmem_device类型的字符设备进行读写的操作
TE安全策略中最基本的参量是type,同时将具有共性的type归在一起构成一个称为attribute的集合。
sepolicy的规则执行也能以attribute作为执行对象
每一个资源对应一个type, 每一个type 对应有一个或几个Attribute.
attrubites定义在external/attrubites:
attribute dev_type;
attribute domain;
attribute fs_type;
Type 的定义就比较分散, 主要有:
type pipefs, fs_type;
type sockfs, fs_type;
type rootfs, fs_type;
type device, dev_type, fs_type;
type adb_device, dev_type;
type ashmem_device, dev_type, mlstrustedobject;
type audio_device, dev_type;
type binder_device, dev_type, mlstrustedobject;
type camera_device, dev_type;
access_vectors:定义每一个class被允许的操作
ile_contexts:保存系统中所有文件的安全上下文定义
roles:只定义了一个role,名字就是r
users:将user与role进行了关联
global_macros:定义一些全局的宏定义
te_macros:定义一些TE策略通用操作的宏
*.te:类型强制规则文件
genfs_contexts:虚拟文件系统的安全上下文设置规则
seapp_contexts :用于app打type标签
service_contexts :系统服务安全上下文
property_contexts :系统属性安全上下文
port_contexts:网络端口安全上下文
维持external/sepolicy 与Google AOSP一致, 尽量不要修改. 特别是不要去除任何的neverallow 语句.
严禁修改untrusted_app.te, 放开untrusted_app 的权限.
系统关键进程启动长时间运行的process, 必须进行domain 切换. 比如netd, installd, vold, zygote 等.
Native thread 严禁使用kernel 标签, 而kernel thread 除init 外, 都应当是kernel 标签.
在user build 中不能存在如su, recovery, init_shell 标签的进程.
在user build 中所有的process 都必须是Enforce Mode
domain 类型定义
执行档 入口定义
SELinux 模式设置
domain 切换
类型访问规则
以mediaserver 这个进程的定义来说明, 对应文件是 mediaserver.te
type mediaserver, domain;
type mediaserver_exec, exec_type, file_type;
在L 版本上,默认都是 enforcing mode
init_daemon_domain(mediaserver)
这个是一个复杂的TE操作宏, 简单来说就是当init fork 子进程执行mediaserver_exec 这个类型的执行档时, 其domain 从init 切换到mediaserver.
unix_socket_connect(mediaserver, property, init)
r_dir_file(mediaserver, sdcard_type)
binder_use(mediaserver)
binder_call(mediaserver, binderservicedomain)
binder_call(mediaserver, appdomain)
binder_service(mediaserver)
allow mediaserver self:process execmem;
allow mediaserver kernel:system module_request;
allow mediaserver media_data_file:dir create_dir_perms;
allow mediaserver media_data_file:file create_file_perms;
allow mediaserver app_data_file:dir search;
allow mediaserver app_data_file:file rw_file_perms;
allow mediaserver sdcard_type:file write;
allow mediaserver gpu_device:chr_file rw_file_perms;
allow mediaserver video_device:dir r_dir_perms;
假如我们要新增一个init 启动的service,名字叫demo_service, 对应的执行档是/system/bin/demo.
/device/mediatke/common/sepolicy 目录下新增demo.te, 在/device/mediatke/common/BoardConfig.mk 的BOARD_SEPOLICY_UNION 宏中新增 demo.te
在demo.te 中
•type demo, domain;
•type demo_exec, exec_type, file_type;
•init_daemon_domain(demo)
/system/bin/demo u:object_r:demo_exec:s0
将SELinux 模式调整到Permissive 模式,然后再测试确认是否与SELinux 约束相关.
ENG 版本:
adb shell setenforce 0
如果还能复现问题,则与SELinux 无关, 如果原本很容易复现, 而Permissive mode 不能再复现, 那么就可能关系比较大.
在Kernel LOG / Main Log 中查询关键字 “avc:” 看看是否有SELinux Policy Exception, 并进一步确认这个异常是否与当时的逻辑相关.
<5> type=1400 audit: avc: denied { read write } for pid=177
comm=”rmt_storage” name=”mem” dev=”tmpfs” ino=6004 scontext=u:r:rmt:s0
tcontext=u:object_r:kmem_device:s0 tclass=chr_file
avc: denied { read write } : 拒绝 read write操作
pid=177 comm=”rmt_storage”: pid进程号 comm进程名
scontext=u:r:rmt:s0 : subject context
tcontext=u:object_r:kmem_device:s0 : object context
tclass=chr_file : class类型是chr_file(字符设备文件)
意思是rmt_storeage进程使用rmt的context访问字符设备kmem_device,并进行read write操作时,被SELinux拒绝
上面的例子中,很明显看出原因是在sepolicy 策略语言中,缺乏这样的语句allow rmt kmem_device:chr_file {read write}
所以在/external/sepolicy/ 目录下相应的te文件加入allow rmt kmem_device:chr_file {read write},重编编译,刷机即可。
当avc很多时,人工去看容易出错且慢,我们可以使用aduit2allow工具来完成这项工作.
audit2allow是policycoreutils中的工具之一,需要在你的linux开发环境中安装。
aduit2allow可以分析log,并自动给出建议的sepolicy语句。
adb shell su -c dmesg | audit2allow
把dmesg log输入到aduit2allow
例如上诉avc语句就会输出:
============= rmt ==============
allow rmt kmem_device:chr_file { read write };