Android P SELinux (一) 基础概念
Android P SELinux (二) 开机初始化与策略文件编译过程
Android P SELinux (三) 权限检查原理与调试
Android P SELinux (四) CTS neverallow处理总结
目录
引子
我们在处理SELinux权限问题的时候,avc denied信息是最关键的,那么avc denied 的打印信息要怎么看、里面的内容每一个字段是什么意思?kernel log的avc denied信息是哪里打印出来的?
以前有个想法,我们系统的操作、命令都是有限的,那其实脚本写完之后需要什么权限都是确定的,那可不可能更快的加好te规则呢?
带着上面的一些疑问,接着在代码想找到是怎么检查权限的,下文会简单介绍一些目前了解到的知识:
假设有如下脚本以及te规则
- /vendor/bin/testA.sh
#!/vendor/bin/sh
echo "hello world" > /data/vendor/test.txt
- /android/device/xxxx/common/sepolicy/file_contexts
/vendor/bin/testA.sh u:object_r:testA_exec:s0
- /android/device/xxxx/common/sepolicy/testA.te
type testA, domain;
type testA_exec, exec_type, vendor_file_type, file_type;
init_daemon_domain(testA)
allow testA console_device:chr_file {
write open getattr ioctl };
allow testA vendor_toolbox_exec:file {
execute_no_trans };
allow testA vendor_data_file:dir {
add_name };
执行脚本后,会有如下报错信息:
[ 5424.996583@0]- type=1400 audit(1577887433.668:59): avc: denied {
write } for pid=4021 comm="testA.sh"
name="vendor" dev="mmcblk0p20" ino=7395 scontext=u:r:testA:s0 tcontext=u:object_r:vendor_data_file:s0 tclass=dir permissive=0
一、权限检测原理
这部分的代码集中在两个部分
- kernel的代码,里面的security/selinux
- /android/external/selinux
一开始,/data/vendor/test.txt是不存在的;testA.sh脚本运行起来之后,它的安全上下文是u:r:testA:s0,type为testA的进程,要拥有对type为vendor_data_file的目录(dir)的写(write)权限,才能创建一个不存在的文件
权限检测其实就是针对进程访问对象的两个权限:拥有的权限和需要的权限,将两者进行计算得出结果,即允许还是拒绝
1、拥有的权限
te规则定义的权限保存在哪里?
testA.te里面它拥有add_name权限,前面的文章有提到te策略文件的编译过程,这个规则最终会生成在二进制策略文件precompiled_sepolicy里面,而policydb这个数据结构是用来组织起所有数据的
其中te规则保存在这个成员里面
/* type enforcement access vectors and transitions */
avtab_t te_avtab;
这里面的数据结构比较复杂,里面主要需要理解几个数据结构:
- 位图ebitmap
- 链表
- 哈希表hashtab
- 符号表symtab
这里面的内容太多了,我只是简单分析了其中te规则一部分代码,有兴趣可以结合参考文章《Linux多安全策略和动态安全策 略框架模块代码分析报告》(见参考文章)来阅读源码深入了解
下面分析的一些结论,如有问题请指出:
testA这个type的进程,对type为vendor_data_file的dir(目录)所拥有的权限是用一个整数u32来表示的:
它的值是:
0x00120010
下面简单看看这个值是怎么生成的?
(allow testA_28_0 vendor_data_file_28_0 (dir (add_name)))
接着看看 /android/external/selinux/libsepol/cil/src/cil_binary.c 里面的__cil_perms_to_datum函数
int __cil_perms_to_datum(struct cil_list *perms, class_datum_t *sepol_class, uint32_t *datum)
{
int rc = SEPOL_ERR;
char *key = NULL;
struct cil_list_item *curr_perm;
struct cil_perm *cil_perm;
uint32_t data = 0;
cil_list_for_each(curr_perm, perms) {
cil_perm = curr_perm->data;
key = cil_perm->datum.fqn;
rc = __perm_str_to_datum(key, sepol_class, &data);
if (rc != SEPOL_OK) {
goto exit;
}
}
*datum = data;
return SEPOL_OK;
exit:
return rc;
}
int __perm_str_to_datum(char *perm_str, class_datum_t *sepol_class, uint32_t *datum)
{
int rc;
perm_datum_t *sepol_perm;
common_datum_t *sepol_common;
sepol_perm = hashtab_search(sepol_class->permissions.table, perm_str);
if (sepol_perm == NULL) {
sepol_common = sepol_class->comdatum;
sepol_perm = hashtab_search(sepol_common->permissions.table, perm_str);
if (sepol_perm == NULL) {
cil_log(CIL_ERR, "Failed to find datum for perm %s\n", perm_str);
rc = SEPOL_ERR;
goto exit;
}
}
*datum |= 1 << (sepol_perm->s.value - 1);
return SEPOL_OK;
exit:
return rc;
}
datum 里面保存的值就是0x00120010
看最关键的一句话:
*datum |= 1 << (sepol_perm->s.value - 1);
上面的操作其实就是将对应的位置1,代表它拥有的权限,那每一个位对应的权限是什么呢?
这个要看:
/android/system/sepolicy/private/access_vectors
common file
{
ioctl
read
write
create
getattr
setattr
lock
relabelfrom
relabelto
append
map
unlink
link
rename
execute
quotaon
mounton
}
class dir
inherits file
{
add_name
remove_name
reparent
search
rmdir
open
audit_access
execmod
}
sepol_perm->s.value可以理解成是解析过程中某个权限在access_vectors定义的class里面的index值,它会用符号表和对应的字符串关联起来,比如add_name在集合里面的是第18个,对应的值是0x00020000
我们这里的是dir,将其合并下:
{
ioctl,read,write,create,getattr,setattr,lock,relabelfrom,relabelto,append,map,unlink,
link,rename,execute,quotaon,mounton,add_name,remove_name,reparent,search,rmdir,open,
audit_access,execmod
}
上面那句话的操作就是将datum的第18位置成1
那么最后计算出来的0x00120010就是:

