打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
SEAndroid在项目开发中的应用

LG项目的开发过程中,客户要求将一些重要数据保存在Android文件系统中,这样数据便可永久保存,避免被格式化。比如将用户通话的累计时长保存在/persist目录下,以避免用户不良退机事件。对Android文件系统中资源进行操作,涉及SELinux机制应用。从Android4.4开始,Android便引入了SELinux机制,SELinux机制下文件系统中的数据受到更严格的保护,应用程序必须明确赋予相应权限后才可读写文件系统中的数据。


一、Android安全机制历程

SELinux机制引入前,Android是基于传统的Linux数据保护机制,即DAC全称是DiscretionaryAccessControl,翻译为自主访问控制。DAC的核心思想就是:进程理论上所拥有的权限与执行它的用户的权限相同。比如,以root用户启动Browser,那么Browser就有root用户的权限,在Linux系统上能干任何事情。因此,只要拥有root权限,系统数据便无安全可言,隐患极大。出于用户数据安全考虑,从Android4.4开始引入SELinux机制,其安全机制叫MACMandatoryAccessControl),翻译为强制访问控制。MAC的核心思想就是:即任何进程想在SELinux系统中干任何事情,都必须先在安全策略配置文件中赋予权限。凡是没有出现在安全策略配置文件中的权限,进程就没有该权限。

引入SELinux机制的Android被称为SEAndroid,所添加的实现代码位于external/sepolicy目录下

来看一个SEAndroid中设置权限的例子:

[例子1]

/*from external/sepolicy/netd.te

 下面这条SELinux语句表示:允许(allownetd域(domain)中的进程 ”写(write)“ 

类型为proc的文件*/

allownetdproc:file write

如果没有在netd.te中使用上例中的权限配置allow语句,则netd就无法往/proc目录下得任何文件中写数据,即使netd具有root权限。显然,MACDAC在权限管理这一块要复杂、要严格、要细致得多。


二、SELinux机制实现

Linux中有两种对象,一种死的(Inactive),一种活的(Active)。死的东西就是文件(Linux哲学:万物皆文件。注意,万不可狭义解释为File),而活的东西就是进程。此处的“死”和“活”是一种比喻,映射到软件层面的意思是:进程能发起动作,如它能打开文件并操作它。而文件只能被进程操作。

1) Security Context

SELinux中所有对象都会被赋予一个安全属性,叫SecurityContextSecurityContext(后续用SContext表示)是一个字符串,主要由三部分组成。可通过相关命令来查询对象的SContext

例如,SEAndroid中进程的SContext可通过ps-Z命令查看,如下图所示:










1 ps -Z结果图

最左边的就是进程的SContext根据SELinux规范,完整的SContext字符串为:

user:role:type[:range]

注意,方括号中的内容表示可选项。s0属于range中的一部分,SContext的核心其实是前三个部分:

user:role:type

以第一个进程/system/bin/logwrapperSContext为例,其值为u:r:init:s0,其中:

  • uuser的意思。SEAndroid中定义了一个SELinux用户,值为u

  • rrole的意思。role是角色之意,它是SELinux中一种比较高层次,更方便的权限管理思路,即Role Based Access Control(基于角色的访问控制,简称为RBAC)。简单地说,一个u可以属于多个role,不同的role具有不同的权限。RBAC因项目开发中不涉及,本文不做介绍。

  • init,代表该进程所属的Domaininit。这里申明一下,MAC的基础管理思路是所谓的Type Enforcement Accesc Control(简称TEAC,一般用TE表示),而不是上面所说的RBAC。对进程来说,Type就是Domain。比如init这个Domain有什么权限,都需要通过类似[例子1]中的allow语句来说明。

  • S0SELinux为了满足军用和教育行业而设计的Multi-Level SecurityMLS)机制有关。简单地说,MLS将系统的进程和文件进行了分级,不同级别的资源需要对应级别的进程才能访问。因项目开发中不涉及,本文不介绍MLS

再来看文件的SContext,读者可通过ls-Z来查看,如图2所示:

2ls -Z结果图

2中倒数第二列所示为文件系统根目录下几个文件和目录的SContext信息,以第一行root目录为例,其信息为u:object_r:rootfs:s0

  • u:同样是user之意,它代表创建这个文件的SELinux user

  • object_r:文件是死的东西,它没法扮演角色,所以在SELinux中,死的东西都用object_r来表示它的role

  • rootfs:死的东西的Type,和进程的Domain其实是一个意思。它表示root目录对应的Typerootfs

  • s0MLS的级别。

通过进程与文件的两个例子,详细说明了SecurityContext。另外,这里说明一下:

