在Linux系统上应用SELinux可能会遇到各种各样的问题,其中之一就是文件标签不生效。开发者可能定义了一个自己的标签,如“/dev/mmcblk0part1 u:object_r:rootdev:s0”,但系统运行起来之后查看标签内容却是“u:object_r:unlabeled:s0”,具体原因需要结合系统实际情况进行分析。本文介绍两种unlabeled标签产生的场景及原因,并给出相关的解决方案供大家参考。

第一种:分区没打标签

忘记给分区打标签的场景理论上不太可能出现,比如系统中常用的/data、/app及/system等分区,其中的只读分区在编译时即完成了打标签的动作,/data这种可读写的分区在系统启动的过程中也会动态执行打标签的动作。但oem对Linux进行定制化开发的过程是十分复杂的,系统工程师很有可能划定了一到两个特殊用途的分区,这种分区没有对应的镜像文件,即编译时不会打包分区,而是在系统启动过程之中完成分区初始化,并且这种初始化是一次性的,之后由对应的服务启动后执行分区的挂载动作。

对于如上场景提到的特殊用途分区,如果在分区挂载之后没有执行打标签的任务,那么分区下所有文件的标签将会以“u:object_r:unlabeled:s0”的形式呈现,即使开发者已经在file_contexts文件中对分区文件定义了上下文。对分区文件的访问将会生成如下形式的avc日志:

avc:  denied  { read } for  pid=1317 comm=\"ua-service\" name=\"edprv\" dev=\"mmcblk0part0\" ino=14 scontext=u:r:ua-service:s0 tcontext=u:object_r:unlabeled:s0 tclass=file permissive=1

这种场景下对应的解决方法比较简单,即在分区挂载之后使用setfiles给分区动态打标签。但这种场景还包含一种更加复杂的细分场景,会导致即使使用setfiles也无法完成打标签的动作。注意在我们遇到的这个场景下,有两个需要关注的重点问题:

1、分区的格式化是一次性的,即在开发者的研发周期内,如果分区没有因出现损坏需要重新格式化,那么这个分区可能一直不会被二次格式化;

2、通过内核config使能SELinux的动作,可能发生在开发进行到一定阶段时进行。

因此,如果分区的格式化早于SELinux的使能,那意味着在编译内核时,SELinux相关属性CONFIG_SECURITY是未配置状态,因此内核文件系统inode结构中并不包含i_security字段(如下代码段所示),进一步导致格式化分区时,写入分区磁盘的inode实际上也不包含i_security字段。

缺少i_security字段需要我们重点关注的原因在于,selinux文件上下文标签存储在i_security字段当中,如果分区磁盘inode结构不包含该字段,那么当内核文件系统inode更新到磁盘时,i_security字段是不会存储到磁盘的。因此在这种场景下,通过setfiles主动给分区设置标签之后,标签能够成功更新到内核文件系统,此时查看标签是生效的,一旦重启系统之后,标签便会消失。

第二种:旧标签未更新

Linux v5.1中发布了一项SELinux的更新,内容如下 [1]:

In case a file has an invalid context set, in an AVC record generated
upon access to such file, the target context is always reported as
unlabeled. This patch adds new optional fields to the AVC record
(srawcon and trawcon) that report the actual context string if it
differs from the one reported in scontext/tcontext. This is useful for
diagnosing SELinux denials involving invalid contexts.

To trigger an AVC that illustrates this situation:

    # setenforce 0
    # touch /tmp/testfile
    # setfattr -n security.selinux -v system_u:object_r:banana_t:s0 /tmp/testfile
    # runcon system_u:system_r:sshd_t:s0 cat /tmp/testfile

AVC before:

type=AVC msg=audit(1547801083.248:11): avc:  denied  { open } for  pid=1149 comm="cat" path="/tmp/testfile" dev="tmpfs" ino=6608 scontext=system_u:system_r:sshd_t:s0 tcontext=system_u:object_r:unlabeled_t:s15:c0.c1023 tclass=file permissive=1

AVC after:

type=AVC msg=audit(1547801083.248:11): avc:  denied  { open } for  pid=1149 comm="cat" path="/tmp/testfile" dev="tmpfs" ino=6608 scontext=system_u:system_r:sshd_t:s0 tcontext=system_u:object_r:unlabeled_t:s15:c0.c1023 trawcon=system_u:object_r:banana_t:s0 tclass=file permissive=1

大概意思就是,如果SELinux标签无效,那么在输出审计日志时,会顺带着以srawcon或trawcon为key值输出无效标签的值,如下日志所示:

avc:  denied  { read } for  pid=5310 comm=\"sh\" name=\"/\" dev=\"dm-0\" ino=2 scontext=u:r:lbjserver:s0 tcontext=u:object_r:unlabeled:s0 tclass=dir permissive=1 trawcon=\"u:object_r:atif_file:s0\"

那么什么情况下会出现这种标签无效的场景呢?当内核文件系统存储的标签,与/etc/selinux下定义的标签不一致时(标签未定义),文件系统中的标签会被视为无效。因此,要出现这种情况,分区的标签要么是分区初始化时打上,并且以后系统启动过程中没有动态给分区打标签,或者是单独给分区刷了个image,而image中文件标签的值在当前系统上未定义。

这种场景下的解决方式与第一种类似,只需要系统在每次启动过程中,主动执行setfiles更新分区标签即可。

[1]. [v2] selinux: log invalid contexts in AVCs

Logo

更多推荐