Linux安全机制之Selinux
一、引言1.1、什么是SelinuxSelinux是一种MAC(强制访问控制)安全机制,是对传统DAC(直接访问控制)机制的加强,而非替换,定义传统进程对文件对象的访问权限,如果不满足则无法执行,不管当前是什么用户。Selinux是一种MAC(强制访问控制)安全机制,是对传统DAC(直接访问控制)机制的加强,而非替换,定义传统进程对文件对象的访问权限,如果不满足则无法执行,不管当前是什么用户。本文
一、引言
1.1、什么是Selinux
Selinux是一种MAC(强制访问控制)安全机制,是对传统DAC(直接访问控制)机制的加强,而非替换,定义传统进程对文件对象的访问权限,如果不满足则无法执行,不管当前是什么用户。
本文下面讲的内容,有安卓源码的小伙伴可以到类似目录(android/device/softwinner/common/)去检索查看Selinux的相关操作。
1.2、为什么提出Selinux
传统的DAC机制缺点:对权限管理太粗矿,只要获取到root权限就能为所欲为;
Selinux的优点:可以把权限管控做得更加细致,即使有root权限也无能为力。
二、Selinux基本要素
2.1、安全上下文
2.1.1、概念
安全上下文就是描述文件或者进程的一种属性。
2.1.2、查看安全上下文的方法
进程上下文:ps -Z,关注进程的domain。
举例:“u:r:init:s0”,其中的init就是domain。
文件上下文:ls -Z,关注文件的type。
举例:“u:object_r:system_file:s0”,其中的system_file就是type。
2.1.3、安全上下文的定义位置
普通文件在file_contexts文件中定义;虚拟文件在genf_contexts文件中定义;属性文件在property_contexs文件中定义;应用配置在seapp_contexts文件中定义。
因为我是Bsp软件工程师,负责的是SOC的驱动部分,所以对于Selinux,上面四种文件改动得比较多的普通文件,所以这里就简单举一个普通文件的安全上下文是如何定义的。
上图中,总共为两个文件定义了安全上下文,可以看到,定义安全上下文的语法:文件位置 标签。
文件位置很好理解,就是这个文件是哪个地方的文件,这里要用绝对路径。
而什么是标签呢?可以看到2.1.2中举的两个例子,“u:r:init:s0”、“u:object_r:system_file:s0”,他们都是标签。一个是进程的标签,一个是文件的标签。而其中的init是进程的domain,system_file是文件的type,init、system_file也就是我们所说的安全上下文。
Selinux 依靠标签来匹配操作和政策(所谓操作和政策,就是在Selinux,允许某个进程操作某个文件,如果未授权,就算是root权限也不行)。
2.2、组
2.2.1、概念
这个组的概念,大家不需要理解的太复杂,其实就是表面意思,作用是将相同类型的文件或者进程集合到一个组里。
就像是Linux中的group,在同一群组下的用户或者文件,享有相同的权限。
使能Selinux,将Selinux编译进内核后,当我们启动开发板,Linux内核被加载进入内存,Selinux就开始运行起来了。Selinux会根据我们为一些文件和进程定义好的标签,给他们打上标签。而那些我们没有为它们定义标签的文件和进程,Selinux会根据自己的规则,为它们打上默认的标签。为了某些同类型的文件,比如/dev/tty这些串口设备文件,我们知道,一块Linux开发板,串口是有很多的,既然是串口,那么它们肯定就应该有着相同的权限,需要将他们归为一个小组。加入我们把它们归为serial_file组,那么就应该把它们的安全上下文,也就是文件的type设置为serial_file。
现在,大家就应该理解什么是组的概念了。
2.2.2、关联类型和属性
从2.2.1我们知道了,像/dev/tty这种串口文件,是属于serial_file这种类型的,那么它们的标签就应该是这样“u:object_r:serial_file:s0”。
说到这里,问题也就来了。在Selinux中,其实是没有serial_file这种类型的,那我们又想用它怎么办呢?
这时候,我们就需要用到关联类型和属性这个概念了。
经过之前的解释,其实大家都知道,所谓类型就是进程和文件的安全上下文,而前面说过,安全上下文就是描述进程和文件的属性。安全上下文这里涉及到的属性呢,可以自己定义,也可以用Selinux定义好的。而关联类型和属性那里的属性呢?则是Selinux自己的标准属性,不需要,也不能让用户来自己定义。
还是以/dev/tty为例。在Selinux中,对/dev下的文件,安全上下文默认设置的都是device这个type。这个时候你想要将这些串口设备文件都放在serial_file这一组,让他们与其他设备文件区分开来,而Selinux中又没有这种类型,那么就需要你将它给定义出来并将它与device这个Selinux自带的属性关联起来了。
自定义类型且关联属性,必须在te文件中,命令:“type type_id, [attribute_id]”,示例:“type serial_file, device;”。
既然可以将自定义类型与属性关联起来,那么非自定义的类型自然也可以和属性关联。比如,你想要让一个文件或者进程拥有多个属性时,就可以将它的安全上下文和一些属性关联起来。命令:“typeattribute type_id atrribute_id”。
2.3、transition(切换)
2.3.1、为什么会有这个
背景:在linux中,子进程会默认继承父进程的属性,子目录会默认继承父目录的属性。
问题:基于以上事实,在某些场景直接继承父对象的属性明显是不合适的,容易造成权限过大、不安全。
为了让新起的进程或新建的文件,归属到它应有的那个组或者type,避免子对象继承父对象的属性而造成的不安全,所以就有了切换这种概念。
注意:系统关键进程或长时间运行的进程,必须进行domain切换,比如netd、installd、vold、zygote等。
2.3.2、如何切换
我们知道,安卓系统所有的进程都是由init派生而来的,所以为了让这些子进程不继承init的权限,就需要让他们进行domain切换,domain切换的意思也就是让他们切换到自己的组,这样它们就会使用自己的权限,而非init进程的权限。示例看下图:
可以看到,图中的vecentekseservice是自定义出来的,并且还和domain这个Selinux自己的标准属性关联起来,然后又用init_daemon_domain这个宏函数,将vecentekseservice这个进程从init的子进程的权限切换到了自己的domain。看到这里,可能大家会有疑惑,这个vecentekseservice不是一个自定义的类型嘛?怎么你又在说进程?其实,上图中的代码,都是在vecentekseservice.te这个文件中的。而在Selinux中,定义相关进程的类型和规则,就是在以它的进程名命名的te文件中进行。
2.4、sepolicy
作用:制定规则,确认进程对文件的访问权限,同样在以它的进程名命名的te文件中定义。
2.4.1、分类
AOSP(安卓开源项目)所定义的通用规则:
- 所在路径为android/system/sepolicy/。里面的public为公共所有,而private对SOC/OEM不可见。
- 警告:不要试图修改它,否则会违反Goole CTS测试,连编译都通不过。
SOC/OEM所定义的规则目录:
- 在android/device/softwinner/common/sepolicy/vendor。
2.4.2、该怎么定义sepolicy规则
- 语法
模板:rule_name source_type target_type:class per_set。
rule,规则,allow, neverallow, allowaudit(record), dontaudit(don't record);
source_type,进程的组,也就是进程的domain(可以通过ps -Z查看);
target_type进程所要操作的文件的type(可以通过ls -Z查看);
class为target_type的类型,system/sepolicy/private/security_classes中有class的定义,常见的有file,dir等;
per_set,定义的权限,常见的有read , write, ioctrol, create, getattr, getattr等。
- 练习
假如有这么一条报错信息:“avc: denied { call } for pid=2162 comm="AudioOut_D" scontext=u:r:audioserver:s0 tcontext=u:r:hal_audiocontrol_default:s0 tclass=binder permissive=1”。
安卓系统打开了Selinux之后,遇到权限报错,就是上面这样的格式。
我们先来按照语法,来拆分一下上面的log信息。
rule:allow;
source_type:audioserver;
target_type:hal_audiocontrol_default;
class:binder;
per_set:call。
所以,将它们组合起来,一条完整的规则应该是:“allow audioserver hal_audiocontrol_default:binder call”。
然后我们通过ps -Z找到“audioserver”所属的进程,将这条规则添加到以它进程名命名的te文件中去。
三、sepolicy实战
3.1、两种工作模式
Enforcing:强制模式,遇到权限问题立即返回无法继续执行;
Permissive:宽容模式,遇到权限问题,允许继续执行,并将错误信息打印出来,一般用于调试。
3.2、sepolicy的启动和关闭
3.2.1、编译阶段
在编译阶段启动或者关闭sepolicy,属于静态配置。
要想在编译阶段启动sepolicy,需要在config文件中这么配置:“CONFIG_SECURITY_SELINUX=y”;并且在BoardConfig.mk文件中配置:“BOARD_KERNEL_CMDLINE := androidboot.selinux=permissive”。
3.2.2、启动过程中
在启动过程中启动或者关闭sepolicy,属于动态配置。
要想在这个时候启动或者关闭sepolicy,如果不想重新编译,可以直接修改cmdline。方法:fastboot boot.img -c androidboot.selinux=permissive。
3.2.3、启动完成后
在启动完成后启用或者关闭sepolicy,也属于动态配置。
setenforce [Enforcing|Permissive|1|0]设置模式。
getenforce:查看模式。
注意:此方法会在重启后失效。
3.3、如何判断是否是selinux引起的问题
设置为Permissive模式,如果问题依旧存在则说明是其他问题引起的,如果问题不存在,那就说明是selinux引起的。
3.4、如何抓取selinux的log
如果是安卓系统,我这里一般是使用“logcat | grep avc”。
3.5、如何修改
Selinux导致的问题,十有八九是权限问题,所以,只要你将权限被限制的文件或者进程的权限解开即可。这里,有两种组合规则的方式:
自动:audit2allow -i deny.log。
手动:根据错误日志拆分关键字,然后组合成规则。
这里建议手动解决好一些。
3.5.1、常见问题的处理原则
不要试图修改谷歌定义的规则;
如果遇到跟goole规则冲突的地方,只能选择绕道或者修改设计。
3.5.2、常见问题的问题类型
- 权限被拒绝,解决方法:补齐该权限。
- 跟AOSP的规则冲突,解决方法:修改进程的domain、修改文件的type、修改设计。
3.5.3、常见问题之新增设备
为新增设备定义具体的标签类型;
为进程添加访问该文件的权限。
3.5.4、新增进程或服务
新建te文件,以进程或服务名字来命名,并将te文件加入编译系统;定义新的domain/type,若有必要需添加transition,增加selinux规则;新建安全上下文,将文件打上标签。
更多推荐
所有评论(0)