这是一个介绍 Android 属性系统的系列文章:
- Android 属性系统入门
- 属性文件生成过程分析
- 如何添加系统属性
- 属性与 Selinux (本文)
- 属性系统整体框架与启动过程分析
- 属性读写过程源码分析
Android O 以后,属性的 Selinux 的规则变得复杂起来了,今天我们先看一个简单的例子,做基本的了解。在Hal与硬件服务
的 treble 章节我们还会继续来学习属性相关的 Selinux 配置。
# 读写属性的示例代码
首先我们在 device/jelly/rice14
目录下创建如下的目录:
PropTest/
├── Android.bp
└── prop_test.cpp
2
3
其中 prop_test.cpp
具体内容如下:
#include <string>
#include <cutils/properties.h>
#define LOG_TAG "prop_test"
#include <log/log.h>
#include <android-base/properties.h>
using namespace std;
int main(int argc, char *argv[])
{
// 打印版本信息
char android_version[PROPERTY_VALUE_MAX];
property_get("ro.build.version.release", android_version, "");
ALOGD("android version : %s", android_version);
// 写入自定义属性
property_set("vendor.my.prop.test", "xxx");
// 读自定义属性
char prop_test[PROPERTY_VALUE_MAX];
property_get("vendor.my.prop.test", prop_test, "");
ALOGD("prop test : %s", prop_test);
while(1) {
}
return 0;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
配套的编译文件 Android.bp
如下:
cc_binary {
name: "prop_test",
srcs: [ "prop_test.cpp" ],
vendor: true,
shared_libs: [
"libbase",
"liblog",
"libcutils",
"libutils"
],
}
2
3
4
5
6
7
8
9
10
11
至此,整个示例程序就完成了,最后修改我们自定义 Product 的配置文件 device/jelly/rice14/rice14.mk
:
PRODUCT_PACKAGES +=
# ......
prop_test \
2
3
# Selinux 的配置
接着需要配置 SeLinux:
在 device/jelly/rice14
目录下创建如下的文件与目录:
sepolicy/
├── file_contexts
├── property_contexts
├── property.te
└── prop_te
2
3
4
5
不要忘了配置自定义 sepolicy 目录,在 device/jelly/rice14/rice14.mk
中添加如下内容:
BOARD_SEPOLICY_DIRS += \
device/jelly/rice14/sepolicy
2
首先在 property.te
中定义属性类型
type vendor_mytest_prop, property_type;
接着在 property_contexts
中配置好我们自定义属性 vendor.my.prop.test
的安全上下文:
vendor.my.prop.test u:object_r:vendor_mytest_prop:s0
接着在 prop_test.te
中配置好可执行文件和对应进程的类型和域转换规则以及属性的读写权限:
# 可执行文件对应进程类型
type myprop_test_dt, domain;
# 可执行文件类型
type myprop_test_dt_exec, exec_type, vendor_file_type, file_type;
# 域转换规则
init_daemon_domain(myprop_test_dt)
domain_auto_trans(shell, myprop_test_dt_exec, myprop_test_dt)
# 属性读写规则
set_prop(myprop_test_dt, vendor_mytest_prop);
get_prop(myprop_test_dt, vendor_mytest_prop);
get_prop(myprop_test_dt, exported2_default_prop);
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
在属性读写规则中,我们添加了对 exported2_default_prop
类型属性的读取规则,exported2_default_prop
是源码中 ro.build.version.release
属性对应的 type,我是通过如下的搜索命令查找到的:
find . -name "property_contexts" | xargs grep ro.build.version.release
./system/sepolicy/public/property_contexts:ro.build.version.release u:object_r:exported2_default_prop:s0 exact string
./system/sepolicy/prebuilts/api/29.0/public/property_contexts:ro.build.version.release u:object_r:exported2_default_prop:s0 exact string
./system/sepolicy/prebuilts/api/28.0/public/property_contexts:ro.build.version.release u:object_r:exported2_default_prop:s0 exact string
2
3
4
最后我们需要再 file_contexts
中配置可执行文件的安全上下文:
/vendor/bin/prop_test u:object_r:myprop_test_dt_exec:s0
接着我们就可以执行在虚拟机的 shell 中执行 prop_test 可执行文件了:
# 重新编译源码
source build/envsetup.sh
lunch rice14-eng
make -j16
# 启动虚拟机
emulator
# 重开一个终端,进入虚拟机 shell 环境
adb shell
# root
su
# selinux 配置为 Permissive 模式
setenforce 0
# 退出 root
exit
prop_test
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
接着我们查看 log:
logcat | grep prop_test
10-23 10:25:42.420 2959 2959 W prop_test: type=1400 audit(0.0:27): avc: denied { read } for name="u:object_r:vendor_mytest_prop:s0" dev="tmpfs" ino=6750 scontext=u:r:shell:s0 tcontext=u:object_r:vendor_mytest_prop:s0 tclass=file permissive=0
10-23 10:25:42.424 2959 2959 D prop_test: android version : 10
10-23 10:25:42.428 2959 2959 D prop_test: prop test :
10-23 10:29:41.930 3704 3704 W prop_test: type=1400 audit(0.0:58): avc: denied { use } for path="/dev/pts/0" dev="devpts" ino=3 scontext=u:r:myprop_test_dt:s0 tcontext=u:r:adbd:s0 tclass=fd permissive=0
10-23 10:29:41.930 3704 3704 W prop_test: type=1400 audit(0.0:60): avc: denied { use } for path="/dev/pts/0" dev="devpts" ino=3 scontext=u:r:myprop_test_dt:s0 tcontext=u:r:adbd:s0 tclass=fd permissive=0
10-23 10:29:41.930 3704 3704 W prop_test: type=1400 audit(0.0:61): avc: denied { use } for path="/dev/goldfish_pipe" dev="tmpfs" ino=7279 scontext=u:r:myprop_test_dt:s0 tcontext=u:r:adbd:s0 tclass=fd permissive=0
10-23 10:29:41.930 3704 3704 W prop_test: type=1400 audit(0.0:62): avc: denied { use } for path="/dev/goldfish_pipe" dev="tmpfs" ino=7279 scontext=u:r:myprop_test_dt:s0 tcontext=u:r:adbd:s0 tclass=fd permissive=0
10-23 10:31:17.980 3919 3919 I prop_test: type=1400 audit(0.0:87): avc: denied { use } for path="/dev/pts/0" dev="devpts" ino=3 scontext=u:r:myprop_test_dt:s0 tcontext=u:r:adbd:s0 tclass=fd permissive=1
10-23 10:31:17.980 3919 3919 I prop_test: type=1400 audit(0.0:88): avc: denied { read write } for path="/dev/pts/0" dev="devpts" ino=3 scontext=u:r:myprop_test_dt:s0 tcontext=u:object_r:devpts:s0 tclass=chr_file permissive=1
10-23 10:31:17.980 3919 3919 I prop_test: type=1400 audit(0.0:89): avc: denied { read write } for path="socket:[9765]" dev="sockfs" ino=9765 scontext=u:r:myprop_test_dt:s0 tcontext=u:r:adbd:s0 tclass=unix_stream_socket permissive=1
10-23 10:31:17.980 3919 3919 I prop_test: type=1400 audit(0.0:90): avc: denied { use } for path="/vendor/bin/prop_test" dev="dm-1" ino=158 scontext=u:r:myprop_test_dt:s0 tcontext=u:r:shell:s0 tclass=fd permissive=1
10-23 10:31:18.000 3919 3919 D prop_test: android version : 10
10-23 10:31:18.002 3919 3919 D prop_test: prop test : xxx
2
3
4
5
6
7
8
9
10
11
12
13
14
发现我们的程序任然缺少一些权限,我们把权限相关的 log 拷贝下来,在源码根目录下新建一个 avc_log.txt 文件,把缺少权限相关的 log 拷贝进去:
# avc_log.txt
10-23 10:38:05.670 2910 2910 W prop_test: type=1400 audit(0.0:28): avc: denied { use } for path="/dev/pts/0" dev="devpts" ino=3 scontext=u:r:myprop_test_dt:s0 tcontext=u:r:adbd:s0 tclass=fd permissive=0
10-23 10:38:05.670 2910 2910 W prop_test: type=1400 audit(0.0:30): avc: denied { use } for path="/dev/pts/0" dev="devpts" ino=3 scontext=u:r:myprop_test_dt:s0 tcontext=u:r:adbd:s0 tclass=fd permissive=0
10-23 10:38:05.670 2910 2910 W prop_test: type=1400 audit(0.0:31): avc: denied { use } for path="socket:[10955]" dev="sockfs" ino=10955 scontext=u:r:myprop_test_dt:s0 tcontext=u:r:adbd:s0 tclass=fd permissive=0
10-23 10:38:05.670 2910 2910 W prop_test: type=1400 audit(0.0:32): avc: denied { use } for path="/dev/goldfish_pipe" dev="tmpfs" ino=7343 scontext=u:r:myprop_test_dt:s0 tcontext=u:r:adbd:s0 tclass=fd permissive=0
10-23 10:38:57.400 3034 3034 I prop_test: type=1400 audit(0.0:39): avc: denied { use } for path="/dev/pts/0" dev="devpts" ino=3 scontext=u:r:myprop_test_dt:s0 tcontext=u:r:adbd:s0 tclass=fd permissive=1
10-23 10:38:57.400 3034 3034 I prop_test: type=1400 audit(0.0:40): avc: denied { read write } for path="/dev/pts/0" dev="devpts" ino=3 scontext=u:r:myprop_test_dt:s0 tcontext=u:object_r:devpts:s0 tclass=chr_file permissive=1
10-23 10:38:57.400 3034 3034 I prop_test: type=1400 audit(0.0:41): avc: denied { read write } for path="socket:[10955]" dev="sockfs" ino=10955 scontext=u:r:myprop_test_dt:s0 tcontext=u:r:adbd:s0 tclass=unix_stream_socket permissive=1
10-23 10:38:57.410 3034 3034 I prop_test: type=1400 audit(0.0:42): avc: denied { use } for path="/vendor/bin/prop_test" dev="dm-1" ino=158 scontext=u:r:myprop_test_dt:s0 tcontext=u:r:shell:s0 tclass=fd permissive=1
2
3
4
5
6
7
8
9
接着执行命令 audit2allow -i avc_log.txt
生成缺失的权限配置:
allow myprop_test_dt adbd:fd use;
allow myprop_test_dt adbd:unix_stream_socket { read write };
allow myprop_test_dt devpts:chr_file { read write };
allow myprop_test_dt shell:fd use;
2
3
4
把这些权限配置加入到 prop_test.te
后,重新编译系统,启动虚拟机,程序即可正常运行。
# 关于
我叫阿豪,2015 年本科毕业于国防科学技术大学指挥信息系统专业,毕业后从事信息化装备的研发工作。
如果你对 Android Framework 感兴趣或者正在学习 Android Framework,可以关注我的微信公众号和抖音,我会持续分享我的学习经验,帮助正在学习的你少走一些弯路。学习过程中如果你有疑问或者你的经验想要分享给大家可以添加我的微信,我拉你进技术交流群。