Android P SELinux (四) CTS neverallow处理总结

Android P SELinux (一) 基础概念
Android P SELinux (二) 开机初始化与策略文件编译过程
Android P SELinux (三) 权限检查原理与调试
Android P SELinux (四) CTS neverallow处理总结

CtsSecurityHostTestCases
首先CTS测试里面关于neverallow的用例,可以学习下这篇文章,代码是动态生成的
Android CTS中neverallow规则生成过程

下面主要分享一些遇到的 CTS neverallow问题的处理方法:

一、一些权限的解决方案

1.1、区分vendor域和system域

按照以往的经验,我们在Android P上添加客制化的脚本,还是喜欢用 #!/system/bin/sh

但是自从Android 8.0的Treble Project之后,vendor和system已经开始分离了,vendor下面的脚本要使用 #!/vendor/bin/sh,这样能减少很多权限问题

举个例子:

/vendor/bin/testA.sh

#!/system/bin/sh
 
...

然后运行的时候会报下面的权限问题

[  973.048826@1]- type=1400 audit(1619667483.432:167): avc: denied { read execute } for pid=4368 comm="testA.sh"
path="/system/bin/sh" dev="mmcblk0p17" ino=1073 scontext=u:r:testA:s0 tcontext=u:object_r:shell_exec:s0 tclass=file permissive=0

这个时候如果直接在te里面添加权限:

allow testA shell_exec:file { read execute };

添加完之后就会发现违反了neverallow规则

FAILED: out/target/product/xxxxx/obj/ETC/sepolicy_neverallows_intermediates/sepolicy_neverallows
/bin/bash -c "(rm -f out/target/product/xxxxx/obj/ETC/sepolicy_neverallows_intermediates/sepolicy_neverallows ) && (ASAN_OPTIONS=detect_leaks=0 out/host/linux-x86/bin/checkpolicy -M -c               30 -o out/target/product/xxxxx/obj/ETC/sepolicy_neverallows_intermediates/sepolicy_neverallows out/target/product/xxxxx/obj/ETC/sepolicy_neverallows_intermediates/policy.conf )"
libsepol.report_failure: neverallow on line 1047 of system/sepolicy/public/domain.te (or line 11499 of policy.conf) violated by allow testA shell_exec:file { execute };
libsepol.check_assertions: 1 neverallow failures occurred
Error while expanding policy
out/host/linux-x86/bin/checkpolicy:  loading policy configuration from out/target/product/xxxxx/obj/ETC/sepolicy_neverallows_intermediates/policy.conf
ninja: build stopped: subcommand failed.
11:42:19 ninja failed with: exit status 1

原因在domain.te的注释里面说的很清楚了,vendor的组件不允许直接运行system的file

