关于 SELinux 的各种理论知识

在用户态部署 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 用户态库的函数:

/(.*)?(/.*)? 的区别

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` 不检查

Linux 上进程 Capabilities 可能变化的场景
Vim: Search for What You Select