目录
1概述
2 DAC机制
2.2 权限
3 seAndroid/seLinux安全机制
3.1 安全上下文(SContext)
3.2 基本规则
3.3实例
4 总结
最近在android的开发过程中遇到一些权限问题。借此机会做个学习总结。
简单的来说Android/Linux的权限分为两大类,一个是seLinux的安全机制,是一种MAC机制(Mandatory Access Control强制访问控制),通俗点理解就是你在门卫处需要预留指纹和规定的访问权限,在访问时指纹和访问的全限必须匹配才能够访问指定资源;还有一种基于进程序所属的用户以及用户组,是一种DAC机制(Discretionary Access Control:自主访问控制),通俗的说就是你需要一个工作牌证明自己可以进入访问资源。第一种是第二种的加强版。在android/linux中目前两种权限机制同时工作。
DAC是传统的Linux的访问控制方式,DAC可以对文件、文件夹、共享资源等进行访问控制。在DAC这种模型中,文件客体的所有者负责管理访问控制。 DAC使用了ACL(Access Control List,访问控制列表)来给普通的用户提供不同的权限,而root用户对文件系统有完全自由的控制权。
2.1 用户/组
首先linux是一个多用户系统,什么是多用户系统?
就是一个系统同时能够多个用户登录,在各自的用户空间进行操作,而相互不干扰。不同的用户有不同的角色,功能,当然还有不同的权限;而他们是通过UID来识别。UID在系统中有唯一性,不能共用。linux将用户分为管理员(root)和普通用户,普通用户又分为系统用户和自定义用户。
用户(user)
如上面描述,user可以简单的理解为系统的使用者,每个使用者都有自己的名称,通过UID区分不同的user,user不同权限不同,root对其它user管理的权限,而普通user只能够管理自己的资源。
组(group)
group可以认为是具有相同属性的user的集合。比如几个用户在同一个系统上开发项目,将其编到统一group中就可以相互访问共享资源,而且可统一修改同一个group中的文件或其它资源的访问权限,便于管理,每个group有对应的GID。一个group可以包括多个user,一个user可以从属于不同的group。
对于用户可以再在/etc/passwd中可以查看相关配置:
格式为: 注册名:口令:用户标识(UID):组标识(GID):注释:用户目录:用户登录的shell类型。
上图中root为管理员UID是0,具有绝对的权限,其它为普通用户。其中jimmy为自定义用户UID为1000,而向上的其它user为系统用户UID在1-500之间。
在linux中的每个用户必须属于一个组,不能独立于组外。在linux中每个文件有所有者、所在组、其它组的概念。同样用户组的信息我们可以在/etc/group中查看:
格式为:组名:组密码:组标识号(GID):附加组成员
为什么需要介绍user和group,原因是linux的DAC机制就是基于它们。
便于理解我们先举个示例,使用ls –al显示目录中文件信息:
先解释一下信息含义:
第一组的第一个表示文件类型:d 目录;- 文件 ;l 链接 ;c 字符设备
后面就是改文件的权限:r读 w 写 x可执行,三组对应的是所有者/所有者组别/其它
rw-rw-r--表示所有者有读写权限,和所有者同组的有读写权限,其它只能读取。
第二组是文件个数
第三组是user 即文件的所有者
第四组是group 即user所属的组
你可以看到每个文件都有自己的user以及group属性。根据前文解释每个文件为所有者user,user所在得组,和其它设置了权限。如assert.c 对于user:jimmy开放了读写权限,和user同组的也有读写权限,其它则是只有只读权限。
DAC就是根据访问主体的user去对比访问客体资源的权限属性,对比信息来判别主体对资源客体是否相应的操作权限。
linux还为我们提供了一些列的指令来配置user、group,以及修改文件等资源所属的user,权限等级等。
user |
|
useradd |
添加用户 |
userdel |
删除用户 |
passwd |
为用户设置密码 |
usermod |
修改用户命令,可以通过usermod 来修改登录名、用户的家目录等等 |
pwcov |
同步用户从/etc/passwd 到/etc/shadow |
pwunconv |
是pwcov 的立逆向操作,是从/etc/shadow和 /etc/passwd 创建/etc/passwd ,然后会删除 /etc/shadow 文件 |
group |
|
groupadd |
添加用户组 |
groupdel |
删除用户组 |
groupmod |
修改用户组信息 |
groups |
显示用户所属的用户组 |
grpck grpconv |
通过/etc/group和/etc/gshadow 的文件内容来同步或创建/etc/gshadow ,如果/etc/gshadow 不存在则创建; |
grpunconv |
通过/etc/group 和/etc/gshadow 文件内容来同步或创建/etc/group ,然后删除gshadow文件; |
还有chgrp/chown/chmod几个关键的操作指令:
chgrp [-R] 用户组名 文件名
目的是将文件所属的组,修改为组名指向的组。
chown [-R] 用户[:用户组] 文件名
目的是将 文件的所属的用户以及组修改为指定的。
chmod [-R] 权限 文件名
目的是修改文件权限为指定权限。其中权限有特别的指定方法,记得前面我们说过文件的权限分为读、写、执行,对应r、w、x,而又分为3组对应user、group、other,为了便于修改rwx分别对应一个bit,如果只读就是100,只写就是010,可执行是001,如果可读可写就是110,所以修改文件user可读可写,group可读,other可读就是:
chmod 644 file_name
6:110 rwx 4:100 rwx
当然chmod还有很多组合方式这里不一一列出。
2.3示例
说了这么多不如实践一下,我们创建一个示例代码,在代码中是打开一个文件:
我们使用gcc 编译生成test可执行文件:
创建一个test_file.txt文件:
可以看到文件的所有者是jimmy,我们执行test:
成功打开。
修改文件权限为000,则打开失败。下面我们将文件权限修改还原,增加user然后修改文件user:
可以看到我们已经修改了文件的user和group,然后执行test:
可以看出我们的进程已经没有权限open改文件了。为什么呢?因为我实在jimmy这个用户下执行test,所以test进程user是jimmy,没有权限打开test_user的文件。
我们切换user到test_user在执行:
可以看到成功执行。
seLinux 是一种MAC安全机制(Mandatory Access Control,强制访问控制)。SELinux在内核中使用MAC检查操作是否允许;系统管理员管理负责访问控制,用户不能直接改变强制访问控制属性;可以定义所有的进程(称为主体)对系统的其他部分(文件、设备、socket、端口和其它进程等,称为客体)进行操作的权限或许可。
和DAC模式不同,seLinux不是基于用户、组来设置权限。而是基于主体与客体,利用安全上下文(SContext)来限制,主体对客体的访问权限。seLinux对2种类型做限制,一个是“死物”即文件类型,还有一种就是“活物”即进程。下面我们在手机查看一下文件和进程的上下文:
文件
进程
如图,对于文件SContext如u:object_r:vendor_configs_file:s0。
1)u: user,指用户,注意该用户非DAC中的用户,而是在MAC中定义的。目前android只定义了“u”这一个user。
2)object_r: 表示role 角色,android目前只定义了一个role即“r”。一个user可以属于多个role,每个role有不同的权限。
3)vendor_configs: 表示type,类,MAC的管理都是基于type来限制的。
4)s0: MLS级别,即安全等级,目前android都是s0
对于进程u:r:kernel:s0和上述类似,u指user,r指role,kernel是type,s0为安全等级。seLinux规范中完整的SContext是:user:role:type[:level]。在这里面需要着重关注的就是type,因为在android中目前user只有一个u,role只有一个r,s等级目前也都是s0。
那么user和role是如何定义的呢?
在android系统中system/sepolicy/public/role_decl中定义了一个role:
role r;
role r types domain; //将r与domain关联
在system/sepolicy/public/users文件中定义了user:
user u roles { r } level s0 range s0 – mls_systemhigh;
此处声明了一个user u,可用的role为 r,默认安全等级为s0,安全范围是s0 到系统定义的最高级别(mls_systemhigh)。
基于上面的声明,如果没有其它的user和role,那么在android中所有的SContext必须基于u、r、domain(type)、s0 – mls_systemhigh定义才是合法的。看一下系统是如何为资源声明SContext的:
其中的rootfs、init_exec等都是属于domain的type。
按照seLinux规范,安全权限设定语法的基本格式如下:
rule_name source_type target_type : class permissive
rule_name:指令如allow、neverallow等
source_type: 主体,通常为属性domain的type
target_type:客体,主体需要访问的type,如fs_type、audio_device、dev_type等
class :访问客体的客体类别
perm:主体对客体具备的权限
以allow为例:
allow audioserver audio_device : chr_file { read write }
audioserver: 主体进程
audio_device:访问客体
chr_file:访问客体类别
read write :权限
描述的意思就是允许进程audioserver对客体audio_device中的chr_file类别进行read/write操作。
1) permissive
在/system/sepolicy/private/access_vectors定义了各种class类别资源的权限,以chr_file为例:
定义class chr_file 并继承file的操作,在继承的基础上还添加了几个操作权限,file的权限如下:
所以chr_file包括了如上的所有操作权限,这样allow就可以设置这些权限;这些权限是系统定义好的,与kernel /security/selinux/include/classmap.h文件中形成映射,所以轻易不能修改。
2) class
在/system/sepolicy/private/security_class文件中,定义了各种class:
这些class也与与kernel /security/selinux/include/classmap.h文件中的定义有相关映射,所以轻易也不能修改。
3) type和attribute
定义一个type的完整命令:
type type_id [alias alias_id,] [attribute_id]
type_id:type名称
alias:指定了type的别名
alias_id: type的别名
attribute_id:属性名称
如:
type init , domain;
type sysfs , fs_type;
其中domain/ fs_type 是type的属性,可以理解为具有相同功能的type的集合,这些属性是约定好的,在/system/sepolicy/public/attribute文件中我们可以查看到相关的定义以及功能描述:
可以看到dev_type是用于devices的,fs_type则是filesystems。attribute的目的是什么?一般在系统中会定义几十或者上百个type,如果对type一个一个的进行修改权限,会很麻烦,但是将不同的type归类通过attribute可以以妻子不该attribute中包含的type的权限。
定义两个type,分别是A和B,它们都管理到attr_test
type A attr_test;
type B attr_test;
使用allow语句,直接针对attr_test
allow attr_test C:file {read write};
上面这个allow语句在编译后的安全策略文件中会被如下两条语句替代:
allow A C:file {read write};
allow B C:file {read write};
attribute可以出现在source_type中,也可以出现在target_type中。
4) Labeling
前面我们说过安全上下文,也简单说明了SContext的设置,SELinux中将设置或分配SContext给进程或文件的工作叫Security Labeling,简单点说叫打标签。一般在叫*_context文件中限制。
LSM初始化时所需要的信息以及SContext信息保存在两个特殊的文件中:initial_sids和initial_sid_context文件。
initial_sids:
定义了LSM初始化时相关的信息。SID是SELinux中一个概念,全称是Security Identifier。SID其实类似SContext的key值。因为在实际运行时,去比较字符串(还u:r:kernel:s0)会严重影响效率。所以SELinux会用SID来匹配每个SContex设定SID(key值),如下是定义SID:
initial_sid_context:
该文件为这些SContext设置SID信息,就是将SID与SContext绑定。
最后就是在*_context文件中设定SContext
(device/qcom/sepolicy/vendor/msm8953/file_contexts):
遇到权限问题,首先需要确认是否是权限导致的,一般Android手机如果有root权限,可以暂时关闭SeLinux模块,确认执行是否正常。如果关闭SeLinux后执行正常说明的确是权限问题,关闭、开启以及获取SeLinux状态指令如下:
setenforce 0 ##设置SELinux 成为permissive模式
setenforce 1 ##设置SELinux 成为enforcing模式 (SELinux开启)
getenforce ##获取SELinux状态(permissive,enforcing,disabled)
确认是权限问题后,开启selinux 系统模块,使用 cat /proc/kmsg |grep avc 或者logcat –v time |grep avc 查看关于权限的log,比如如下log:
type=1400 audit(32.939:25): avc: denied { open } for pid=2592 comm="chmod" path="/dev/block/mmcblk0p25" dev="tmpfs" ino=6494 scontext=u:r:init_shell:s0 tcontext=u:object_r:block_device:s0 tclass=blk_file permissive=1
根据log,按照录下方法,寻找关键字,在对应得te文件中添加权限:
scontext |
tcontext |
tclass |
permissive |
u:r:init_shell:s0 |
u:object_r:block_device:s0 |
blk_file |
denied{ open } |
你就需要在 init_shell相应的te文件中添加如下设定
allow init_shell block_device:blk_ file { open };
这条语句表示允许init_shell对block_device 中的 blk_file类别的文件进行open操作。
不过在添加property_set权限是发现按照上述添加方式,会出现与原有neverallow规则冲突的地方,如果去除neverallow 规则又可能导致安全问题后期可能出现CTS测试不过问题,像华为还有自己的安全测试软件用于测试文件权限修该也可能导致测试不过,这个时候可以根据原有的属性关键字查找对应属性添加权限方式添加。
以MTK平台添加MEC算法时遇到的权限问题为例:
1)在device/metiake/sepolicy/下查找系统音频相关属性关键字如
一般在property_context文件中:
可以看到af. u:object_r:audio_prop:s0 MTK 系统有类似af.mixer.pcm 以af. 开头的属性。
有两种操作方式,一个就是就是将添加的属性修改为af开头,如af.awinic.suppot 在测试是否可以正常使用property_set。
另一个就是添加自己客制化的:
在property_context添加awinic. u:object_r:audio_prop:s0
在property.te添加 type audio_prop property_type,core_property_type;
在对应的mediaserver.te添加allow mediaserver audio_prop:property_service set;
编译烧录;
一般使用第一种方案,这样不需要添加额外的权限,也可以防止权限添加不当等问题。
android/linux有2中权限机制,一种DAC基于用户、组机制,不同的用户、组对不同的资源有不同的权限。可以说是按照类别区分,一旦你具有和资源用户同等级别,你就可以访问资源。这种机制有明显的缺陷,比如root进程衍生的子进程就完全继承了父进程的权限,可以访问所有资源,给黑客们留下了攻破系统的致命后门。
为了系统的安全,第二种基于MAC机制的seLinux被引入,该机制能够对所有的资源对用不同的访问者设定不同的权限,由管理员监看权限,而不是用户管理。该机制基于user和role,注意user非第一种user的含义。对不同的资源设定SContext,主体访问客体的资源时通过SContext确定是否有权限。
两种机制共同存在,组成Android/linux安全系统。
参考文献:
https://www.jianshu.com/p/ba7c4e8cd699
https://www.jianshu.com/p/14a54a49e007?from=timeline&isappinstalled=0