翻译过来,是这个规则
allow testA vendor_data_file:dir {
search add_name getattr };
奇怪的是,这里怎么会多出来两个权限呢,我都没有加上去
原因是来自这条规则:
这个在domain.te里面
allow domain vendor_data_file:dir {
getattr search };
2、需要的权限
要分析操作vendor_data_file:dir所需要的权限,借助打印堆栈信息来分析:
[ 5424.899991@1]d CPU: 1 PID: 4119 Comm: testA.sh Tainted: P O 4.9.113 #14
[ 5424.907561@1]d Hardware name: Generic DT based system
[ 5424.912595@1]d [bc3c7a84+ 16][<c020dc10>] show_stack+0x20/0x24
[ 5424.918447@1]d [bc3c7aa4+ 32][<c05443ec>] dump_stack+0x90/0xac
[ 5424.924311@1]d [bc3c7aec+ 72][<c04d5954>] slow_avc_audit+0x88/0xa8
[ 5424.930515@1]d [bc3c7b34+ 72][<c04da498>] audit_inode_permission+0x80/0x88
[ 5424.937417@1]d [bc3c7bac+ 120][<c04daf20>] selinux_inode_permission+0x2d0/0x314
[ 5424.944664@1]d [bc3c7bcc+ 32][<c04d1f44>] security_inode_permission+0x4c/0x68
[ 5424.951824@1]d [bc3c7bec+ 32][<c03a5bc8>] __inode_permission2+0x50/0xf0
[ 5424.958455@1]d [bc3c7bfc+ 16][<c03a5cb0>] inode_permission2+0x20/0x54
[ 5424.964930@1]d [bc3c7c84+ 136][<c03a9048>] path_openat+0xb30/0x118c
[ 5424.971137@1]d [bc3c7d2c+ 168][<c03aabe4>] do_filp_open+0x7c/0xe0
[ 5424.977179@1]d [bc3c7d7c+ 80][<c0397b34>] do_sys_open+0x124/0x238
[ 5424.983303@1]d [bc3c7d8c+ 16][<c0397c90>] SyS_openat+0x1c/0x20
[ 5424.989165@1]d [00000000+ 0][<c02085c0>] ret_fast_syscall+0x0/0x48
用addr2line看看对应的代码调用逻辑
1 show_stack c020dc10
show_stack
kernelcode/arch/arm/kernel/traps.c:263
2 dump_stack c05443ec
dump_stack
kernelcode/lib/dump_stack.c:53
3 slow_avc_audit c04d5954
slow_avc_audit
kernelcode/security/selinux/avc.c:775
4 audit_inode_permission c04da498
audit_inode_permission
kernelcode/security/selinux/hooks.c:3005
5 selinux_inode_permission c04daf20
selinux_inode_permission
kernelcode/security/selinux/hooks.c:3058
6 security_inode_permission c04d1f44
security_inode_permission
kernelcode/security/security.c:611 (discriminator 5)
7 __inode_permission2 c03a5bc8
__inode_permission2
kernelcode/fs/namei.c:436
8 inode_permission2 c03a5cb0
inode_permission2
kernelcode/fs/namei.c:486
9 path_openat c03a9048
may_o_create
kernelcode/fs/namei.c:3018
10 do_filp_open c03aabe4
do_filp_open
kernelcode/fs/namei.c:3569
11 do_sys_open c0397b34
do_sys_open
kernelcode/fs/open.c:1073
12 SyS_openat c0397c90
SyS_openat
kernelcode/fs/open.c:1093
13 ret_fast_syscall c02085c0
ret_fast_syscall
kernelcode/arch/arm/kernel/entry-common.S:37
堆栈打印添加在kernel代码里面:
kernelcode$ git diff security/selinux/avc.c
diff --git a/security/selinux/avc.c b/security/selinux/avc.c
index e60c79d..79cea20 100644
--- a/security/selinux/avc.c
+++ b/security/selinux/avc.c
@@ -771,7 +771,7 @@ noinline int slow_avc_audit(u32 ssid, u32 tsid, u16 tclass,
sad.result = result;
a->selinux_audit_data = &sad;
-
+ dump_stack();
common_lsm_audit(a, avc_audit_pre_callback, avc_audit_post_callback);
return 0;
}
跟着堆栈简单看一遍代码,不难发现,需要的权限是从这里来的
may_o_create
kernelcode/fs/namei.c:3018
static int may_o_create(const struct path *dir, struct dentry *dentry, umode_t mode)
{
struct user_namespace *s_user_ns;
int error = security_path_mknod(dir, dentry, mode, 0);
if (error)
return error;
s_user_ns = dir->dentry->d_sb->s_user_ns;
if (!kuid_has_mapping(s_user_ns, current_fsuid()) ||
!kgid_has_mapping(s_user_ns, current_fsgid()))
return -EOVERFLOW;
error = inode_permission2(dir->mnt, dir->dentry->d_inode, MAY_WRITE | MAY_EXEC);
if (error)
return error;
return security_inode_create(dir->dentry->d_inode, dentry, mode);
}
inode_permission2的第三个参数 MAY_WRITE | MAY_EXEC
会一直传递到 kernelcode/security/selinux/hooks.c
selinux_inode_permission这个函数的第二参数mask
static int selinux_inode_permission(struct inode *inode, int mask)
{
const struct cred *cred = current_cred();
u32 perms;
bool from_access;
unsigned flags = mask &</

本文详细解析了Android P的SELinux权限机制,包括权限检测原理、avc_denied信息解读、调试方法以及如何利用宏和工具如setools优化规则。通过实例演示了权限检查流程,提供了编译和策略文件管理的技巧,适合深入理解Android权限控制。
2180

被折叠的 条评论
为什么被折叠?



