当前位置: 代码迷 >> 综合 >> [Android Framework]Android 11 SELinux avc权限解决方法
  详细解决方案

[Android Framework]Android 11 SELinux avc权限解决方法

热度:6   发布时间:2023-12-05 11:44:54.0

1.SELinux与Android的关系

SELinux(Security-Enhanced Linux)是由美国国家安全局开发的一种安全增强型Linux内核模块,从Android5.0(API 21)开始被Google引入并强制集成,用于最大限度地减小系统中服务进程可访问的资源,从而增强系统安全性,也就是说即使系统漏洞被Hacker钻了空子,也不容易为所欲为。对于开发者来说,也就意味着即使你开发的服务拥有root权限,你开发的系统服务也并不能任意访问系统资源。

2.在Android中与SELinux有关的命令:

前提:处于root模式下

1.adb shell getenforce 获取系统当前SELinux运行模式
2.adb shell setenforce 0 设置运行模式为Permissive
3.adb shell setenforce 1 设置运行模式为Enforcing

Permissive:只打印log,不进行限制
Enforcing:打印log+限制权限
Disabled:不打印log,不进行限制(无法通过ADB设置)

3.如何定位和理解SELinux权限问题

在进行系统开发时,容易遇到开发定制的系统级服务运行时功能不正常的情况,比如获取不到系统属性和cmdline的节点值,这种时候可以优先排查avc权限问题。即在kernel日志或者logcat中通过过滤功能有问题的服务名称或者avc或者audit和找到如下的Log输出。

avc: denied {
     read } for service=csdn scontext=u:r:csdn:s0 tcontext=u:object_r:sysfs:s0 tclass=file

如果出现了该Log,那么说明当前的系统服务是存在SELinux权限受限的,此时,我们需要理解这句Log对我们来说意味着什么。

首先,我们可以通过以下几个关键词来"肢解"该Log:
denied:read : 被拒绝的权限是read
scontext:csdn : 被拒绝权限的服务是csdn
tcontext:sysfs : csdn被拒之门外的文件未sysfs
tclass:file : 文件类型为file

再整合成我们自己的话,也就是说我们开发的csdn服务被SELinux拒绝读取file类型的sysfs这个文件的权限。

4.如何解决SELinux权限问题

我们再次回到上述的关键词

denied:read : 被拒绝的权限是read
scontext:csdn : 被拒绝权限的服务是csdn
tcontext:sysfs : csdn被拒之门外的文件未sysfs
tclass:file : 文件类型为file

此时,我们需要在源码的根目录下寻找我们开发的服务所需要的策略文件csdn.te,例如我们项目中位于device/qcom/sepolicy/目录下。在里面加入如下代码:

allow csdn sysfs:file {
     read };

有一点需要注意的地方是,SELinux权限报错并不会一次全部输出在log中,一次可能只会输出一条,所以需要一个权限一个权限去解决,比如在该例子中,假设后面又报了对sysfs的open权限,只需要在上述代码的花括号中加入open即可,即:

allow csdn sysfs:file {
     read open };

如果是一般的权限问题,可能到这里就解决了,只需要再重新编译刷机即可。
至此,感谢阅读。


BUT,对于有的幸运玩家来说,在Android的SELinux机制中,有的文件是不允许通过这种方式来放开权限的,比如该例中的sysfs,如果像上面那样加了代码并编译,会提示neverallow错误,顾名思义,就是这种操作是从来不被允许的。

#1 neverallow on line XXX of system/sepolicy/csdn.te (or line XXX of XXX) violated by allow system_app sysfs:file {
     read };
#2 1 neverallow failures occurred
#3 Error while expanding policy

对于neverallow错误的解决方法,可以去另外百度,有直接修改neverallow规则,或者去提示violate的策略文件中加上我们需要的例外服务,但这些方法都违反了Google定义的强制规则,并可能导致CTS报错,故在这里我只分享一种比较安全但是稍显复杂的方法。

共需要修改3个文件:

1.在csdn.te同级目录的file.te中,加入一个域的别名,如在该例中,我们原本需要访问的是sysfs域中的一个文件,所以我们在file.te中定义一个新的域,名字可以随便取,如:

type csdn_sysfs, fs_type, sysfs_type;

该语句的意思是,定义一个叫做csdn_sysfs的域(domain),该域具有fs_type和sysfs_type属性(attribute),关于更多domain和attribute的语法和定义可以百度SELinux关键字和语法。

2.在同目录中的file_contexts中加入对需要操作权限的文件路径与我们定义的域进行映射,如:

/sys/devices/sensors_info/sensors_prop/sensor_dir u:object_r:csdn_sysfs:s0

这样即可将sensor_dir与我们定义的域csdn_sysfs映射起来。
3.最后一步,在我们定义的csdn.te策略文件中,把之前的sysfs替换为我们新定义的域,即

allow csdn csdn_sysfs:file {
     open read };

完成后,编译刷机,对于这部分幸运玩家来说,大部分问题也可以解决了。


BUT, 在这部分幸运玩家中,还有极少数幸运玩家中的幸运玩家,会碰到一个情况,就是编译通过了,刷机成功了,但是仍然在log中打出缺少对sysfs操作权限的错误。

有一个概率较大的原因,是由于我们在file_contexts中映射的路径并不是它实际存在的路径,导致了映射失败,从而添加权限失败。
确定该问题的方法是,adb shell中通过ls -Z取查看该文件的域,如果映射成功了,会变为csdn_sysfs,但是如果失败,仍然为sysfs。

-r--r--r-- 1 root root u:object_r:sysfs:s0  4096 2021-11-17 08:05 sensor_dir

此时,我们需要在adb shell中到我们需要访问的节点通过ls -l检查一下该节点的真实路径

lrwxrwxrwx 1 root root    0 2021-11-17 08:05 subsystem -> /sys/devices/virtual/sensors_info/sensors_prop/sensor_dir

然后将第2个文件file_contexts中的代码改为

/sys/devices/virtual/sensors_info/sensors_prop/sensor_dir u:object_r:csdn_sysfs:s0

至此,编译刷机,问题解决。

  相关解决方案