这是一个介绍 Android 特点体系的系列文章:

  • Android 特点体系入门
  • 特点文件生成进程剖析
  • 如何增加体系特点
  • 特点与 Selinux (本文)
  • 特点体系全体结构与启动进程剖析
  • 特点读写进程源码剖析

Android O 以后,特点的 Selinux 的规矩变得复杂起来了,今日咱们先看一个简单的比如,做根本的了解。在Hal与硬件服务的 treble 章节咱们还会持续来学习特点相关的 Selinux 装备。

读写特点的示例代码

首先咱们在 device/jelly/rice14 目录下创立如下的目录:

PropTest/
├── Android.bp
└── prop_test.cpp

其间 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;
}

配套的编译文件 Android.bp 如下:

cc_binary {
    name: "prop_test",
    srcs: [ "prop_test.cpp" ],
    vendor: true,
    shared_libs: [
        "libbase",
        "liblog",
        "libcutils",
        "libutils"
    ],
}

至此,整个示例程序就完成了,最终修正咱们自定义 Product 的装备文件 device/jelly/rice14/rice14.mk :

PRODUCT_PACKAGES +=
    # ......
    prop_test 

Selinux 的装备

接着需求装备 SeLinux

device/jelly/rice14 目录下创立如下的文件与目录:

sepolicy/
├── file_contexts
├── property_contexts
├── property.te
└── prop_te

不要忘了装备自定义 sepolicy 目录,在 device/jelly/rice14/rice14.mk 中增加如下内容:

BOARD_SEPOLICY_DIRS += 
    device/jelly/rice14/sepolicy

首先在 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);

在特点读写规矩中,咱们增加了对 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

最终咱们需求再 file_contexts 中装备可履行文件的安全上下文:

/vendor/bin/prop_test                   u:object_r:myprop_test_dt_exec:s0

接着咱们就可以履行在虚拟机的 shell 中履行 prop_test 可履行文件了:

# 启动虚拟机
emulator
# 重开一个终端,进入虚拟机 shell 环境
adb shell
# root
su 
# selinux 装备为 Permissive 模式
setenforce 0
# 退出 root
exit
prop_test

接着咱们查看 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

发现咱们的程序任然缺少一些权限,咱们把权限相关的 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

接着履行命令 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;

把这些权限装备加入到 prop_test.te 后,重新编译体系,启动虚拟机,程序即可正常运转。