目录
一、基本概念
1、工作模式
1)、关闭模式
2)、宽容模式
3)、强制模式
2、进程和资源
3、安全属性
1)、进程SContext
2)、资源SContext
4、TE术语
5、allow语句
6、调试
7、基本原理
二、策略文件
1、sepolicy/vendor/service.te
2、sepolicy/system/private/service_contexts
3、sepolicy/vendor/shen.te
4、sepolicy/vendor/file_contexts
三、配置新进程的SEAndroid
1、配置进程shen的安全策略
1)、domain
2)、exec_type
3)、init_domain_domain
2、赋予二进制文件SContext
3、赋予binder通信功能
四、问题思路
1、如何定位Selinux权限问题?
2、违反neverallow规则解决思路
1)访问 default property 超过权限
2)为新服务添加标签并解决拒绝事件
3)新设备节点增加访问权限
SELinux原本是美国国安局联合一些公司设计的一个针对Linux的安全加强系统。
SELinux出现之前,Linux系统上的安全模型叫做DAC(自主访问控制),其原理是进程所拥有的权限与执行它的用户的权限相同(例如:以root用户启动Browser,那么Browser就有root用户的权限,在Linux系统上能干任何事情)。SELinux的出现结束了这种宽松的访问。
SELinux在DAC的基础之上,设计了新的安全模型叫做MAC(强制访问控制),其原理是任何进程想在SELinux系统中干任何事情,都必须先在安全策略配置文件中赋予权限,凡是没有在安全策略配置文件配置的权限,进程就没有该权限。Android4.4版本上正式推出的一套以SELinux为基础于核心的系统安全机制,且命名为SEAndroid,自此SELinux便被"移植"到了Android上了。
参考:Selinux小结
SELinux通常有如下三种模式:disable、permissive、enforcing。通常发布版都将使用强制模式Enforcing。
可以通过getenfoce来获取当前工作模式,还可以通过setenforce 0来设置当前模式为Permissive;也可以通过setenforce 1来设置当前模式为Enforcing。除此之外还可以进入fastboot模式设置:
#设置参数bootargs
$setenv bootargs androidboot.selinux = permissive/enforcing
#保存参数
$saveenv
#重启系统
$reset
如果SELinux 被关闭(开启Disable模式),就使用传统的Linux系统默认的自主访问控制(DAC)方式,不需要增强安全性的环境来说,该模式是很好用的(换句话禁止SELinux功能)。
编辑配置文件/etc/selinux/config,把SELINUX=disabled,然后重启系统,SELinux 就被禁用了。
还可以通过修改源代码的方式来关闭SELINUX,再Android S修改如下代码
BuildServer108:~/works/v501_driver/android/system$ git diff
diff --git a/core/init/selinux.cpp b/core/init/selinux.cpp
index 1d0daed8..8e6e5c4d 100644
--- a/core/init/selinux.cpp
+++ b/core/init/selinux.cpp
@@ -114,10 +114,11 @@ EnforcingStatus StatusFromProperty() {
}
bool IsEnforcing() {
- if (ALLOW_PERMISSIVE_SELINUX) {
- return StatusFromProperty() == SELINUX_ENFORCING;
- }
- return true;
+ //if (ALLOW_PERMISSIVE_SELINUX) {
+ // return StatusFromProperty() == SELINUX_ENFORCING;
+ //}
+ //return true;
+ return false;
}
在 Permissive 模式中,安全策略规则并没有被强制执行。当安全策略规则应该拒绝访问时,访问仍然被允许。然而,此时会向日志文件发送一条消息,表示该访问应该被拒绝(换句话使能SELinux功能,但是SELinux不会拒绝方法但会打印日志)。
SELinux Permissive 模式主要用于以下几种情况:审核当前的 SELinux 策略规则;测试新应用程序,看看将 SELinux 策略规则应用到这些程序时会有什么效果;解决某一特定服务或应用程序在 SELinux 下不再正常工作的故障。
总结:仅记录但不强制执行 SELinux 安全政策。
Enforcing 模式, SELinux 被启动,并强制执行所有的安全策略规则(换句话使能SELinux,并强制按照SElinux的规则来进行权限访问)。
总结:强制执行并记录安全政策。如果失败,则显示为 EPERM 错误。
有人比喻Linux中有两种东西:死的(Inactive)、活的(Active)。活的东西就是进程,而死的东西就是资源(普通文件、特殊文件、套接字等)。他们之间就是一种使用(操作)与被使用(被操纵)的关系:进程能发起动作(例如它能打开文件并操作它);而文件只能被进程操作。
SElinux Policy语言将死的和活的东西都给打上"标签",通过"标签"将系统内的资源(包括进程)分成不同的角色(比如:用户、客体),进而对整个系统资(包括进程)进行合理安全的管控。
在SELinux世界里,每种东西都会被赋予一个安全属性(官方说法叫Security Context,后文简称SContext),也可以理解为第一小节提到的标签。
Linux中的所有东西,包括活的进程、死的资源都有自己对应的SContext安全属性,因此可以把SContext分为进程SContext和资源SContext,无论哪种安全属性,他们的本质都是一个字符串,其格式如下:
user:role:type[:range]
#user表示用户:SEAndroid中仅定义了一个SELinux用户,用u表示
#role表示角色:可以暂时理解为在SELinux用户可以有多个不同的role,不同role所具有的权限也不一样
#type表示类型:不同的type所具有的权限也不一样,但跟role有本质的不同
#range表示安全级别:通常为s0,不用怎么关注
HWBLN-H:/ $ ps -Z -A
LABEL USER PID PPID VSZ RSS WCHAN ADDR S NAME
u:r:init:s0 root 1 0 22180 1404 0 0 S init
u:r:kernel:s0 root 2 0 0 0 0 0 S [kthreadd]
u:r:kernel:s0 root 3 2 0 0 0 0 S [ksoftirqd/0]
u:r:kernel:s0 root 5 2 0 0 0 0 S [kworker/0:0H]
u:r:logd:s0 logd 405 1 20132 4168 0 0 S logd
u:r:servicemanager:s0 system 406 1 11320 808 0 0 S servicemanager
u:r:hwservicemanager:s0 system 407 1 14064 1532 0 0 S hwservicemanager
u:r:vndservicemanager:s0 system 408 1 10116 280 0 0 S vndservicemanager
u:r:hal_health_default:s0 system 473 1 12964 720 0 0 S [email protected]
u:r:hal_memtrack_default:s0 system 474 1 12988 772 0 0 S [email protected]
u:r:hal_thermal_default:s0 system 475 1 13100 520 0 0 S [email protected]
u:r:hal_usb_default:s0 root 476 1 13044 556 0 0 S [email protected]
u:r:displayeffect:s0 system 477 1 13128 516 0 0 S [email protected]
u:r:hal_audio_default:s0 audioserver 478 1 50088 2356 0 0 S [email protected]
u:r:mediacommservice:s0 system 479 1 13140 532 0 0 S [email protected]
u:r:hi110x_daemon:s0 system 480 1 16352 808 0 0 S [email protected]
u:r:untrusted_app:s0:c512,c768 u0_a118 21453 684 4176436 310404 0 0 S com.tencent.mm
u:r:untrusted_app:s0:c512,c768 u0_a118 21473 684 3089252 57528 0 0 S com.tencent.mm:push
u:r:untrusted_app:s0:c512,c768 u0_a118 21721 684 7797824 87584 0 0 S com.tencent.mm:appbrand0
u:r:untrusted_app:s0:c512,c768 u0_a118 21737 684 7432960 42216 0 0 S com.tencent.mm:appbrand1
输入命令ps -Z -A能够查询当前所有进程的安全属性,最右边列表示进程名称,最左边列表示该进程对应的安全属性。大多数的进程的SContext的user值为u表示SEAndroid用户;role值为r表示进程;range值为s0。
值得注意的是它们的type不一样,init进程的type值为init,进程logd的type值为logd,除此之外还有些进程的type是相同的,例如腾讯的几个进程的type都是untrusted_app。进程SContext的type字段代表该进程所属的Domain(域名),个人理解domain就跟tcp协议栈里面的域名一样,它对应于一类进程,后文将要讲解的安全策略就会应用于该类的所有进程,即相同type的进程具有相同的访问权限。
#查看根目录的安全属性
1|HWBLN-H:/ $ ls -l -Z
drwxrwx--- 7 system cache u:object_r:cache_file:s0 4096 2020-04-30 14:34 cache
drwxrwx--x 57 system system u:object_r:system_data_file:s0 4096 2020-07-27 09:00 data
lrwxrwxrwx 1 root root u:object_r:rootfs:s0 23 1970-01-01 08:00 default.prop -> system/etc/prop.default
drwxr-xr-x 22 root root u:object_r:device:s0 3920 2020-07-27 09:00 dev
lrwxrwxrwx 1 root root u:object_r:rootfs:s0 11 1970-01-01 08:00 etc -> /system/etc
lrwxrwxrwx 1 root root u:object_r:rootfs:s0 8 2020-07-27 09:00 log -> /splash2
drwxr-xr-x 10 root system u:object_r:tmpfs:s0 240 2020-07-27 09:02 mnt
drwxr-xr-x 8 root root u:object_r:system_file:s0 4096 1970-01-01 08:00 odm
drwxr-xr-x 2 root root u:object_r:rootfs:s0 40 1970-01-01 08:00 oem
drwxr-xr-x 2 root root u:object_r:rootfs:s0 80 1970-01-01 08:00 res
drwx------ 2 root root u:object_r:rootfs:s0 40 2020-01-14 11:08 root
drwxr-x--- 2 root root u:object_r:rootfs:s0 420 1970-01-01 08:00 sbin
drwxr-xr-x 5 root root u:object_r:storage_file:s0 140 2020-07-27 09:02 storage
dr-xr-xr-x 14 root root u:object_r:sysfs:s0 0 2020-07-27 09:00 sys
drwxr-xr-x 22 root root u:object_r:system_file:s0 4096 1970-01-01 08:00 system
drwxr-xr-x 9 root root u:object_r:vendor_file:s0 4096 1970-01-01 08:00 vendor
在Android系统根目录执行ls -Z命令,能够查看根目录文件的SContext。大多数资源的SContext的user值为u代表SEAndroid用户;role值为object_r代表文件;range值为s0。
值得注意的他们的type不一样,目录cache的type值为cache_file;目录root和res和etc的type值为rootfs;目录system的type值为system_file;跟进程SContext一样,有些资源的type相同,代表他们是同一类,后文结束的allow语句配置的安全策略都将应用同type的资源。
#/system/bin目录下的安全属性
1|HWBLN-H:/ $ ls /system/bin/ -Z -l
-rwxr-xr-x 1 root shell u:object_r:system_file:s0 207 2020-01-14 11:39 am
lrwxr-xr-x 1 root shell u:object_r:zygote_exec:s0 13 2020-01-14 11:39 app_process -> app_process64
-rwxr-xr-x 1 root shell u:object_r:zygote_exec:s0 24988 2020-01-14 11:39 app_process32
-rwxr-xr-x 1 root shell u:object_r:zygote_exec:s0 23960 2020-01-14 11:39 app_process64
-rwxr-xr-x 1 root shell u:object_r:system_file:s0 194 2020-01-14 11:39 locksettings
lrwxr-xr-x 1 root shell u:object_r:system_file:s0 6 2020-01-14 11:39 log -> toybox
-rwxr-xr-x 1 root shell u:object_r:logcat_exec:s0 6840 2020-01-14 11:39 logcat
-rwxr-xr-x 1 root shell u:object_r:shell_exec:s0 298864 2020-01-14 11:39 sh
#/vendor/lib目录下的安全属性
1|HWBLN-H:/ $ ls /vendor/lib/ -l -Z
total 53600
-rw-r--r-- 1 root root u:object_r:vendor_file:s0 113284 2020-01-14 11:03 [email protected]
-rw-r--r-- 1 root root u:object_r:vendor_file:s0 159148 2020-01-14 11:03 [email protected]
-rw-r--r-- 1 root root u:object_r:vendor_file:s0 71608 2020-01-14 10:58 displayeffect.hi6250.so
drwxr-xr-x 2 root shell u:object_r:same_process_hal_file:s0 4096 2020-01-14 10:50 egl
drwxr-xr-x 2 root shell u:object_r:vendor_hal_file:s0 4096 2020-01-14 11:03 hw
drwxr-xr-x 2 root shell u:object_r:vendor_file:s0 4096 2020-01-14 11:03 hwcam
-rw-r--r-- 1 root root u:object_r:vendor_file:s0 53792 2020-01-14 10:58 libAntiTheftCA.so
-rw-r--r-- 1 root root u:object_r:vendor_file:s0 62704 2020-01-14 10:50 libBestShot.so
-rw-r--r-- 1 root root u:object_r:vendor_file:s0 16048 2020-01-14 10:58 libBootloaderOeminfo.so
如上文件/system/bin/log的type值为system_file,文件/system/bin/logcat的type值为logcat_exec,文件/system/bin/sh的type值为shell_exec。个人理解type后戳为file表示单纯的文件,后戳为exec代表它能够被执行,但是他们可不是天生就是这样的,具体详情后文将介绍。
SEAndroid世界将所有事物都打上了标签(每个事物都有自己的SContext),那么SEAndroid就能根据这些SContext来进行统一管理。SEAndroid就是通过一系列的te文件来管理所有进程和资源的权限,每个te文件里面都有无数条te语句,每条te语句对应一条安全策略。
#一条完整的te安全控制语句格式应该为: AV规则 主体 客体:客体类别 许可
#示例1:
allow netd proc:file write
#allow为AV规则,表示允许
#netd为主体,一般为进程SContext的type
#proc为客体,一般为资源SContext的type
#file为客体类别,表示proc是一个文件
#write为许可,表示写权限
#综上可以解读为:允许域名为netd的所有进程具有对资源文件proc的write权限
#示例2:
allow zygote init:process sigchld;
#allow为AV规则,表示允许
#zygote为主体
#init为客体,一般为资源SContext的type
#process为客体类别,表示init是一个进程
#sigchld为许可,表示发送信号
#综上可以解读为:允许zygote进程向init进程发送信号
上节初步介绍了访问规则,在实际开发过程中,通常用的最多的还是allow语句,该语句格式如下:
allow 主体type 客体type 客体类别: {权限}
当SELinux拒绝访问权限的时候,将会打印如下日志:
avc:denied { map } for path="/data/local/data/mute.png" dev="mmcblk0p37" ino=5338 scontext=u:r:bootvideo:s0 tcontext=u:object_r:system_data_file:s0 tclass=file permissive=0
像这类日志都是以关键字avc开头,需要注意其中几个很重要的字段:
通过上面几个关键字,我们就可以为其配置专门的AV规则,安装allow语句的规则可以在对应主体进程的te文件下添加allow语句:
allow bootvideo system_data_file:file { map }
我们配置了allow语句之后,可能还没生效,这个时候就可能需要调试了,查看一下我们的allow语句是否被编译到镜像里面的cil文件中。
例如System的一些配置编译之后会放到如下目录
除了上述方式去进行allow语句的手动配置,我们还可以使用audio2allow工具来自动帮我们生成allow语句,具体用法点击我。
这里附上我学习资料中的一张图,感觉还是比较形象的,如下图:
(1) Subject: 一个必须在场的主体,以引起一个物体采取行动(例如,读取文件时,信
息仅在涉及主题时流动)
(2) Object: 一个对象管理器,它知道特定资源(例如文件)所需的操作并可以强制执行
这些操作(即,如果策略允许,则允许它写入文件)
(3) Security Server: 一个安全服务器,它根据安全策略规则做出有关主体权限以对对象
执行请求的操作的决策
(4) Security Policy: 使用 SELinux 策略语言描述规则的安全策略
(5) AVC: 通过缓存安全服务器决策来提高系统性能的访问向量缓存 (AVC)
当一个 subject(如: 一个应用)试图访问一个 object(如:一个文件),Kernel 中的策略执行服务器将检查 AVC (Access Vector Cache), 在 AVC 中,subject 和 object 的权限被缓存(cached)。如果基于 AVC 中的数据不能做出决定,则请求安全服务器,安全服务器在一个矩阵中查找“应用+文件”的安全环境。然后根据查询结果允许或拒绝访问。
这里不深入讲解SEAndroid相关的所有文件,也不讲解一些基本type和属性的一些定义,只需要明白android默认的策略配置都基本上写好了,这些策略配置基本上不需要我们去更改。但是厂商定制策略文件的sepolicy目录下。这里介绍该目录下几个比较重要的文件。
service.te文件主要定义了一些service(init.rc服务对应的进程)的type。即定义了init.rc中的service对应进程的SContext的type字段。这些type通常会继承于service_manager_type。如下:
type hitv_service, service_manager_type;
type bootvideo_service, service_manager_type;
type quickplay_service, service_manager_type;
type shen_service, service_manager_type;
前文定义了一些type,但是这些type并没有得到使用,service_contexts文件就是用来配置Service(四大组件之一)系统服务的安全属性SContext,其中使用service.te里面定义的type(进程的type也叫域名Domain)。如下:
TVService u:object_r:tvservice_service:s0
hitvservice u:object_r:tvservice_service:s0
hirmservice u:object_r:tvservice_service:s0
bootvideo u:object_r:bootvideo_service:s0
quickplay u:object_r:quickplay_service:s0
shen u:object_r:shen_service:s0
前文已经为init.rc将启动的进程做了安全属性的配置,那么可以在为每个进程做具体的安全策略,通常每个自定义进程就会对应一个te文件,编译系统打包过程中会自动将sepolicy目录下的配置拼接到原生SEAndroid中。这里拿自定义进程shen来举例,那么就需要创建一个shen.te文件来配置具体安全策略。如下:
type shen,coredomain,domain;
#定义shen,继承于coredomain和domain,将代表某一组进程的域名
type shen_exec,exec_type,file_type;
#定义shen_exec,继承于exec_type和file_type,将代表某一资源,继承了执行权限
init_domain_domain(shen)
#具体安全策略
allow 语句
neverallow 语句
前文配置了进程SContext,那么这里还需要配置资源SContext,如下:
#Devices
/dev/sockect/vold0 u:object_r:vold_sockect:s0
/dev/sockect/vold1 u:object_r:vold_sockect:s0
#System files
/system/bin/bootvideo u:object_r:bootvideo_exec:s0
/system/bin/quickplay u:object_r:quickplay_exec:s0
/system/bin/shen u:object_r:shen_exec:s0
如上配置文件/system/bin/shen的type为shen_exec,前文继承于exec_type,表示该资源为一个可执行文件。
service shen /system/bin/shen
user root
class core
现如今就可以在init.rc中配置如上代码,让init进程fock创建一个进程shen,并执行了/system/bin/shen里面的代码。其中新进程名叫shen,执行的资源文件是/system/bin/shen,但是仅仅这样,系统开机启动后并没有启动进程shen,原来是因为init进程在启动rc文件中的service之前初始化了selinux相关的东西,因此selinux校验不过,进程shen无法被启动,因此需要作如下步骤配置:
创建sepolicy/vendor/shen.te文件:
type shen,coredomain,domain;
#定义shen,继承于coredomain和domain,将代表某一组进程的域名
type shen_exec,exec_type,file_type;
#定义shen_exec,继承于exec_type和file_type,将代表某一资源,继承了执行权限
init_domain_domain(shen)
binder_use(shen)
binder_service(shen)
binder_call(shen,system_server)
hwbinder_use(shen)
set_prop(shen,exported_system_prop)
set_prop(shen,exported2_system_prop)
allow shen mediaserver_service:service_manager { find };
allow shen mediaserver:binder { call};
allow shen surfaceflinger_service:service_manager { find };
allow shen surfaceflinger:binder { call };
allow shen surfaceflinger:fd { call };
allow shen system_file:dir { read open };
allow shen system_prop:property_service { set };
allow shen storage_file:link_file {read open };
allow shen system_data_file:file {read open map };
上面的策略文件,先定义了两个type,还记得type是什么吗?没错,是Linux任何东西(包括进程和资源)都具有的SContext安全属性中的type。上面的shen.te文件前两句就定义了两个type:shen和shen_exec,其中shen继承于domain,shen_exec继承于file_type。
深入原生sepolicy代码中的定义就会知道,domain专门用来修饰进程SContext的type,表示一个域(一组进程),我们的进程叫shen,因此用字符串shen来表示这组进程,在后面的allow语句中都是allow shen xxxx来实现安全策略。
file_type是用来修饰资源SContext的type,这里定义了一个shen_exec,并继承于exec_type/file_type,其中exec_type表示可执行文件,因此只要有某资源安全属性的type字段就表示它能够被执行。在后文中我们会把shen_exec赋予/system/bin/shen文件。
init_domain_domain(shen)是一个宏,声明当一个domain为init的进程创建一个子进程执行一个type为study_exec的文件时,将该子进程的domain设置为shen,而不是继承父进程(它的父进程就是init进程)的domain。
rc文件中指定进程shen执行/system/bin/shen这个二进制文件,但到目前为止,这个文件就是一个普普通通的文件,shen进程读取该文件的代码时候将会被selinux权限校验的时候拒绝,因此我们还需要对该文件赋予可被执行的type。
sepolicy/vendor/file_contexts专门用来配置所有资源的SContext,因此我们只需要在该文件中进行如下配置即可:
#sepolicy/vendor/file_contexts
/system/bin/shen u:object_r:shen_exec:s0
经过上面两个步骤init可以成功启动进程shen,但是此时的shen并不能在系统生态中使用binder进程间通信功能。
sepolicy/vendor/service.te中定义继承于service_manager_type的type,如下:
type shen_service, service_manager_type;
将拥有service_manager_type的type赋予给进程shen,如下:
shen u:object_r:shen_service:s0
遇到neverallow规则问题,千万别急着去注释/剔除里面原有的规则(原生的尽量别动它!)。增加allow规则是常见的解决办法,但是随着 android 版本的升级,系统对 selinux 的管控越来越严,增加了大量的 neverallow。一般情况下,向默认标签授予权限的做法是错误的。其中许多权限都是 neverallow 规则所不允许的。按照上面方法去添加 selinux 可能会违反了 neverallow规则,编译时候会报 neverallow 相关的错误。
这是比较常见的 neverallow 错误,比如在 systemserver 进程中去对新加的属性设置属性,运行时就会报 default propert 的错误。
遇到这种类型的错误,首先需要给新增的属性添加标签。在 property.te 中定义 property
type demo_prop, property_type;
在 Android 12 中,由于新的 neverallow,如果对 file 进行操作将会编译不过
neverallow domain {
property_type
-system_property_type
-product_property_type
-vendor_property_type
}:file no_rw_file_perms;
新的 property type 需要指出具体是什么 property type,比如用于 vendor 就改成如下
type demo_prop, property_type, vendor_property_type;
但是如果这个属性还需要在 coredomain 中使用,比如 shell,那么上面写法还是会报一样的 neverallow,需要额外添加 vendor_restricted_property_type 或者是 vendor_restricted_prop(demo_prop)
在 proerty_contexts 中匹配所需要访问的 property
prop u:object_r:demo_prop:s0
然后再根据提示添加政策,比如例子是在systemserver中操作,所以在system_server.te中添加如下政策:
allow system_server demo_prop:property_service set;
比如新添加了一个 java 服务,那么需要在 service.te 中新增对应 type: type new_service, service_manager_type; 然后在 service_contexts 中匹配servicename u:object_r: new_service:s0
最后添加根据报错添加相关政策
native 的服务一般是通过 rc 文件定义的可执行程序拉起并注册进 ServiceManager 的,所以要对可执行程序做如下权限配置:
创建一个新域 newdomain.te
type newdomain, domain;
type newdomain _exec, exec_type, file_type;
init_daemon_domain(newdomain)
在 file_contexts 添加对应标签
Service 地址 u:object_r:newdomain _exec:s0
最后添加相关的政策
payjoy移植过程中遇到过这个问题,同时注意一定要在项目文件中进行配置(不要去配Google那边的了)。部分配置如下:
./service_context.te
# Start of PayJoy changes
payjoy_access_service u:object_r:payjoy_access_service:s0
# End of PayJoy changes
./service.te
# Start of PayJoy changes
type payjoy_access_service, system_server_service, service_manager_type;
# End of PayJoy changes
不要少了任何一步,否则都有可能不成功。
bsp 创建了一个新的设备节点,即使权限是 777,android 层也是没有访问权限的。
同样首先在 device.te 中添加定义
type new_device, dev_type;
然后在 file_context 中添加对应标签
device 路径 u:object_r:md_block_device:s0
最后添加相关的政策
总结:对于neverallow 需要缩小权限,明确具体需要对什么添加权限。然后根据需要的类型添加对应的 type 和 context 标签, 最后添加对应的政策。