起因:
错误log:
ERROR: permissive domains not allowed in user builds" 1>&2;
起因:在进一笔sepolicy代码的时候,报出的build break,从log也能很明显的看出,有一个domain,因为添加了permissive关键字而在user版本上导致的编译错误。
思索:为什么要在user版本上禁止使用permissive标签,在翻了资料后发现为了开发和debug的需要,加上permissive关键字后,可以将该domain的一些denied log打印出来,这样就方便了一次将所有的权限添加。这样做的方式了避免了那些开机启动的应用程序没有权限,有时候log太快被冲掉的问题。而且这个好处也避免了让整台机器都处于permissive的非安全状态。
使用:
- 在te文件中添加
permissive myapp_t;
- 动态添加
// 添加 semanager permissive -a myapp_t // 删除 semanager permissive -d myapp_t
错误跟踪链:
总结: 在编译bootimage的时候,sepolicy编译出来后,会进行一些条件的check,而permissivedomain的check也就在其中。
log出处:
// system/sepolicy/Android.mk $(built_sepolicy_neverallows) @mkdir -p $(dir $@) $(hide) $< -m -M true -G -c $(POLICYVERS) $(PRIVATE_NEVERALLOW_ARG) $(PRIVATE_CIL_FILES) -o $@.tmp -f /dev/null $(hide) $(HOST_OUT_EXECUTABLES)/sepolicy-analyze $@.tmp permissive > $@.permissivedomains // 1 $(hide) if [ "$(TARGET_BUILD_VARIANT)" = "user" -a -s $@.permissivedomains ]; then \ //2 echo "==========" 1>&2; \ echo "ERROR: permissive domains not allowed in user builds" 1>&2; \ echo "List of invalid domains:" 1>&2; \ cat $@.permissivedomains 1>&2; \ exit 1; \ fi $(hide) mv $@.tmp $@
首先看到1处,调用sepolicy-analyze来对之前创建出的tmp文件进行分析,将结果保存在permissivedomain中。
紧接着2处进行判断,如果当前的目标版本是user,并且有permissivedomain这个文件,那么就说明已经违反了规定,将cat的结果打印到终端,同时exit,将整个打包进行失败。sepolicy-analyze内容
上面说到在makefile中的进行分析,如果有分析结果就说明是错误了,那么分析的内容是什么呢?
// system/sepolicy/tools/sepolicy-analyze/sepolicy-analyze.c int main(int argc, char **argv) { char *policy; struct policy_file pf; policydb_t policydb; int rc; int i; if (argc < 3) usage(argv[0]); policy = argv[1]; if(load_policy(policy, &policydb, &pf)) exit(1); for(i = 0; i < NUM_COMPONENTS; i++) { if (!strcmp(analyze_components[i].key, argv[2])) { // 1 rc = analyze_components[i].func(argc - 2, argv + 2, &policydb);//2 if (rc && USAGE_ERROR) { usage(argv[0]); } policydb_destroy(&policydb); return rc; } } usage(argv[0]); exit(0); }
从上面的分析,我们可以知道,第一个参数是文件,而第二个参数是permissive。1处的意思在这里一个for循环来找到对应的策略,也是就permissive的策略:
#define COMP(x) { #x, sizeof(#x) - 1, x ##_usage, x ##_func } static struct { const char *key; size_t keylen; void (*usage) (void); int (*func) (int argc, char **argv, policydb_t *policydb); } analyze_components[] = { COMP(dups), COMP(neverallow), COMP(permissive), COMP(typecmp), COMP(booleans), COMP(attribute) };
数组中的permissive简化下来:
static struct { const char *key = "permissive"; size_t keylen = sizeof("permissive"); void permissive_usage (); void permissive_func (int argc , char ** argv, policydb_t *policydb); }
所以2处调用的就是permissive_func ```c // system/sepolicy/tools/sepolicy-analyze/perm.c static int list_permissive(policydb_t * policydb) { struct ebitmap_node *n; unsigned int bit;
/*
- iterate over all domains and check if domain is in permissive */ ebitmap_for_each_bit(&policydb->permissive_map, n, bit) { if (ebitmap_node_get_bit(n, bit)) { printf(“%s\n”, policydb->p_type_val_to_name[bit -1]); } } return 0; }
int permissive_func (int argc, attribute ((unused)) char **argv, policydb_t *policydb) { if (argc != 1) { USAGE_ERROR = true; return -1; } return list_permissive(policydb); } ``` 到这里也就很清晰,通过for循环来寻找文件找到permissive的domain。因为将print的结果都重定向到文件中,所以$@.permissivedomain中就有了对应的permissive的domain。
总结:
permissive domain是用来帮助开发者进行开发以及debug的工具,用来一次性查看需要用到的权限,并且可以通过工具生成对应的规则,而permissive domain在设计中更多的被看做是一个临时的状态,而不是一个最终的状态。所以在user版本也就是发型版本中是不能够出现permissive的。而我们应该遵循这样的设计逻辑,不要在user版本中permissivedomain,只在开发和debug阶段添加对应逻辑方便调试。
参考资料