full_treble_only(`
    # Do not allow vendor components to execute files from system
    # except for the ones whitelist here.
    neverallow {

修改脚本为:

#!/vendor/bin/sh
 
...

可以解决上述问题

但是改完之后,有些在/system/bin/ 下的命令就用不了,比如pm、am、logcat这些Android才有的命令,而不是在toybox里面的

如果实在要用,可以这样操作:

#!/vendor/bin/sh
 
am broadcast -a com.test.broadcast.toast -e Message "hello world" > /dev/console

直接运行也会报错
console:/ # /vendor/bin/testA.sh[3]: am: not found

可以修改成:

/system/bin/cmd activity broadcast -a com.test.broadcast.toast -e Message "hello world" > /dev/console

te里面要补充关联上属性: vendor_executes_system_violators

type testA, domain, vendor_executes_system_violators;

1.2、setenforce的可行性

这个肯定是没有办法解决的,如果随便一个脚本都可以拥有关闭SELinux的权限,那还得了

在userdebug和eng版本上,可以先这样debug搞:

添加permissive testA;

对testA type以permissive状态运行

不过只能在eng和userdebug版本下才能用

type testA, coredomain, domain, mlstrustedsubject;
type testA_exec, exec_type, file_type;
 
permissive testA;

init_daemon_domain(testA)

要过CTS,这个权限可是天敌啊,主要就是有些功能想做的事情太多,权限太大,搞得非得要临时关闭SELinux来处理

跟着代码实现,不难发现setenforce命令的操作其实就是写节点/sys/fs/selinux/enforce

结合前面提到的权限检查原理,很快可以发现,这个权限就是我们无法绕过的根本原因了

static ssize_t sel_write_enforce(struct file *file, const char __user *buf,
                 size_t count, loff_t *ppos)
 
{
    ......
        length = task_has_security(current, SECURITY__SETENFORCE);
        if (length)
            goto out;
    ......
}

这个权限检查就是后面报avc denied的地方了

allow testA kernel:security { setenforce };

对应的neverallow规则如下:

# Only init prior to switching context should be able to set enforcing mode.
# init starts in kernel domain and switches to init domain via setcon in
# the init.rc, so the setenforce occurs while still in kernel. After
# switching domains, there is never any need to setenforce again by init.
neverallow * kernel:security setenforce;
neverallow { domain -kernel } kernel:security setcheckreqprot;

这儿有个不是办法的办法:就是不影响现有命令的基础上,新增了一个节点,然后直接不做权限检查

diff --git a/security/selinux/selinuxfs.c b/security/selinux/selinuxfs.c
index 72c145dd799f..b2332f55415c 100644
--- a/security/selinux/selinuxfs.c
+++ b/security/selinux/selinuxfs.c
@@ -100,6 +100,7 @@ enum sel_inos {
        SEL_ROOT_INO = 2,
        SEL_LOAD,       /* load policy */
        SEL_ENFORCE,    /* get or set enforcing status */
+       SEL_ENABLE,     /* get or set enforcing status */
        SEL_CONTEXT,    /* validate context */
        SEL_ACCESS,     /* compute access decision */
        SEL_CREATE,     /* compute create labeling decision */
@@ -193,6 +194,57 @@ static const struct file_operations sel_enforce_ops = {
        .llseek         = generic_file_llseek,
 };
  
+#ifdef CONFIG_SECURITY_SELINUX_DEVELOP
+static ssize_t sel_write_enable(struct file *file, const char __user *buf,
+                size_t count, loff_t *ppos)
+
+{
+    char *page = NULL;
+    ssize_t length;
+    int new_value;
+
+    if (count >= PAGE_SIZE)
+        return -ENOMEM;
+
+    /* No partial writes. */
+    if (*ppos != 0)
+        return -EINVAL;
+
+    page = memdup_user_nul(buf, count);
+    if (IS_ERR(page))
+        return PTR_ERR(page);
+
+    length = -EINVAL;
+    if (sscanf(page, "%d", &new_value) != 1)
+        goto out;
+
+    if (new_value != selinux_enforcing) {
+        audit_log(current->audit_context, GFP_KERNEL, AUDIT_MAC_STATUS,
+            "enforcing=%d old_enforcing=%d auid=%u ses=%u",
+            new_value, selinux_enforcing,
+            from_kuid(&init_user_ns, audit_get_loginuid(current)),
+            audit_get_sessionid(current));
+        selinux_enforcing = new_value;
+        if (selinux_enforcing)
+            avc_ss_reset(0);
+        selnl_notify_setenforce(selinux_enforcing);
+        selinux_status_update_setenforce(selinux_enforcing);
+    }
+    length = count;
+out:
+    kfree(page);
+    return length;
+}
+#else
+#define sel_write_enable NULL
+#endif
+
+static const struct file_operations sel_enable_ops = {
+    .read       = sel_read_enforce,
+    .write      = sel_write_enable,
+    .llseek     = generic_file_llseek,
+};
+
 static ssize_t sel_read_handle_unknown(struct file *filp, char __user *buf,
                                        size_t count, loff_t *ppos)
 {
@@ -1790,6 +1842,7 @@ static int sel_fill_super(struct super_block *sb, void *data, int silent)
        static struct tree_descr selinux_files[] = {
                [SEL_LOAD] = {"load", &sel_load_ops, S_IRUSR|S_IWUSR},
                [SEL_ENFORCE] = {"enforce", &sel_enforce_ops, S_IRUGO|S_IWUSR},
+               [SEL_ENABLE] = {"enable", &sel_enable_ops, S_IRUGO|S_IWUSR},
                [SEL_CONTEXT] = {"context", &transaction_ops, S_IRUGO|S_IWUGO},
                [SEL_ACCESS] = {"access", &transaction_ops, S_IRUGO|S_IWUGO},
                [SEL_CREATE] = {"create", &transaction_ops, S_IRUGO|S_IWUGO},

然后开关就用:

关SELinux

echo 0 > /sys/fs/selinux/enable

开SELinux

echo 1 > /sys/fs/selinux/enable

权限还得补一下:

allow testA selinuxfs:file rw_file_perms;
selinux_check_access(testA)

相比setenforce,差异就是去掉了权限检查,开了一个后门,比较bug的存在

1.3、dac_override 解决办法

谷歌官方文档的一段话总结的很精辟

https://source.android.com/security/selinux/device-policy#granting_the_dac_override_capability
在这里插入图片描述
详见:selinux dac_override/dac_read_search问题处理思路

比如修改group的:

未修改前:

service testA /system/bin/testA.sh -start
    user root
    group root
    disabled
    oneshot
    seclabel u:r:testA:s0

修改后:

service testA /system/bin/testA.sh -start
    user root
    group root system
    disabled
    oneshot
    seclabel u:r:testA:s0

大部分情况下,我们可以直接ls -l 查看要访问的文件或者目录的user 和 group

但是有些情况下,我们很难搞清楚访问的文件或者目录所在的group是哪个,这些信息在avc denied里面是不会有的

可以在kernel中补充这些代码,打印出来(参考文章:Debugging DAC_OVERRIDE

patch:

kernelcode$ git diff .
diff --git a/fs/namei.c b/fs/namei.c
index a5a05d3..9b8f0da 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -39,6 +39,8 @@
 #include <linux/init_task.h>
 #include <asm/uaccess.h>
  
+#include <linux/printk.h>
+
 #include "internal.h"
 #include "mount.h"
  
@@ -341,8 +343,12 @@ int generic_permission(struct inode *inode, int mask)
  
        if (S_ISDIR(inode->i_mode)) {
                /* DACs are overridable for directories */
-               if (capable_wrt_inode_uidgid(inode, CAP_DAC_OVERRIDE))
-                       return 0;
+        if (capable_wrt_inode_uidgid(inode, CAP_DAC_OVERRIDE)) {
+            return 0;
+        } else {
+            pr_err("dac_override(dir) denied for cap=%3o inode=%lu with uid=%u gid=%u\n",
+                mask&0777, inode->i_ino, inode->i_uid.val, inode->i_gid.val);
+        }
                if (!(mask & MAY_WRITE))
                        if (capable_wrt_inode_uidgid(inode,
                                                     CAP_DAC_READ_SEARCH))
@@ -354,9 +360,17 @@ int generic_permission(struct inode *inode, int mask)
         * Executable DACs are overridable when there is
         * at least one exec bit set.
         */
-       if (!(mask & MAY_EXEC) || (inode->i_mode & S_IXUGO))
-               if (capable_wrt_inode_uidgid(inode, CAP_DAC_OVERRIDE))
-                       return 0;
+       // if (!(mask & MAY_EXEC) || (inode->i_mode & S_IXUGO))
+       //      if (capable_wrt_inode_uidgid(inode, CAP_DAC_OVERRIDE))
+    /* IXUGO = (S_IXUSR|S_IXGRP|S_IXOTH) */
+    if (!(mask & MAY_EXEC) || (inode->i_mode & S_IXUGO)) {
+        if (capable_wrt_inode_uidgid(inode, CAP_DAC_OVERRIDE)) {
+            return 0;
+        } else {
+            pr_err("dac_override(file) denied for cap=%3o inode=%lu with uid=%u gid=%u\n",
+                mask&0777, inode->i_ino, inode->i_uid.val, inode->i_gid.val);
+        }
+    }
  

diff --git a/security/selinux/avc.c b/security/selinux/avc.c
index e60c79d..9b1dfad 100644
--- a/security/selinux/avc.c
+++ b/security/selinux/avc.c
@@ -734,7 +734,11 @@ static void avc_audit_post_callback(struct audit_buffer *ab, void *a)
        if (ad->selinux_audit_data->denied) {
                audit_log_format(ab, " permissive=%u",
                                 ad->selinux_audit_data->result ? 0 : 1);
-       }
+        if (ad->u.cap == 1) {
+            audit_log_format(ab, " cap=dac_override, dumping stack");
+            WARN_ON(ad->u.cap == 1);
+        }
+    }
 }
  
 /* This is the slow part of avc audit with big stack footprint */

打印信息如下:

[   44.952920@0]- ------------[ cut here ]------------
[   44.952983@0]- WARNING: CPU: 0 PID: 3901 at :739 avc_audit_post_callback+0x180/0x184
[   44.961323@0]- Modules linked in: hardware_dmx(O) jpegenc(O) amvdec_av1(O) amvdec_mavs(O) encoder(O) amvdec_avs2(O) amvdec_vp9(O) amvdec_vc1(O) amvdec_real(O) amvdec_mmpeg4(O) amvdec_mpeg4(O) amvdec_mmpeg12(O) amvdec_mpeg12(O) amvdec_mmjpeg(O) amvdec_mjpeg(O) amvdec_h265(O) amvdec_h264mvc(O) amvdec_mh264(O) amvdec_h264(O) amvdec_avs(O) stream_input(O) decoder_common(O) video_framerate_adapter(O) firmware(O) media_clock(O) tb_detect(PO) mali(O) dnlp_alg(P)
[   45.000226@0]d CPU: 0 PID: 3901 Comm: cmd Tainted: P        W  O    4.9.113 #20
[   45.007459@0]d Hardware name: Generic DT based system
[   45.012491@0]d [bc4afac4+  16][<c020dc10>] show_stack+0x20/0x24
[   45.018343@0]d [bc4afae4+  32][<c054394c>] dump_stack+0x90/0xac
[   45.024201@0]d [bc4afb14+  48][<c0228724>] __warn+0x104/0x11c
[   45.029897@0]d [bc4afb2c+  24][<c022880c>] warn_slowpath_null+0x30/0x38
[   45.036456@0]d [bc4afb5c+  48][<c04d4fec>] avc_audit_post_callback+0x180/0x184
[   45.043606@0]d [bc4afbb4+  88][<c04f20e0>] common_lsm_audit+0x260/0x7c0
[   45.050165@0]d [bc4afbfc+  72][<c04d5994>] slow_avc_audit+0x9c/0xb0
[   45.056376@0]d [bc4afc64+ 104][<c04da364>] cred_has_capability+0x118/0x124
[   45.063184@0]d [bc4afc74+  16][<c04da3f0>] selinux_capable+0x38/0x3c
[   45.069483@0]d [bc4afc9c+  40][<c04d1370>] security_capable+0x4c/0x68
[   45.075870@0]d [bc4afcac+  16][<c0233d58>] ns_capable_common+0x7c/0x90
[   45.082336@0]d [bc4afcc4+  24][<c0233e00>] capable_wrt_inode_uidgid+0x28/0x54
[   45.089409@0]d [bc4afcf4+  48][<c03a5abc>] generic_permission+0x100/0x208
[   45.096138@0]d [bc4afd14+  32][<c0414df0>] kernfs_iop_permission+0x58/0x64
[   45.102951@0]d [bc4afd34+  32][<c03a5c84>] __inode_permission2+0xc0/0xf0
[   45.109589@0]d [bc4afd44+  16][<c03a5cfc>] inode_permission2+0x20/0x54
[   45.116057@0]d [bc4afd8c+  72][<c0396fb4>] SyS_faccessat+0xec/0x220
[   45.122273@0]d [00000000+   0][<c02085c0>] ret_fast_syscall+0x0/0x48
[   45.128879@0]s DI: DI: tasklet schedule cost 128ms.
[   45.133835@0]- ---[ end trace 40474d395a0d6c86 ]---
[   45.138462@0]- dac_override(file) denied for cap= 22 inode=5 with uid=1000 gid=1000
[   45.138640@1]- type=1400 audit(1620051338.180:75): avc: denied { dac_override } for pid=3900 comm="cmd" capability=1 scontext=u:r:testA:s0 tcontext=u:r:testA:s0 tclass=capability permissive=0 cap=dac_override, dumping stack

打印的gid在 system/core/include/private/android_filesystem_config.h 里面查找,就知道init.rc里面要加什么了

1.4、echo打印到终端

#echo "test" > /dev/console
allow testA console_device:chr_file rw_file_perms;

1.5、读写U盘内容

读U盘:

allow testA mnt_media_rw_file:dir { search read open };
allow testA vfat:file r_file_perms;
allow testA vfat:dir r_dir_perms;

要写和创建的话,权限给大一点就行了:

allow testA mnt_media_rw_file:dir { search read open };
allow testA vfat:dir create_dir_perms;
allow testA vfat:file create_file_perms;

1.6、am 命令

allow testA activity_service:service_manager { find };
allow testA system_server:binder { call transfer };
binder_use(testA)
binder_call(system_server, testA)

pm命令同理

1.7、ioctl

[   57.670632] type=1400 audit(1623998437.166:9): avc: denied { ioctl } for pid=4285 comm="Thread-43" path="socket:[30690]" dev="sockfs" ino=30690 ioctlcmd=0x8927 scontext=u:r:untrusted_app_25:s0:c512,c768 tcontext=u:r:untrusted_app_25:s0:c512,c768 tclass=tcp_socket permissive=0

参考:
AndroidQ上ioctl变化

添加:

allow untrusted_app_25 self:udp_socket ioctl;
allowxperm untrusted_app_25 self:udp_socket ioctl SIOCGIFHWADDR;

两句都要添加上,缺一不可

二、常见违反neverallow规则的解决方案

2.1、指定文件、属性、服务的安全上下文,避免权限放大

这一类neverallow规则,其实搞明白为什么要这么做,是很容易解决的

有一个系统属性persist.sys.test

console:/data # getprop -Z persist.sys.test
u:object_r:system_prop:s0

假如你想要设置这个属性,加了一个权限

set_prop(testA, system_prop)

结果一编译就违反neverallow规则了,这是为什么?

因为没有特别指定,persist.sys.开头的属性,它的安全上下问题都是u:object_r:system_prop:s0

/android/system/sepolicy/private/property_contexts
 
persist.sys.            u:object_r:system_prop:s0

一旦你给了这个权限,意味着你可以设置的属性不仅仅是persist.sys.test了,还可以是persist.sys.Apersist.sys.B 等等

这就是权限放大,neverallow规则的限定,就是为了不给你这么玩

我们要做的就是,指定文件、属性、服务的安全上下文,收缩权限

下面有一些例子:

2.1.1、testNeverallowRules131 解决方案

<Test result="fail" name="testNeverallowRules131">
        <Failure message="junit.framework.AssertionFailedError: The following errors were encountered when validating the SELinuxneverallow rule:">
          <StackTrace>junit.framework.AssertionFailedError: The following errors were encountered when validating the SELinuxneverallow rule:
neverallow { domain -kernel -init -recovery } block_device:blk_file { open read write };
libsepol.report_failure: neverallow violated by allow testA block_device:blk_file { read write open };
libsepol.check_assertions: 1 neverallow failures occurred
 
</StackTrace>
        </Failure>
      </Test>

testA是因为要操作 /dev/block/testblock

console:/ # ls -lZ /dev/block/testblock
brw------- 1 root root u:object_r:block_device:s0 179,   5 1970-01-01 08:00 /dev/block/testblock

给这个testblock block指定type,不然就会有权限放大的问题

sepolicy/device.te
type testblock_device, dev_type;
 
sepolicy/file_contexts
/dev/block/testblock                 u:object_r:testblock_device:s0

接着allow语句修改为:

allow testA testblock_device:blk_file { read write open };

2.1.2、testNeverallowRules198 解决方案

<Test result="fail" name="testNeverallowRules198">
        <Failure message="junit.framework.AssertionFailedError: The following errors were encountered when validating the SELinuxneverallow rule:">
          <StackTrace>junit.framework.AssertionFailedError: The following errors were encountered when validating the SELinuxneverallow rule:
neverallow {      domain      -coredomain      -appdomain       -data_between_core_and_vendor_violators       -socket_between_core_and_vendor_violators      -vendor_init    } {      coredomain_socket      core_data_file_type      unlabeled     }:sock_file ~{ append getattr ioctl read write };
libsepol.report_failure: neverallow violated by allow testA property_socket:sock_file { open };
libsepol.check_assertions: 1 neverallow failures occurred
 
</StackTrace>
        </Failure>
      </Test>

查脚本,看设置的是哪个属性,然后把这个属性的安全上下文指定到我们自定义的上面去

sepolicy/property_contexts
sys.testA.finish            u:object_r:test_prop:s0
 
sepolicy/testA.te
set_prop(testA, test_prop)

2.1.3、testNeverallowRules154 解决方案

<Test result="fail" name="testNeverallowRules154">
        <Failure message="junit.framework.AssertionFailedError: The following errors were encountered when validating the SELinuxneverallow rule:">
          <StackTrace>junit.framework.AssertionFailedError: The following errors were encountered when validating the SELinuxneverallow rule:
neverallow * default_android_service:service_manager add;
libsepol.report_failure: neverallow violated by allow testA default_android_service:service_manager { add };
libsepol.check_assertions: 1 neverallow failures occurred
 
</StackTrace>
        </Failure>
      </Test>

首先这个违反的neverallow规则如下,禁止所有的进程添加default_android_service类型的service_manager

neverallow * default_android_service:service_manager add;

先看看之前的解决方案:
sepolicy/service_contexts

添加
test.service                               u:object_r:test_service:s0

service.te:

添加
type test_service,    app_api_service, system_server_service, service_manager_type;

testA.te 里面如下:

allow testA default_android_service:service_manager { add  };

然后再把neverallow规则干掉,神挡杀神,佛挡杀佛

neverallow { domain -testA } default_android_service:service_manager add;

看到这里,比较疑惑的点是service_contexts和service.te,和普通文件一样,service也是可以给它定义一个新的type

但是定义了这个test_service type之后,没有生效?

从报错的信息来看,test.service的type依然还是default_android_service,这是为何?

06-10 08:50:30.172  3062  3062 E SELinux : avc:  denied  { add } for service=test.service pid=4707 uid=0 scontext=u:r:testA:s0 tcontext=u:object_r:default_android_service:s0 tclass=service_manager permissive=0

这时,我们查看 /system/etc/selinux/plat_service_contexts 里面的内容,发现我们添加的test.service 压根就不在里面,难怪不会生效

在板卡上直接修改添加,重启之后,就会发现报错已经变成了这样了:

06-10 08:55:15.748  3062  3062 E SELinux : avc:  denied  { add } for service=test.service pid=4732 uid=0 scontext=u:r:testA:s0 tcontext=u:object_r:test_service:s0 tclass=service_manager permissive=0

接下来再添加权限就行了,这个时候就不会违反neverallow规则

透过现象看本质,为什么会导致这个问题呢?

原因要从编译出plat_service_contexts那里说起,回到/android/system/sepolicy/Android.mk

include $(CLEAR_VARS)
 
LOCAL_MODULE := plat_service_contexts
LOCAL_MODULE_CLASS := ETC
LOCAL_MODULE_TAGS := optional
ifeq ($(PRODUCT_SEPOLICY_SPLIT),true)
LOCAL_MODULE_PATH := $(TARGET_OUT)/etc/selinux
else
LOCAL_MODULE_PATH := $(TARGET_ROOT_OUT)
endif
 
include $(BUILD_SYSTEM)/base_rules.mk
 
plat_svcfiles := $(call build_policy, service_contexts, $(PLAT_PRIVATE_POLICY))
 
plat_service_contexts.tmp := $(intermediates)/plat_service_contexts.tmp
$(plat_service_contexts.tmp): PRIVATE_SVC_FILES := $(plat_svcfiles)
$(plat_service_contexts.tmp): PRIVATE_ADDITIONAL_M4DEFS := $(LOCAL_ADDITIONAL_M4DEFS)
$(plat_service_contexts.tmp): $(plat_svcfiles)
    @mkdir -p $(dir $@)
    $(hide) m4 -s $(PRIVATE_ADDITIONAL_M4DEFS) $(PRIVATE_SVC_FILES) > $@
 
$(LOCAL_BUILT_MODULE): PRIVATE_SEPOLICY := $(built_sepolicy)
$(LOCAL_BUILT_MODULE): $(plat_service_contexts.tmp) $(built_sepolicy) $(HOST_OUT_EXECUTABLES)/checkfc $(ACP)
    @mkdir -p $(dir $@)
    sed -e 's/#.*$$//' -e '/^$$/d' $< > $@
    $(HOST_OUT_EXECUTABLES)/checkfc -s $(PRIVATE_SEPOLICY) $@
 
built_plat_svc := $(LOCAL_BUILT_MODULE)
plat_svcfiles :=
plat_service_contexts.tmp :=

mk里面搜索的目录是PLAT_PRIVATE_POLICY,前面有提过,这个目录是/android/system/sepolicy/private 以及 BOARD_PLAT_PRIVATE_SEPOLICY_DIR 所组成的
所以device厂商下面的sepolicy/service_contexts 没有用到

解决办法很简单:

首先指定拓展的私有目录 BOARD_PLAT_PRIVATE_SEPOLICY_DIR
BOARD_PLAT_PRIVATE_SEPOLICY_DIR += \
    xxxx/sepolicy/private
 
然后添加:
xxxx/sepolicy/private/service_contexts
 
内容为:
test.service                               u:object_r:test_service:s0

编译selinux_policy,就可以看到这个context被追加到plat_service_contexts了

还有,在这个地方添加的service_contexts,并不是所有情况都没有作用

看看这两段mk:

# Include precompiled policy, unless told otherwise
ifneq ($(PRODUCT_PRECOMPILED_SEPOLICY),false)
LOCAL_REQUIRED_MODULES += precompiled_sepolicy precompiled_sepolicy.plat_and_mapping.sha256
endif
else
# The following files are only allowed for non-Treble devices.
LOCAL_REQUIRED_MODULES += \
    sepolicy \
    vendor_service_contexts
endif

vendor_service_contexts

include $(CLEAR_VARS)
 
LOCAL_MODULE := vendor_service_contexts
LOCAL_MODULE_CLASS := ETC
LOCAL_MODULE_TAGS := optional
LOCAL_MODULE_PATH := $(TARGET_ROOT_OUT)
 
include $(BUILD_SYSTEM)/base_rules.mk
 
vendor_svcfiles := $(call build_policy, service_contexts, $(PLAT_VENDOR_POLICY) $(BOARD_VENDOR_SEPOLICY_DIRS) $(REQD_MASK_POLICY))
 
vendor_service_contexts.tmp := $(intermediates)/vendor_service_contexts.tmp
$(vendor_service_contexts.tmp): PRIVATE_SVC_FILES := $(vendor_svcfiles)
$(vendor_service_contexts.tmp): PRIVATE_ADDITIONAL_M4DEFS := $(LOCAL_ADDITIONAL_M4DEFS)
$(vendor_service_contexts.tmp): $(vendor_svcfiles)
    @mkdir -p $(dir $@)
    $(hide) m4 -s $(PRIVATE_ADDITIONAL_M4DEFS) $(PRIVATE_SVC_FILES) > $@
 
$(LOCAL_BUILT_MODULE): PRIVATE_SEPOLICY := $(built_sepolicy)
$(LOCAL_BUILT_MODULE): $(vendor_service_contexts.tmp) $(built_sepolicy) $(HOST_OUT_EXECUTABLES)/checkfc $(ACP)
    @mkdir -p $(dir $@)
    sed -e 's/#.*$$//' -e '/^$$/d' $< > $@
    $(hide) $(HOST_OUT_EXECUTABLES)/checkfc -s $(PRIVATE_SEPOLICY) $@
 
built_vendor_svc := $(LOCAL_BUILT_MODULE)
vendor_svcfiles :=
vendor_service_contexts.tmp :=
 
endif

注释很清楚,也就是说在非treble的设备上,上面路径的service_contexts会编译到vendor_service_contexts里面;如果要生效,就得把它加到private的拓展目录

2.1.4、testNeverallowRules252 解决方案

<Test result="fail" name="testNeverallowRules252">
        <Failure message="junit.framework.AssertionFailedError: The following errors were encountered when validating the SELinuxneverallow rule:">
          <StackTrace>junit.framework.AssertionFailedError: The following errors were encountered when validating the SELinuxneverallow rule:
neverallow { domain -init -vendor_init -system_server -dumpstate } debugfs:file { { append create link unlink relabelfrom rename setattr write } open read ioctl lock };
libsepol.report_failure: neverallow violated by allow mediaserver debugfs:file { read open };
libsepol.report_failure: neverallow violated by allow hal_memtrack_default debugfs:file { read open };
libsepol.check_assertions: 2 neverallow failures occurred

报错信息如下:

[  818.411706@1] type=1400 audit(1623295203.581:48): avc: denied { read } for pid=3147 comm="HwBinder:3147_3" name="test_en" dev="debugfs" ino=377 scontext=u:r:mediaserver:s0 tcontext=u:object_r:debugfs:s0 tclass=file permissive=0

拿其中mediaserver的来分析,查找修改记录,很快就可以得知,这个是在读写节点 /sys/kernel/debug/test/test_en 的时候出现的权限问题

和上面的处理思路一样,我们要给这个节点指定一个安全上下文,不然就是权限放大的问题

但是和普通的文件不同,这个不是添加在file_contexts里面了,而是要添加在 genfs_contexts

file_contexts 里面的指定的文件,都是编译之后就有了的,而像 /sys/kernel/debug 、 /proc 这些是系统起来之后动态生成的,这些的节点文件的安全上下文就要在genfs_contexts里面指定的

genfscon debugfs /sys/kernel/debug/test/test_en u:object_r:debugfs_test:s0

这里很容易犯一个错误,就是直接把完整路径写到第三个字段里面去了

实际上,debugfs 指代的就是/sys/kernel/debug这个路径,后面要写的是相对于debugfs 的路径

因此,正确的写法是这样的:

genfscon debugfs /test/test_en u:object_r:debugfs_test:s0

当然,file.te里面还得定义debugfs_test这个type

type debugfs_test, fs_type, sysfs_type, debugfs_type;

修改之后是要编译precompiled_sepolicy的,而不是像file_contexts可以直接在板子上修改,重启之后报错如下:

[  818.411706@1] type=1400 audit(1623295203.581:48): avc: denied { read } for pid=3147 comm="HwBinder:3147_3" name="test_en" dev="debugfs" ino=377 scontext=u:r:mediaserver:s0 tcontext=u:object_r:debugfs_afc:s0 tclass=file permissive=0

再往下只要添加对应的权限就行了

2.1.5、exec_type

还有一个例子也是很重要的

[   10.583284] type=1400 audit(10.256:8): avc: denied { entrypoint } for pid=3233 comm="init" path="/vendor/bin/testA.sh" dev="mmcblk0p16" ino=712 scontext=u:r:testA:s0 tcontext=u:object_r:vendor_file:s0 tclass=file permissive=0

添加权限

allow testA vendor_file:file entrypoint;

结果违反了neverallow规则,然后继续干掉规则呗,这还不简单

FAILED: out/target/product/xxxx/obj/ETC/sepolicy_neverallows_intermediates/sepolicy_neverallows
/bin/bash -c "(rm -f out/target/product/xxxx/obj/ETC/sepolicy_neverallows_intermediates/sepolicy_neverallows ) && (ASAN_OPTIONS=detect_leaks=0 out/host/linux-x86/bin/checkpolicy -M -c           30 -o out/target/product/xxxx/obj/ETC/sepolicy_neverallows_intermediates/sepolicy_neverallows out/target/product/xxxx/obj/ETC/sepolicy_neverallows_intermediates/policy.conf )"
libsepol.report_failure: neverallow on line 376 of system/sepolicy/public/domain.te (or line 10221 of policy.conf) violated by allow testA vendor_file:file { entrypoint };
libsepol.check_assertions: 1 neverallow failures occurred

domain.te里面的neverallow规则:

# Ensure that all entrypoint executables are in exec_type or postinstall_file.
neverallow * { file_type -exec_type -postinstall_file }:file entrypoint;

正确的处理方式和前面提到的是一样的,也是同个道理

sepolicy/file_contexts
 
/vendor/bin/testA.sh        u:object_r:testA_exec:s0

这就是为什么我们之前添加脚本的时候,每次都要加这样一句话的原因

2.2、利用attributes绕开neverallow规则

这一类处理方式并不是从根源上解决,而是利用了规则的定义绕开了而已

8.0以前的不会有这样的一些规则,归根到底还是treble工程

2.2.1、testNeverallowRules184 解决方案

<Test result="fail" name="testNeverallowRules184">
        <Failure message="junit.framework.AssertionFailedError: The following errors were encountered when validating the SELinuxneverallow rule:">
          <StackTrace>junit.framework.AssertionFailedError: The following errors were encountered when validating the SELinuxneverallow rule:
neverallow {      domain      -coredomain      -appdomain      -binder_in_vendor_violators     } binder_device:chr_file { { getattr open read ioctl lock map } { open append write lock map } };
libsepol.report_failure: neverallow violated by allow testA binder_device:chr_file { ioctl read write open };
libsepol.check_assertions: 1 neverallow failures occurred
 
</StackTrace>
        </Failure>
      </Test>

利用属性 binder_in_vendor_violators 规避这个问题

type testA, domain;
修改为
type testA, domain, binder_in_vendor_violators;

原因很简单,binder_in_vendor_violators是一个属性,查看上面的neverallow规则就可以明白

2.2.3、testNeverallowRules185 解决方案

<Test result="fail" name="testNeverallowRules185">
        <Failure message="junit.framework.AssertionFailedError: The following errors were encountered when validating the SELinuxneverallow rule:">
          <StackTrace>junit.framework.AssertionFailedError: The following errors were encountered when validating the SELinuxneverallow rule:
neverallow {      domain      -coredomain      -appdomain       -binder_in_vendor_violators     } service_manager_type:service_manager find;
libsepol.report_failure: neverallow violated by allow testA activity_service:service_manager { find };
libsepol.check_assertions: 1 neverallow failures occurred

和上面的一样,利用属性 binder_in_vendor_violators 规避这个问题

type testA, domain;
修改为
type testA, domain, binder_in_vendor_violators;

这些属性之所以会存在,说明了Google并没有做到最完美的system和vendor分离,所有有这么一个像是后门的东西

还有一些:

  • vendor_executes_system_violators
  • data_between_core_and_vendor_violators
  • system_writes_vendor_properties_violators

2.3、vendor的脚本,在有权限的目录读写,不要操作system的

这类问题很明显,system和vendor分离之后才有的,没有啥好讲的,多看看报错信息和domain.te里面的注释,就能搞明白了

2.3.1、testNeverallowRules217 解决方案

<Test result="fail" name="testNeverallowRules217">
        <Failure message="junit.framework.AssertionFailedError: The following errors were encountered when validating the SELinuxneverallow rule:">
          <StackTrace>junit.framework.AssertionFailedError: The following errors were encountered when validating the SELinuxneverallow rule:
neverallow {          domain          -coredomain          -appdomain          -vendor_executes_system_violators          -vendor_init      } {          exec_type          -vendor_file_type          -crash_dump_exec          -netutils_wrapper_exec      }:file { entrypoint execute execute_no_trans };
libsepol.report_failure: neverallow violated by allow testA shell_exec:file { execute };
libsepol.check_assertions: 1 neverallow failures occurred
 
</StackTrace>
        </Failure>
      </Test>

首先,如果脚本在/vendor/bin下面,先将sh改过来

#!/system/bin/sh
 
修改为:
 
#!/vendor/bin/sh

以上能解决大部分问题,但是如果有些命令只有在/system/bin下面才有的,那这个时候就没有办法了,这个shell_exec:file 一定要加

此时,只能通过vendor_executes_system_violators 来规避问题了

type testA, domain, binder_in_vendor_violators;
 
修改为:
 
type testA, domain, binder_in_vendor_violators, vendor_executes_system_violators;

2.3.2、testNeverallowRules203 解决方案

<Test result="fail" name="testNeverallowRules203">
        <Failure message="junit.framework.AssertionFailedError: The following errors were encountered when validating the SELinuxneverallow rule:">
          <StackTrace>junit.framework.AssertionFailedError: The following errors were encountered when validating the SELinuxneverallow rule:
neverallow {      domain      -appdomain       -coredomain      -data_between_core_and_vendor_violators       -vendor_init    } {      core_data_file_type                        -zoneinfo_data_file    }:{ { chr_file blk_file } { file lnk_file sock_file fifo_file } } ~{ append getattr ioctl read write map };
libsepol.report_failure: neverallow violated by allow testA system_data_file:file { open };
libsepol.check_assertions: 1 neverallow failures occurred
 
</StackTrace>
        </Failure>
      </Test>

仔细看domain.te里面的注释,就能明白了

# vendor domains may only access files in /data/vendor, never core_data_file_types

把脚本里面的路径修改一下

TEMP_PATH="/data/test"
 
修改为:
 
TEMP_PATH="/data/vendor/test"

当然,有些neverallow规则还是可以利用属性绕过的(像data_between_core_and_vendor_violators),不过能正面解决就直接解决了

2.4、dac_override 问题

这个在前面以及提到过了,阅读一下参考文章就能明白了
解决这个问题从来不是添加SELinux处理的,必须要去解决unix user/group/others权限问题

2.5、读写/data数据

这里讨论下另一个解决方案:

2.5.1、testNeverallowRules237 另一种解决方案

<Test result="fail" name="testNeverallowRules237">
        <Failure message="junit.framework.AssertionFailedError: The following errors were encountered when validating the SELinuxneverallow rule:">
          <StackTrace>junit.framework.AssertionFailedError: The following errors were encountered when validating the SELinuxneverallow rule:
neverallow {   domain   -system_server   -system_app   -init   -installd    -vold_prepare_subdirs     } system_data_file:file { append create link unlink relabelfrom rename setattr write };
libsepol.report_failure: neverallow violated by allow testA system_data_file:file { write create setattr unlink };
libsepol.check_assertions: 1 neverallow failures occurred

再次看到这个很好奇,不是已经在上面的处理方法中解决了吗,修改读写的路径,改成有权限的地方就行了

这里再提供另一种解决思路,对于那些我们不方便修改代码的,这是一个福音

以下面的这个权限为例:

allow testA system_data_file:file { write create unlink };

这个权限添加的目的是为了读写、删除这个文件: /data/test.txt
问题就在于进程testA他没有直接权限读写啊

按照以前的思路:干掉规则 -testA

neverallow {
  domain
  -system_server
  -system_app
  -init
  -installd # for relabelfrom and unlink, check for this in explicit neverallow
  -vold_prepare_subdirs # For unlink
  -testA
  with_asan(`-asan_extract')
} system_data_file:file no_w_file_perms;

