Android SELinux是Google从android L 开始,强制引入的一套非常严格的权限管理机制,主要用于增强系统的安全性。
然而,在开发中,特别是手机开发中,我们经常会遇到由于SELinux造成的各种权限不足,即使拥有“万能的root权限”,也不能获取全部的权限。本文旨在结合具体案例,讲解如何根据log来快速解决90%的SELinux权限问题。
为了澄清是否因为SELinux导致的问题,可先执行:
setenforce 0 (临时禁用掉SELinux,在项目初始阶段经常使用该方式,以免权限问题影响开发进度)
getenforce (得到结果为Permissive)
如果问题消失了,基本可以确认是SELinux造成的权限问题,需要通过正规的方式来解决权限问题。
遇到权限问题,在logcat或者kernel的log中一定会打印avc denied提示缺少什么权限,可以通过命令过滤出所有的avc denied,再根据这些log各个击破:
k79v1_64_tee_ven_p:/ #cat /dev/kmsg |grep avc
或
k79v1_64_tee_ven_p:/ #logcat |grep avc
例如:
audit(0.0:67): avc: denied { write } for path="/dev/block/vold/93:96" dev=“tmpfs” ino=1263 scontext=u:r:kernel:s0 tcontext=u:object_r:block_device:s0 tclass=blk_file permissive=0
可以看到有avc denied,且最后有permissive=0,表示不允许。
解决原则是:缺什么权限补什么,一步一步补到没有avc denied为止。
解决权限问题需要修改的权限文件如下位置,以.te结尾
such as:
device/mediatek/mt6779/sepolicy/bsp/*.te
下面给出四个案例:
audit(0.0:67): avc: denied { write } for path="/dev/block/vold/93:96" dev="tmpfs" ino=/1263 scontext=u:r:kernel:s0 tcontext=u:object_r:block_device:s0 tclass=blk_file permissive=0
分析过程:
缺少什么权限: { write }权限,
谁缺少权限: scontext=u:r:kernel:s0
对哪个文件缺少权限:tcontext=u:object_r:block_device
什么类型的文件: tclass=blk_file
完整的意思: kernel进程对block_device类型的blk_file缺少write权限。
解决方法:在上文A位置,找到kernel.te这个文件,加入以下内容:
allow kernel block_device:blk_file write;
make installclean后重新编译,刷boot.img才会生效。
audit(0.0:53): avc: denied { execute } for path="/data/data/com.mofing/qt-reserved-files/plugins/platforms/libgnustl_shared.so" dev="nandl" ino=115502 scontext=u:r:platform_app:s0 tcontext=u:object_r:app_data_file:s0 tclass=file permissive=0
分析过程:
缺少什么权限: { execute}权限,
谁缺少权限: scontext = u:r:platform_app:s0
对哪个文件缺少权限:tcontext = u:object_r:app_data_file
什么类型的文件: tclass= file
完整的意思: platform_app进程对app_data_file类型的file缺少execute权限。
解决方法:在上文A位置,找到platform_app.te这个文件,加入以下内容:
allow platform_app app_data_file:file execute;
make installclean后重新编译,刷boot.img才会生效。
audit(1444651438.800:8): avc: denied { search } for pid=158 comm="setmacaddr" name="/" dev="nandi" ino=1 scontext=u:r:engsetmacaddr:s0 tcontext=u:object_r:vfat:s0 tclass=dir permissive=0
解决方法 :engsetmacaddr.te
allow engsetmacaddr vfat:dir { search write add_name create }; 或者
allow engsetmacaddr vfat:dir create_dir_perms;
(create_dir_perms包含search write add_name create可参考external/sepolicy/global_macros的定义声明)
audit(1441759284.810:5): avc: denied { read } for pid=1494 comm="sdcard" name="0" dev="nandk" ino=245281 scontext=u:r:sdcardd:s0 tcontext=u:object_r:system_data_file:s0 tclass=dir permissive=0
解决方法 :sdcardd.te
allow sdcardd system_data_file:dir read; 或者
allow sdcardd system_data_file:dir rw_dir_perms;
(rw_dir_perms包含read write,可参考external/sepolicy/global_macros的定义声明)
通过这四个案例,我们可以总结出一般规律,
以第案例4为例:
audit(1441759284.810:5): avc: denied { read } for pid=1494 comm="sdcard" name="0" dev="nandk" ino=245281 scontext=u:r:sdcardd:s0 tcontext=u:object_r:system_data_file:s0 tclass=dir permissive=0
某个scontext对某个tclass类型的tcontext缺乏某个权限,我们需要允许这个权限:
我们的log重新排列一下,
scontext = u:r:sdcardd:s0
tcontex t= u:object_r:system_data_file:s0
tclass = dir
avc: denied { read }
得到万能套用公式如下:
在scontext所指的.te文件(例如sdcardd.te)中加入类似如下allowe内容:
allow sdcardd system_data_file:dir read
更多内容请参考external/sepolicy/global_macros来了解更多权限声明。
要加入的权限很多时,可以用中括号,比如:
allow engsetmacaddr vfat:dir { search write add_name create};
修改A位置的.te文件遇到编译错误怎么办?
(首先请排除拼写错误)说明此项权限是SELinux明确禁止的,也是Google CTS禁止的,如果产品不需要过CTS,可以修改。一般来说,编译出错的log会提示相关哪个文件哪一行出错,文件位置一定会在B里的.te文件。比如B规定了以下neverallow,
neverallow system_server sdcard_type:dir { open read write };
那么system_server是不能拥有这些权限的,如果赋予这些权限就编译报错,解决方法是根据编译错误提示的行号,把这一句注释掉即可。
驱动创建了一个新的设备节点,即使权限是777,android层也是没有访问权限的。
下面以一个思立微指纹设备/dev/silead_stub 节点为示范,让此节点被用户空间的 hal_fingerprint_default 进程访问。
1.定义一个dev_type类型的 silead_stub 设备:
device/mediatek/mt6779/sepolicy/bsp$ vim hal_fingerprint_default.te
type silead_fpd_device, dev_type;
2.编辑 file_contexts,将/dev/silead_stub节点声明为第1步定义的 silead_fpd_device:
device/mediatek/mt6779/sepolicy/bsp$ vim file_contexts
/dev/silead_stub u:object_r:silead_fpd_device:s0
3.在hal_fingerprint_default.te,根据 dmesg | grep avc允许 hal_fingerprint_default 对 silead_fpd_device 这个节点可读可写:
device/mediatek/mt6779/sepolicy/bsp$ vim hal_fingerprint_default.te
allow hal_fingerprint_default silead_fpd_device:chr_file rw_file_perms;
其他进程如需访问 /dev/silead_stub 节点,依样画葫芦,增加对 silead_fpd_device 的权限即可。
代码创建了一个新的文件/目录,即使权限是777,android层也是没有访问权限的。
下面以一个思立微指纹存图目录data/vendor/silead/为示范,让此节点被用户空间的 hal_fingerprint_default 进程访问。
1.定义一个file_type and data_file_type 类型的 silead_fpd_data_file 文件:
device/mediatek/mt6779/sepolicy/bsp$ vim hal_fingerprint_default.te
type silead_fpd_data_file, file_type, data_file_type
2.编辑 file_contexts,将/data/(vendor|system)/silead(/.*)?文件/目录声明为第1步定义的 silead_fpd_data_file:
device/mediatek/mt6779/sepolicy/bsp$ vim file_contexts
/data/(vendor|system)/silead(/.*)? u:object_r:silead_fpd_data_file:s0
3.在hal_fingerprint_default.te,根据 logcat | grep avc允许 hal_fingerprint_default 对 silead_fpd_data_file 这个节点可读可写:
device/mediatek/mt6779/sepolicy/bsp$ vim hal_fingerprint_default.te
allow hal_fingerprint_default silead_fpd_data_file:file { create_file_perms };
allow hal_fingerprint_default silead_fpd_data_file:dir rw_dir_perms;
其他进程如需访问 data/vendor/silead/目录访问,依样画葫芦,增加对 silead_fpd_data_file 的权限即可。