SELinux中,各类数据的SecurityContext定义所在文件如下:
a. App
进程->mac_permissions.xml
b. App
数据文件->seapp_contexts
c.
系统文件->file_contexts
d.
系统属性->property_contexts

2) Security policy

MAC的核心思想可知,其机制实现的核心在于安全策略配置文件。那么,SELinux中安全策略配置文件如何进行编写?下面将介绍它。

由前述可知,MAC基本管理单位是TEACTypeEnforcement Accesc Control),然后是高一级别的RoleBased Accesc ControlRBAC是基于TE的,而TE也是SELinux中最主要的部分。因此,

下面来看看TE

2.1 TE介绍

在例子1中,大家已经见过TEallow语句了,再来细致研究下它:

[例子2]

allownetd proc:file write

这条语句的语法为:

  • allowTEallow语句,表示授权。除了allow之外,还有allowauditdontauditneverallow等。

  • netdsource type。也叫subjectdomain

  • proctarget type。它代表其后的file所对应的Type

  • file:代表Object Class。它代表能够给subject操作的一类东西。例如FileDirsocket等。在Android系统中,有一个其他Linux系统没有的Object Class,那就是Binder

  • write:在该类Object Class中所定义的操作。

根据SELinux规范,完整的allow相关的语句格式为:

rule_namesource_type target_type : class perm_set

我们直接来看几个实例:

[例子3]

//SEAndroid中的安全策略文件policy.conf

#允许zygote域中的进程向inittype的进程(ObjectClassprocess)发送sigchld信号

allowzygote init:process sigchld;

#允许zygote域中的进程searchgetattr类型为appdomain的目录。注意,多个perm_set

#可用{}括起来

allowzygote appdomain:dir { getattr search };

#来个复杂点的:

#source_typeunconfineddomaintarget_type为一组type,由
#{fs_type dev_type file_type }
构成。object_class也包含两个,为{chr_file file }

#perm_set语法比较奇特,前面有一个~号。它表示除了{entrypointrelabelto}之外,{chr_file#file}这两个object_class所拥有的其他操作

allowunconfineddomain {fs_type dev_type file_type}:{chr_file file }  \

 ~{entrypointrelabelto};

#特殊符号除了~外,还有-号和*号,其中:

#1):-号表示去除某项内容。

#2):*号表示所有内容。

#下面这条语句中,source_type为属于appdomain,但不属于unconfinedomain的进程。

#*表示所有和capability2相关的权限

#neverallow:表示绝不允许。

neverallow{ appdomain -unconfineddomain } self:capability2 *;

特别注意,前面曾提到说权限必须显示声明,没有声明的话默认就没有权限。那neverallow语句就没必要存在了。因为”无权限“是不需要声明的。确实如此,neverallow语句的作用只是在生成安全策略文件时进行检查,判断是否有违反neverallow语句的allow语句。例如,笔者修改shell.te中一个语句后,生成安全策略文件时就检测到了冲突,如图3所示:

3 neverallow的作用

如图3所示,笔者修改shell.te后,意外导致了一条allow语句与neverallow语句冲突,从而生成安全策略文件失败。

下面我们来看上述allow语句中所涉及到的objectclasspermset

1) ObjectclassPermSet

Objectclass代表对象类别,类似面向对象编程中的类定义。这里列举了一些常见的Objectclass,见下面的SEPolicy示例:

[external/sepolicy/security_classes示例]

.......

#此文件定义了Android平台中支持的Objectclass

#根据SELinux规范,ObjectClass类型由class关键字申明

#file-related classes

classfilesystem

classfile #代表普通文件

classdir   #代表目录

classfd    #代表文件描述符

classlnk_file  #代表链接文件

classchr_file  #代表字符设备文件

 ......

 

#network-related classes

classsocket   #socket

classtcp_socket

classudp_socket

......

classbinder  #Android平台特有的binder

classzygote  #Android平台特有的zygote

 

#Android平台特有的属性服务。注意其后的userspace这个词

classproperty_service# userspace和用户空间中的SELinux权限检查有关,下文再解释

上述示例展示了SEAndroidObjectClass的定义,其中:

  • Object Class需要通过class语句申明。这些申明一般放在一个叫security_class的文件中。

  • 另外,这些classkernel中相关模块紧密结合。

据说:在kernel编译时会根据security_class文件生成对应的头文件。从这里可以看出,SELinux需要根据发行平台来做相应修改。同时可以看出,该文件一般也不需要我们去修改。

再来看PermsetPermset指得是某种Objectclass所拥有的操作。以file这种Objectclass而言,其拥有的Permset就包括readwriteopencreateexecute等。

