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
大

本文详细介绍了Android PSELinux的权限管理,包括基础概念、开机初始化、权限检查原理、CTS测试处理和一些常见的解决方案。重点讨论了区分vendor和system域、setenforce的可行性、DAC_OVERRIDE的处理、echo打印到终端、读写U盘内容、am命令和ioctl等场景的处理方法,并分析了如何避免违反neverallow规则。同时,提出了对权限策略的思考,如利用属性规避权限放大问题和定义公共权限。
最低0.47元/天 解锁文章
2250





