在用户态部署 SELinux
SELinux 架构图(源自 SELinux Notebook ):
正如此文 所述:
There can only be one Security Server, which resides in the kernel. However, the AVCs and OMs can reside both in the kernel and in userspace.
- Security Server 即二进制策略储存处,内核 LSM 中又称 policydb,有且仅有一份,处于内核中;
- Access Vector Cache(AVC)缓存 Security Server 对近期一些访问行为的决定,既可在内核态,又可在用户态;
- Object Manager 实施对客体访问的管理(阻拦或放行),既可在内核态,又可在用户态。
关于使用用户态的 AVC 和 OM,可参考 libselinux 中提供的 selinux_check_access()
函数
(man 3 selinux_check_access
):
1selinux_check_access
2 avc_has_perm
3 avc_has_perm_noaudit
4 // search in avc first, and if fails, ...
5 avc_lookup
6 avc_search_node
7 hvalue = avc_hash(ssid, tsid, tclass);
8 cur = avc_cache.slots[hvalue] // avc_cache: global var in userspace
9 // ... query security server in kernel
10 security_compute_av_flags_raw
11 snprintf(path, sizeof path, "%s/access", selinux_mnt);
12 fd = open(path, O_RDWR | O_CLOEXEC);
13 write(fd, buf, strlen(buf))
14 read(fd, buf, len - 1)
15 avc_insert
可以看到,上述流程用到了用户态的 avc_cache
,若查询不到对访问的判定,则通过 selinuxfs 的 /access
接口查询,随后将结果缓存至 AVC 中。
另外,关于用户态 AVC 还有一个重点:在使用用户态 AVC 之前必须先用 avc_open()
对其进行初始化,而此刻可以通过其入参自定义用户态 AVC 的 enforcing/permissive 状态,将其设置得与内核态的不一致。
关于这部分的源码:
avc_enforcing
:用户态的 enforcing/permissive 状态;avc_setenforce
:用户态是否自行设置了状态
用户态判定 SELinux 是否使能的接口:is_selinux_enabled()
。其判定的方法:
- selinuxfs 是否挂载
- 通过
init_selinuxmnt()
在 so 加载的时候执行 - 内核态动态关闭 SELinux 时会将 selinuxfs umount 掉
- 通过
/etc/selinux/config
是否存在
关于 File Contexts
file_contexts
文件仅定义“路径->标签”的映射关系,反映了当前这套(广义)SELinux 策略给各个路径赋予的标签。其本身不属于(狭义)二进制策略文件的一部分;换言之,内核并不感知该文件,因此在创建某个文件/路径并需要为其打标签时,内核无法查询到承载在该文件中的,该文件/路径所对应的标签。实际上,各文件及路径的“正确”标签需要通过刷新获得,而不是一创建出来就是对的。举例说明:/data/file
被创建后,其默认会先继承父目录 /data
的标签,但这个标签不一定符合 file_contexts
对 /data/file
的规定;需要执行 restorecon
后,/data/file
才能从 file_contexts
中获取其正确的标签。
可参考 OpenHarmony 中 policycoreutils 的 restorecon 实现 ,其中用到了多个 libselinux 用户态库的函数:
selabel_open()
selinux_restorecon_set_sehandle()
selinux_restorecon_parallel(path, flags, nthreads)
__selinux_once(once_control, func)
(见 libselinux/src/selinux_internal.h 源码)
/(.*)?
与 (/.*)?
的区别
file_contexts
中常见的两种正则表达式写法,语义上有些许区别:
/data/data/(.*)? u:object_r:data_data_file:s0
表示/data/data/
目录下的子文件和子目录标签为data_data_file
,不包括/data/data
本身/data/data(/.*)? u:object_r:data_data_file:s0
表示/data/data/
目录本身及该目录下的子文件和子目录标签为data_data_file
,包括/data/data
本身
进程修改自己的 SELinux 标签
libselinux 对外提供了 setcon()
API,由此回答
得知,其在 libselinux/src/procattr.c
中通过 all_selfattr_def(con, current)
宏展开定义。其最终实质是写进程的 /proc/<pid>/attr/current
接口。注意:需要 dyntransition
的 SELinux 权限。
Android 上 SELinux 策略构建流程
1各策略文件.te ---(m4)--> 总策略文件.conf ---(checkpolicy)--> .cil ---(secilc)--> policy.31
2 ^^^^^^ 检查 neverallow,加 `-N` 不检查