经过上面的处理经验,一个简单的方案出来了,把/data/test.txt 修改成/data/vendor/test.txt

然后权限拉满,就可以正常创建和删除了

allow testA vendor_data_file:dir create_dir_perms;
allow testA vendor_data_file:file create_file_perms;

下面讨论的是另一种解决方案:

data根目录,它所对应的安全上下文为:u:object_r:system_data_file:s0

drwxrwx--x  40 system system u:object_r:system_data_file:s0            4096 2021-06-10 16:09 data

如果创建一个文件 /data/test.txt, 它的安全上下文是继承于父目录的,也就是u:object_r:system_data_file:s0

所以在上面的报错会看到是对system_data_file类型的目录缺少写的权限

但是,如果有一种方式可以实现创建文件或者子目录的时候,不要继承父目录的scontext,改成一个拥有权限的type,那不就可以了嘛?

没错,还真的有这种操作,这个叫做type transition , 类型转移

在前面的基础文档里面已经有介绍了,我们直切主题,用宏 file_type_auto_trans

file_type_auto_trans(testA, system_data_file, vendor_data_file)
#####################################
# file_type_auto_trans(domain, dir_type, file_type)
# Automatically label new files with file_type when
# they are created by domain in directories labeled dir_type.
#
define(`file_type_auto_trans', `
# Allow the necessary permissions.
file_type_trans($1, $2, $3)
# Make the transition occur by default.
type_transition $1 $2:dir $3;
type_transition $1 $2:notdevfile_class_set $3;
')
 
意思就是,testA(第一个参数domain)这个type的进程,在system_data_file(第二个参数dir_type)类型的目录下面,创建文件时,指定它的type为vendor_data_file(第三个参数file_type)

同样的,这里同样要给vendor_data_file加权限

这一句将会发生如下变化:

console:/ # ls -lZ /data/test.txt
-rw------- 1 root system u:object_r:system_data_file:s0 5320 2021-06-10 17:06 /data/test.txt
 
变成
 
console:/ # ls -lZ /data/test.txt
-rw------- 1 root system u:object_r:vendor_data_file:s0 5320 2021-06-10 17:06 /data/test.txt

接着还有删除文件时需要下面的权限,这个正常添加就行了,下面的权限不会违反neverallow规则

[   67.886702@2] type=1400 audit(1623315964.145:18): avc: denied { remove_name } for pid=3142 comm="HwBinder:3142_2" name="test.txt" dev="mmcblk0p21" ino=1263 scontext=u:r:testA:s0 tcontext=u:object_r:system_data_file:s0 tclass=dir permissive=0

这种方式不一定适用于app,因为app里面还有其他neverallow规则会限制

它不是针对某个进程,而是针对的是一个域,这样会导致所有在这个域里面的进程都会创建出新的type的文件

三、一些思考

3.1、修改属性的neverallow规则,还能测试通过?

在/android/system/sepolicy/public/property.te 这一段,本来以为会有报错的,因为违反了neverallow规则

compatible_property_only(`
  # Neverallow coredomain to set vendor properties
  neverallow {
    coredomain
    -init
    -system_writes_vendor_properties_violators
    -system_server
    -platform_app
    -testA
  } {
    property_type
    -audio_prop
    -bluetooth_a2dp_offload_prop
    -bluetooth_prop
    -bootloader_boot_reason_prop
......
    -test_prop
  }:property_service set;
')

但实际上并没有报错,找到CTS测试的源码:

@RestrictedBuildTest
  public void testNeverallowRules436() throws Exception {
    String neverallowRule = "neverallow {      coredomain      -init      -system_writes_vendor_properties_violators    } {      property_type      -audio_prop      -bluetooth_a2dp_offload_prop      -bluetooth_prop      -bootloader_boot_reason_prop      -boottime_prop      -config_prop      -cppreopt_prop      -ctl_bootanim_prop      -ctl_bugreport_prop      -ctl_console_prop      -ctl_default_prop      -ctl_dumpstate_prop      -ctl_fuse_prop      -ctl_interface_restart_prop      -ctl_interface_start_prop      -ctl_interface_stop_prop      -ctl_mdnsd_prop      -ctl_restart_prop      -ctl_rildaemon_prop      -ctl_sigstop_prop      -ctl_start_prop      -ctl_stop_prop      -dalvik_prop      -debug_prop      -debuggerd_prop      -default_prop      -device_logging_prop      -dhcp_prop      -dumpstate_options_prop      -dumpstate_prop      -exported2_config_prop      -exported2_default_prop      -exported2_radio_prop      -exported2_system_prop      -exported2_vold_prop      -exported3_default_prop      -exported3_radio_prop      -exported3_system_prop      -exported_bluetooth_prop      -exported_config_prop      -exported_dalvik_prop      -exported_default_prop      -exported_dumpstate_prop      -exported_ffs_prop      -exported_fingerprint_prop      -exported_overlay_prop      -exported_pm_prop      -exported_radio_prop      -exported_secure_prop      -exported_system_prop      -exported_system_radio_prop      -exported_vold_prop      -exported_wifi_prop      -extended_core_property_type      -ffs_prop      -fingerprint_prop      -firstboot_prop      -hwservicemanager_prop      -last_boot_reason_prop      -log_prop      -log_tag_prop      -logd_prop      -logpersistd_logging_prop      -lowpan_prop      -mmc_prop      -net_dns_prop      -net_radio_prop      -netd_stable_secret_prop      -nfc_prop      -overlay_prop      -pan_result_prop      -persist_debug_prop      -persistent_properties_ready_prop      -pm_prop      -powerctl_prop      -radio_prop      -restorecon_prop      -safemode_prop      -serialno_prop      -shell_prop      -system_boot_reason_prop      -system_prop      -system_radio_prop      -test_boot_reason_prop      -traced_enabled_prop      -vendor_default_prop      -vendor_security_patch_level_prop      -vold_prop      -wifi_log_prop      -wifi_prop    }:property_service set;";
    boolean fullTrebleOnly = false;
    boolean compatiblePropertyOnly = true;
 
    if ((fullTrebleOnly) && (!isFullTrebleDevice()))
    {
      return;
    }
    if ((compatiblePropertyOnly) && (!isCompatiblePropertyEnforcedDevice()))
    {
      return;
    }
 
    File policyFile = (isSepolicySplit()) && (this.mVendorSepolicyVersion < 28) ?
      this.deviceSystemPolicyFile :
      this.devicePolicyFile;
 
    ProcessBuilder pb = new ProcessBuilder(new String[] { this.sepolicyAnalyze.getAbsolutePath(), policyFile
      .getAbsolutePath(), "neverallow", "-w", "-n", neverallowRule });
 
    pb.redirectOutput(ProcessBuilder.Redirect.PIPE);
    pb.redirectErrorStream(true);
    Process p = pb.start();
    p.waitFor();
    BufferedReader result = new BufferedReader(new InputStreamReader(p.getInputStream()));
 
    StringBuilder errorString = new StringBuilder();
    String line;
    while ((line = result.readLine()) != null) {
      errorString.append(line);
      errorString.append("\n");
    }
    assertTrue("The following errors were encountered when validating the SELinuxneverallow rule:\n" + neverallowRule + "\n" + errorString, errorString
      .length() == 0);
  }

代码是在这里被return了,也就是没有做检查,所以测试结果是pass的

if ((compatiblePropertyOnly) && (!isCompatiblePropertyEnforcedDevice()))
{
  return;
}
 
 
//isCompatiblePropertyEnforcedDevice拿的是系统属性,从板子里面看了看,这个值是false
public static boolean isCompatiblePropertyEnforcedDevice(ITestDevice device)
    throws DeviceNotAvailableException
{
  return PropertyUtil.propertyEquals(device, "ro.actionable_compatible_property.enabled", "true");
}

简单追了一下这里面的流程,首先compatible_property_only是一个宏,定义如下

# Compatible property only
# SELinux rules which apply only to devices with compatible property
#
define(`compatible_property_only', ifelse(target_compatible_property, `true', $1,
ifelse(target_compatible_property, `cts',
# BEGIN_COMPATIBLE_PROPERTY_ONLY -- this marker is used by CTS -- do not modify
$1
# END_COMPATIBLE_PROPERTY_ONLY -- this marker is used by CTS -- do not modify
, )))

值来源于target_compatible_property,这个值是前面提到的transform-policy-to-conf里面的变量传递进来的

-D target_compatible_property=$(PRIVATE_COMPATIBLE_PROPERTY)

继续,值是从PRODUCT_COMPATIBLE_PROPERTY来的

PRIVATE_COMPATIBLE_PROPERTY := $(PRODUCT_COMPATIBLE_PROPERTY)

往下

/android/build/make/core/config.mk
 
# Boolean variable determining if the whitelist for compatible properties is enabled
PRODUCT_COMPATIBLE_PROPERTY := false
ifneq ($(PRODUCT_COMPATIBLE_PROPERTY_OVERRIDE),)
  PRODUCT_COMPATIBLE_PROPERTY := $(PRODUCT_COMPATIBLE_PROPERTY_OVERRIDE)
else ifeq ($(PRODUCT_SHIPPING_API_LEVEL),)
  #$(warning no product shipping level defined)
else ifneq ($(call math_lt,27,$(PRODUCT_SHIPPING_API_LEVEL)),)
  PRODUCT_COMPATIBLE_PROPERTY := true
endif

PRODUCT_COMPATIBLE_PROPERTY 的值默认为false,如果有PRODUCT_COMPATIBLE_PROPERTY_OVERRIDE,则用这个

然后是PRODUCT_SHIPPING_API_LEVEL

也就是说,PRODUCT_COMPATIBLE_PROPERTY 最后的值为false

所以前面的neverallow规则不会生效

再看看属性ro.actionable_compatible_property.enabled是怎么来的?

# Sets ro.actionable_compatible_property.enabled to know on runtime whether the whitelist
# of actionable compatible properties is enabled or not.
ifeq ($(PRODUCT_ACTIONABLE_COMPATIBLE_PROPERTY_DISABLE),true)
ADDITIONAL_DEFAULT_PROPERTIES += ro.actionable_compatible_property.enabled=false
else
ADDITIONAL_DEFAULT_PROPERTIES += ro.actionable_compatible_property.enabled=${PRODUCT_COMPATIBLE_PROPERTY}
endif

PRODUCT_ACTIONABLE_COMPATIBLE_PROPERTY_DISABLE 是空的没有定义,也是最后生成的值是false

追查了代码,发现原来以前BoardConfig.mk里面是有定义的

PRODUCT_SHIPPING_API_LEVEL := 28

但是后面居然把这个去掉了?

PRODUCT_SHIPPING_API_LEVEL 这个会影响到一些neverallow规则

还会影响到这个属性ro.product.first_api_level的生成

3.2、抽离业务场景需求的公共权限

或许我们会有一个疑问,就是为什么不把所有自己的脚本都用同一个安全上下文呢?

这的确是可行的方案,可以更加便捷的完成功能需求,但是从权限管控的初衷来看,分开了之后能够达到权限最小化;

在上面的思路基础上,引发了另一个想法:
定义一个自定义的属性,然后添加脚本的时候,关联上这个属性,就拥有了对应的权限了

比如:

attribute testdomain;

testdomain.te里面定义一些公共的权限,比如:

allow testdomain console_device:chr_file rw_file_perms;
allow testdomain vendor_toolbox_exec:file { execute_no_trans };
allow testdomain vendor_data_file:dir create_dir_perms;
allow testdomain vendor_data_file:file create_file_perms;

然后在对应的type中关联属性

type testA, domain, testdomain;

这只是一个目前的想法~

3.3 其他

对于第三方apk的权限问题,比如:

目前这个在系统端没有找到解决方案,只能从apk实现上入手了

[   97.450511@1] type=1400 audit(1623116538.872:18): avc: denied { create } for pid=3953 comm="testB" name="shpex98m.tmp" scontext=u:r:testB:s0 tcontext=u:object_r:system_data_file:s0 tclass=file permissive=0

这个是绕不过neverallow规则的

[   45.533219@3] type=1400 audit(1623985073.895:7): avc: denied { execute } for pid=4052 comm="bootloader" path="/data/data/com.xxxx.xxx/files/test/test.so" dev="mmcblk0p21" ino=1085 scontext=u:r:system_app:s0 tcontext=u:object_r:system_app_data_file:s0 tclass=file permissive=0

可以看看下面这个文章:
https://dongdaima.com/article/54042

到这里,基本上我所了解的部分就介绍完了,总结来说,多看看代码,报错信息以及te文件里面的注释,能学到很多东西

能不动到原生的部分,就不要修改

四、参考文章

dac_override

android 用户组

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值