Objectclass一样,SELinuxSEAndroid所支持的Permset也需要声明,来看下面的例子:

[external/sepolicy/access_vectors]

#SELinux规范中,定义permset有两种方式,一种是使用下面的common命令

#其格式为:commoncommon_name { permission_name ... }common定义的permset

#被另外一种permset命令class所继承

#以下是Android平台中,file对应的权限(permset)。其大部分权限读者能猜出是干什么的。

#有一些权限需要结合文后的参考文献来学习

commonfile {

     ioctlread write create getattr setattr lock relabelfrom relabelto

     appendunlink link rename execute swapon quotaon mounton }

 

#除了common外,还有一种class命令也可定义permset,如下面的例子:

#class命令的完整格式是:

#classclass_name [ inherits common_name ] { permission_name ... }

#inherits表示继承了某个common定义的权限 注意,class命令定义的权限其实针对得就是

#某个objectclass。它不能被其他class继承

classdir inherits file{

  add_name remove_name reparent search rmdir open audit_access execmod

}

#来看SEAndroid中的binderproperty_service这两个Objectclass定义了哪些操作权限

classbinder{

     impersonate call set_context_mgr transfer }

classproperty_service{set }

提示:ObjectclassPermset的具体内容(SELinux中其实叫AccessVector)都和Linux系统/Android系统密切相关。所以,从知识链的角度来看,Linux编程基础很重要。

2) typeattributeallow

现在再来看type的定义,和type相关的命令主要有三个,如下面的例子所示:

[external/sepolicy相关文件]

#type命令的完整格式为:typetype_id [alias alias_id,] [attribute_id]

#其中,方括号中的内容为可选。alias指定了type的别名,可以指定多个别名。

#下面这个例子定义了一个名为shelltype,它和一个名为domain的属性(attribute)关联

typeshell, domain;#本例来自shell.te,注意,可以关联多个attribute

 

#属性由attribute关键字定义,如attributes文件中定义的SEAndroid使用的属性有:

attributedomain

attributefile_type

 

#可以在定义type的时候,直接将其和某个attribute关联,也可以单独通过

#typeattribue将某个type和某个或多个attribute关联起来,如下面这个例子

#将前面定义的system类型和mlstrustedsubject属性关联了起来

typeattributesystem mlstrustedsubject

特别注意:对初学者而言,attributetype的关系最难理解,因为“attribute”这个关键词实在是没取好名字,很容易产生误解:

  • 实际上,typeattribute位于同一个命名空间,即不能用type命令和attribute命令定义相同名字的东西。

  • 其实,attribute真正的意思应该是类似type(或domaingroup这样的概念。比如,将type Aattribute B关联起来,就是说type A属于group B中的一员。

使用attribute有什么好处呢?一般而言,系统会定义数十或数百个Type,每个Type都需要通过allow语句来设置相应的权限,这样我们的安全策略文件编起来就会非常麻烦。有了attribute之后呢,我们可以将这些Type与某个attribute关联起来,然后用一个allow语句,直接将source_type设置为这个attribute就可以了:

  • 这也正是typeattribute位于同一命名空间的原因。

  • 这种做法实际上只是减轻了TE文件编写者的烦恼,安全策略文件在编译时会将attribute拓展为其包含的type。如例子4所示:

[例子4]

#定义两个type,分别是A_tB_t,它们都管理到attribute_test

typeA_t attribute_test;

typeB_t attribute_test;

 

#写一个allow语句,直接针对attribute_test

allowattribute_test C_t:file {read write};

#上面这个allow语句在编译后的安全策略文件中会被如下两条语句替代:

allowA_t C_t:file {read write};

allowB_t C_t:file {read write};

前面讲过,TE的完整格式为:

rule_namesource_type target_type : class perm_set

所以,attribute可以出现在source_type中,也可以出现在target_type中。

提示:一般而言,定义type的时候,都会在名字后添加一个_t后缀,例如typesystem_t。而定义attribute的时候不会添加任何后缀。但是Android平台没使用这个约定俗成的做法。不过没关系,SEAndroid中定义的attribute都在external/sepolicy/attribute这个文件中,如果分不清是type还是attribute,则可以查看这个文件中定义了哪些attribute

最后我们来看看TE中的rule_name,一共有四种:

  • allow:赋予某项权限。

  • allowauditaudit含义就是记录某项操作。默认情况下是SELinux只记录那些权限检查失败的操作。allowaudit则使得权限检查成功的操作也被记录。注意,allowaudit只是允许记录,它和赋予权限没关系。赋予权限必须且只能使用allow语句。

  • dontaudit:对那些权限检查失败的操作不做记录。

  • neverallow:前面讲过,用来检查安全策略文件中是否有违反该项规则的allow语句。如例子5所示:

[例子5]

#来自external/sepolicy/netd.te文件

#永远不允许netd域中的进程读写 dev_type类型的块设备文件(Objectclassblk_file

neverallownetd dev_type:blk_file { read write }

最后,总结说明一下:

a.external/sepolicy/attributes -> 所有定义的attributes都在这个文件
b.external/sepolicy/access_vectors ->
对应了每一个class可以被允许执行的命令
c.external/sepolicy/roles ->
Android中只定义了一个role,名字就是r,将rattributedomain相关联
d.external/sepolicy/users ->
其实是将userroles进行了关联,设置了user的安全级别,s0为最低级是默认的级别,mls_systemHigh是最高的级别
e.external/sepolicy/security_classes ->
指的是上文命令中的class,个人认为这个class的内容是指在android运行过程中,程序或者系统可能用到的操作的模块
f.external/sepolicy/te_macros ->
系统定义的宏全在te_macros文件
g.external/sepolicy/***.te ->
一些配置的文件,包含了各种运行的规则

三、SELinux机制在项目开发中调试

在开发LG需求如通话时长累加等功能时,在向文件系统写入文件时,发现不生效。通过如下步骤来解决:

1通过如下命令来判定是否为权限受限。

首先,getenforce; //获取当前seLinux状态

终端显示:Enforcing,则表示SELinux机制打开。

或者,sestatus

终端显示如下:

SELinux status:                 enabled 
SELinuxfs mount:                /selinux 
Current mode:                   enforcing //表示SELinux机制打开
Mode from config file:          enforcing 
Policy version:                 24 
Policy from config file:        targeted 

selinux有两种工作模式:
“permissive”
:所有操作都被允许(即没有MAC),但如有违反权限的话,会记录日志;
“enforcing”
:所有操作都会进行权限检查;


然后、setenforce0; //关闭seLinux

复测一下功能,调试生效


再次、setenforce1;   //打开seLinux

再复测一下功能,调试生效,则证明由SELinux机制受限导致的。


二、搜索如下kernellog,可明确判定是否由权限引起。
cat/proc/kmsg | grep ‘avc:denied’dmesg| grep ‘avc:denied’

可见到类似如下的相关log

<36>[50830.234458] (1)[242:logd.auditd]type=1400 audit(1489558059.720:228): avc: denied{ read } for pid=4766comm="debuggerd" path="/dev/block/mmcblk0p1"dev="tmpfs" ino=3256DJ_08.15_A scontext=u:r:debuggerd:s0tcontext=u:object_r:nvram_device:s0 tclass=blk_filepermissive=0



分析过程: 
缺少什么权限:          {read}权限, 
谁缺少权限:              scontext=u:r:debuggerd:s0, 

对哪个文件缺少权限:tcontext=u:object_r:nvram_device:s0

什么类型的文件:        tclass=blk_file 


权限问题的log的核心部分可归纳为如下标志性格式:

avc:denied  { 操作权限 } for pid=xxxx comm=“进程名” scontext=u:r:源类型:s0tcontext=u:r:目标类型:s0 tclass=访问类型permissive=0

3、解决方法
谁缺少权限,就去谁对应的TE文件中去添加权限配置语句。
例如,上述权限问题的logdebuggerdnvram_deviceblk_file缺少read的权限,那就去
debuggerd.te中添加权限配置语句,如下:

allowdebuggerd nvram_device:blk_file read;

修改之后,重新编译并烧入boot.img。 

四、SELinux机制在项目开发中客制化

首先,按照Google的官方文档:需要linux内核首先是支持selinux的,且需要androidselinux的配置文件即extern/sepolicy里面的内容。然后就是修改BoardConfig.mk。首先会包含厂商定制的sepolicy的文件夹:BOARD_SEPOLICY_DIRS然后将规则添加到了sepolicy中:BOARD_SEPOLICY_DIRS这样,我们编译出来的image就有了selinux的功能。其实如果没有厂商定制的话,也是会编译到external/sepolicy的,这样,就是使用andriod所有默认的sepolicyMTK平台的sepolicy的支持位于device/mediatek/common/sepolicy

本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
【热】打开小程序,算一算2024你的财运
selinux 日志抓取和验证
深入理解SELinux SEAndroid(第一部分)
seandoird 简述
Android CTS中neverallow规则生成过程
android 8.1 安全机制 — SEAndroid & SELinux
SEAndroid安全机制中的进程安全上下文关联分析
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服