Skip to content

Files

Latest commit

481eb0e · Dec 26, 2021

History

History
633 lines (409 loc) · 29.2 KB

File metadata and controls

633 lines (409 loc) · 29.2 KB

八、将上下文应用于文件

在最后一章中,我们升级了系统,收集了审计日志,并开始分析审计记录。我们发现文件系统上的一些对象没有标记。在本章中,我们将:

  • 了解文件系统和文件系统对象如何获得它们的标签
  • 演示更改标签的技巧
  • 引入用于标注的扩展属性
  • 调查文件上下文和动态类型转换

标记文件系统

Linux 上的文件系统源自 mount,安卓上的ramdisk rootfs除外。Linux 上的文件系统差异很大。一般来说,为了支持 SELinux 的所有特性,您需要一个支持xattrsecurity命名空间的文件系统。我们在设置内核配置时看到了这个需求。

文件系统对象在创建时,都以初始上下文开始,就像所有其他内核对象一样。文件上的上下文只是从它们的父级继承而来,所以如果父级没有标记,那么子级就没有标记,类型转换规则除外。通常,如果上下文未标记,则推断数据是在启用 SELinux 支持之前在文件系统上创建的,或者xattr中的类型标签不存在于当前加载的策略中。

初始标签或初始安全 id ( sid )在sepolicy文件initial_sid_contexts中。每个对象类都有其相关的初始sid存在。例如,让我们看一下下面的代码片段:

...
sid fs u:object_r:labeledfs:s0
sid file u:object_r:unlabeled:s0
...

fs_use

文件系统可以通过多种方式进行标记。最好的情况是当文件系统支持xattrs时。在这种情况下,fs_use_xattr语句应该出现在策略中。这些声明出现在sepolicy目录下的fs_use文件中。fs_use_xattr的语法是:

fs_use_xattr <fstype> <context>

sepolicyfs_use,我们可以参考一个ext4文件系统的例子:

...
fs_use_xattr ext3 u:object_r:labeledfs:s0;
fs_use_xattr ext4 u:object_r:labeledfs:s0;
fs_use_xattr xfs u:object_r:labeledfs:s0;
...

这告诉 SELinux 当它遇到一个ext4 fs对象时;查看标签或文件上下文的扩展属性。

fs_task_use

文件系统的另一种标记方式是在创建对象时使用进程的上下文。这对于伪文件系统很有意义,在伪文件系统中,对象实际上是进程上下文,例如pipefssockfs。这些伪文件系统管理管道和套接字系统调用,并不真正装载到用户空间。它们存在于内核内部,供内核使用。然而,它们确实有对象,并且像任何其他对象一样,它们需要被标记。这就是fs_task_use政策声明有意义的背景。这些内部文件系统只能由进程直接访问,并为这些进程提供服务。因此,给它们贴上造物主的标签是有意义的。语法如下:

fs_task_use <fstype> <context>

sepolicy文件fs_use中的示例如下:

...
# Label inodes from task label.
fs_use_task pipefs u:object_r:pipefs:s0;
fs_use_task sockfs u:object_r:sockfs:s0;
...

fs_use_trans

您可能希望在实际安装的伪文件系统上设置标签的下一种方式是使用fs_use_trans。这将在伪文件系统上设置一个文件系统范围的标签。其语法如下:

fs_use_trans <fstype> <context>

例从到sepolicy文件fs_use如下:

...
fs_use_trans devpts u:object_r:devpts:s0;
fs_use_trans tmpfs u:object_r:tmpfs:s0;
...

日内瓦会议

如果fs_use_*语句中没有符合您的用例,而符合vfat文件系统和procfs的用例,那么您将使用genfscon语句。为genfscon指定的标签适用于该文件系统挂载的所有实例。例如,您可能希望将genfscon用于vfat文件系统。如果你有两个vfat坐骑,他们会对每个坐骑使用相同的genfscon语句。但是,genfscon的行为与procfs不同,并且允许您标记文件系统中的每个文件或目录。

*genfscon的语法如下:

genfscon <fstype> <path> <context>

sepolicy genfs_contexts举例如下:

...
# Label inodes with the fs label.
genfscon rootfs / u:object_r:rootfs:s0
# proc labeling can be further refined (longest matching prefix).
genfscon proc / u:object_r:proc:s0
genfscon proc /net/xt_qtaguid/ctrl u:object_r:qtaguid_proc:s0
...

注意rootfs部分路径是/。不是procfs,所以不支持对其标注任何细粒度;所以/是你唯一能用的东西。然而,你可以对procfs变得狂野,并设置成你想要的任何粒度。

挂载选项

另一个选项,如果这些都不符合你的需要,是通过mount命令行传递context选项。这设置了一个文件系统范围的挂载上下文,例如genfscon,但是在需要有单独标签的多个文件系统的情况下很有用。例如,如果您安装了两个vfat文件系统,您可能希望分开访问它们。使用genfscon语句,两个文件系统将使用genfscon提供的相同标签。通过在装载时指定标签,可以用不同的标签装载两个vfat文件系统。

以下面的命令为例:

mount -ocontext=u:object_r:vfat1:s0 /dev/block1 /mnt/vfat1
mount -ocontext=u:object_r:vfat2:s0 /dev/block1 /mnt/vfat2

除了之外,作为挂载选项的上下文还有:fscontextdefcontext。这些选项在上下文中是互斥的。fscontext选项设置用于某些操作的元文件系统类型,如挂载,但不改变每个文件的标签。defcontext为覆盖initial_sid语句的未标记文件设置默认上下文。最后,另一个选项rootcontext允许您在文件系统中设置根信息节点上下文,但仅限于该对象。根据手册页挂载(man 8 mount),发现它在无状态 Linux 中很有用。

用扩展属性标注

最后,可能是最常用的标注方式,是使用扩展属性支持,也称为xattr或 EA 支持。即使有xattr支持,新对象也会继承其父目录的上下文;然而,这些标签具有基于每个文件系统对象或基于索引节点的粒度。如果您还记得,我们必须打开或验证我们的文件系统在安卓系统上启用了XATTR(CONFIG_EXT4_FS_XATTR)支持,并通过配置选项CONFIG_EXT4_FS_SECURITY配置 SELinux 来使用它。

扩展属性是文件的键值元数据存储。SELinux 安全上下文使用security.selinux键,该值是一个字符串,是安全上下文或标签。

文件 _ 上下文文件

sepolicy目录内,你会找到file_contexts文件。参考该文件来设置支持每个文件安全标签的的文件系统的属性。请注意,一些伪文件系统也支持这一点,例如tmpfssysfs和最近的rootfsfile_context文件具有如下基于正则表达式的语法,其中regexp是路径的正则表达式:

regexp <type> ( <file label> | <<none>> )

如果为一个文件定义了多个正则表达式,则使用最后一个匹配,因此顺序很重要。

下面的列表显示了文件系统对象类型的每个类型字段值、它们的含义和系统调用接口:

  • --:这个表示常规文件。
  • -d:这个表示一个目录。
  • -b:此表示块文件。
  • -s:这个表示一个套接字文件。
  • -c:这个表示一个字符文件。
  • -l:这个表示链接文件。
  • -p:此表示命名管道文件。

正如你所看到的,类型本质上是通过ls -la命令输出的模式。如果没有指定,则匹配所有内容。

下一个字段是文件标签或特殊标识符<<none>>。要么提供上下文,要么提供标识符<<none>>。如果您指定了上下文,查询file_contexts的 SELinux 工具将使用指定上下文的最后一个匹配。如果指定的上下文是<<none>>,则表示没有分配上下文。所以,留下我们找到的那个。关键词<<none>>在 AOSP 参考中没有使用,sepolicy

需要注意的是,上一段明确指出 SELinux 工具使用file_contexts策略。内核不知道这个文件存在。SELinux 通过使用在file_context中查找上下文的工具,或者通过fs_use_*genfs策略声明,从用户空间显式设置所有对象,从而对其进行标记。换句话说,file_contexts不是内置在核心策略文件中,也不是由内核直接加载或使用的。在构建时,file_contexts文件构建在 ramdisk rootfs 中,可以在/file_contexts中找到。此外,在构建期间,系统映像会被标记,从而将设备本身从这一负担中解放出来。

在安卓系统中,initueventdinstalld都经过了修改,可以查找它们正在创建的对象的上下文;以便他们能正确地给它们贴上标签。因此,所有创建文件系统对象的初始化内置程序,如mkdir,都被修改为使用file_contexts文件(如果存在的话),同样的情况也适用于installdueventd

让我们来看看位于sepolicy中的file_context文件的一些片段:

...
/dev(/.*)? u:object_r:device:s0
/dev/accelerometer u:object_r:sensors_device:s0
/dev/alarm u:object_r:alarm_device:s0
...

这里,我们在/dev中设置文件的上下文。注意条目是如何从最普通的文件到更具体的dev文件排序的。因此,任何未被更具体条目覆盖的文件都将以上下文u:object_r:device:s0结束,而匹配的文件以更具体的标签结束。例如,/dev/accelerometer处的加速度计将获得上下文u:object_r:sensors_device:s0。请注意,类型字段被省略了,这意味着它在上匹配所有文件系统对象,例如目录(type -d)。

你可能想知道目录本身是如何获取文件上下文的。看一些片段,我们说/或根,通过genfs_context文件中的语句genfscon rootfs / u:object_r:rootfs:s0被标记。本章前面说过,“新对象继承其父目录的上下文。”因此,我们可以推断/dev是有背景的u:object_r:rootfs:s0,因为这是/的标签。我们可以通过将-Z标志传递给ls来测试这一点,以向我们显示/dev的标签。在 UDOO 串行连接上,执行以下命令:

130|root@udoo:/ # ls -laZ / 
...
drwxr-xr-x root root u:object_r:device:s0 dev
...

看起来这个假设是不正确的,但是要注意的是,确实每样东西都有一个标签,如果没有指定,那么就是从父代继承的。回顾sepolicy,我们可以看到dev文件系统最初是用fs_use_trans devtmpfs u:object_r:device:s0;策略声明设置的。因此,当挂载文件系统时,它被设置为文件系统范围。稍后,当通过initueventd添加条目时,它们使用file_contexts条目将新创建的文件系统对象的上下文设置为file_contexts文件中指定的内容。位于 /dev的文件系统是一个devtmps伪文件系统,它是一个文件系统的例子,通过fs_use_trans语句具有文件系统范围的标签,但是也可以通过file_contexts;支持细粒度的标签。文件系统在 Linux 上的功能不太一致。

动态类型转换

SELinux 策略语句type_transition指示的动态类型转换是一种允许文件动态确定其类型的方法。因为这些都被编译到策略中,所以这些与file_contexts文件没有任何关系。这些策略语句允许策略作者基于创建文件的上下文动态规定文件的上下文。这些在您不控制源代码或者不希望以任何方式耦合 SELinux 的情况下都很有用。例如,wpa恳求者,这是一个运行无线网络支持的服务,并在其数据目录中创建一个套接字文件。它的数据目录标有类型wifi_data_file,正如所料,套接字以该标签结束。但是,该套接字由系统服务器共享。现在,我们可以只允许系统服务器访问类型和对象类,但是,hostapd和其他东西正在该目录中创建套接字和其他对象,因此这些对象也具有这种类型。我们真的想确保有问题的两个插座,一个由hostapd使用,另一个由系统服务器使用,保持相互排斥。为此,我们需要能够以更精细的粒度标记其中一个套接字,而要这样做,我们可以修改代码或者使用动态类型转换。与其摆弄代码,不如使用类型转换,如下所示:

type_transition wpa wifi_data_file:sock_file wpa_socket;

这是来自sepolicy文件wpa_supplicant.te的实际语句。它说,当类型为wpa的进程创建类型为wifi_data_file的文件,并且对象类为sock_file以在创建时将其标记为wpa_socket时。语句语法如下:

type_transition <creating type> <created type>:<class> <new type>;

从 SELinux 策略版本 25 开始,type_transition语句可以支持命名类型转换,其中存在第四个参数,并且是文件的名称:

type_transition <creating type> <created type>:<class> <new type> <file name>;

我们将在sepolicy文件system_server.te中看到这个文件名的使用示例:

type_transition system_server system_data_file:sock_file system_ndebug_socket "ndebugsocket";

请注意文件名或基本名称,而不是路径,并且必须完全匹配。不支持 Regex。有趣的是,动态转换不限于文件对象,而是任何对象类事件进程。我们将在第 9 章向域添加服务中看到动态流程转换是如何使用的。

示例和工具

有了理论,让我们看看系统中标记文件的工具和技术。让我们从挂载一个ramfs文件系统开始。我们将从重新装载/开始,因为它是只读的,并为文件系统创建一个装载点。通过 UDOO 串行控制台,执行:

root@udoo:/ # mount -oremount,rw /
root@udoo:/ # mkdir /ramdisk
root@udoo:/ # mount -t ramfs -o size=20m ramfs /ramdisk

现在,我们想看看文件系统有哪个标签:

# ls -laZ / | grep ramdisk 
drwxr-xr-x root root u:object_r:unlabeled:s0 ramdisk

如您所知,initial_sid_context文件为文件系统设置了这个初始的sid:

sid file u:object_r:unlabeled:s0

如果我们想在一个新的标签中得到这个 ramdisk,我们需要在策略中创建类型,并设置一个新的genfscon语句来使用它。我们将在策略文件file.te中声明新类型:

type ramdisk, file_type, fs_type;

类型策略语句语法如下:

type <new type>, <attribute0,attribute1…attributeN>;

SELinux 中的属性是允许您定义公共组的语句。它们是通过attribute语句定义的。在安卓 SELinux 策略中,我们已经定义了file_typefs_type。我们将在这里使用它们,因为我们正在创建的这种新类型具有file_typefs_type属性。file_type属性与文件的类型相关联,而fs_type属性意味着该类型也与文件系统相关联。属性,现在,不是很重要;所以不要纠结于细节。

接下来要修改的是sepolicy文件,genfs_context通过添加以下内容:

genfscon ramfs / u:object_r:ramdisk:s0

现在,我们将编译引导映像并将其闪存到设备,或者更好的是,让我们使用动态策略重新加载支持,如下所示。

从 UDOO 项目树的根中构建刚刚的sepolicy项目:

$ mmm external/sepolicy/

将新政策推过adb,如下:

$ adb push $OUT/root/sepolicy /data/security/current/sepolicy
544 KB/s (86409 bytes in 0.154s)

使用setprop命令触发重新加载:

$ adb shell setprop selinux.reload_policy 1

如果连接了串行控制台,您应该会看到:

SELinux: Loaded policy from /data/security/current/sepolicy

如果没有,只有adb,勾选dmesg:

$ adb shell dmesg | grep "SELinux: Loaded"
<4>SELinux: Loaded policy from /sepolicy
<6>init: SELinux: Loaded property contexts from /property_contexts
<4>SELinux: Loaded policy from /data/security/current/sepolicy

成功的加载应该在路径/data/security/current/sepolicy使用我们的策略。让我们卸载 ramdisk 并重新装载它,以检查其类型:

root@udoo:/ # umount /ramdisk 
root@udoo:/ # mount -t ramfs -o size=20m ramfs /ramdisk
root@udoo:/ # ls -laZ / | grep ramdisk
drwxr-xr-x root root u:object_r:ramdisk:s0 ramdisk

我们能够修改策略并使用genfscon来更改文件系统类型,现在为了显示继承,让我们继续使用touch在文件系统上创建一个文件:

root@udoo:/ # cd /ramdisk
root@udoo:/ramdisk # touch hello
root@udoo:/ramdisk # ls -Z
-rw------- root root u:object_r:ramdisk:s0 hello

正如我们所料,新文件被标记为 ramdisk 类型。现在,假设当我们从外壳进行触摸时,我们希望文件是不同的类型,例如ramdisk_newfile;我们如何做到这一点?我们可以通过修改 touch 本身来查阅file_contexts来做到这一点,或者我们可以定义一个动态类型转换;让我们尝试动态类型转换方法。type_transition说法的第一个论据是创造型;那么我们的外壳是什么类型的呢?您可以通过执行以下操作来实现这一点:

root@udoo:/ramdisk # echo `cat /proc/self/attr/current`
u:r:init_shell:s0

更简单的方法是运行id -Z命令,该命令使用前面提到的proc文件。对于串行控制台,执行:

root@udoo:/ramdisk # id -Z
uid=0(root) gid=0(root) context=u:r:init_shell:s0

并对adb shell 运行相同的命令:

$ adb shell id -Z
uid=0(root) gid=0(root) context=u:r:shell:s0

请注意我们的串行控制台外壳和adb外壳之间的差异,在第 9 章向域添加服务;我们会解决的。正因为如此,我们现在制定的政策将解决这两种情况。

首先打开sepolicy文件,init_shell.te,并在文件末尾添加以下内容:

type_transition init_shell ramdisk:file ramdisk_newfile;

sepolicy文件shell.te执行此操作:

type_transition shell ramdisk:file ramdisk_newfile;

现在,我们需要声明新的类型;所以打开sepolicy文件,file.te并追加以下内容:

type ramdisk_newfile, file_type;

注意,我们只使用了file_type属性。这是因为文件系统永远不应该有类型ramdisk_newfile,只有驻留在该文件系统中的文件应该有。

现在,构建adb策略,将其推送到设备,并触发重新加载。完成后,创建文件并检查结果:

$ adb shell 'touch /ramdisk/shell_newfile'
$ adb shell 'ls -laZ /ramdisk'
-rw-rw-rw- root root u:object_r:ramdisk:s0 shell_newfile

所以没用。让我们通过尝试一个ext4文件系统的例子来研究原因。让我们使用以下命令:

root@udoo:/ # cd /data/
root@udoo:/data # mkdir ramdisk

现在,检查它的上下文:

root@udoo:/data # ls -laZ | grep ramdisk
drwx------ root rootu:object_r:system_data_file:s0 ramdisk

标签是system_data_file。这没有帮助,因为它不适用于我们的类型转换规则;要解决这个问题,我们可以使用chcon命令显式更改文件上下文:

root@udoo:/data # chcon u:object_r:ramdisk:s0 ramdisk
root@udoo:/data # ls -laZ | grep ramdisk
drwx------ root root u:object_r:ramdisk:s0 ramdisk

现在,随着上下文的改变以匹配我们之前尝试的 ramdisk,让我们尝试在这个目录中创建一个文件:

root@udoo:/data/ramdisk # touch newfile
root@udoo:/data/ramdisk # ls -laZ
-rw------- root root u:object_r:ramdisk_newfile:s0 newfile

如您所见,类型转换已经发生。这是为了说明您在使用 SELinux 和 Android 时可能会发现的问题。既然我们已经表明我们的type_transition语句是有效的,那么失败的原因只有两种可能:文件系统不支持它,或者我们缺少“打开它”的东西。原来,后者是如此;我们错过了我们的fs_use_trans声明。所以继续打开sepolicy文件,fs_use并添加以下行:

fs_use_trans ramfs u:object_r:ramdisk:s0;

该语句在该文件系统上启用 SELinux 动态转换。现在,重建sepolicy项目,adb push策略文件,并通过setprop启用动态重载:

$ mmm external/sepolicy
$ adb push $OUT/root/sepolicy /data/security/current/sepolicy546 KB/s (86748 bytes in 0.154s)
$ adb shell setprop selinux.reload_policy 1
root@udoo:/ # cd ramdisk
root@udoo:/ramdisk # touch foo
root@udoo:/ramdisk # ls -Z
-rw------- root root u:object_r:ramdisk_newfile:s0 foo

在这里拥有它,对象拥有由动态类型转换确定的正确值。我们缺少fs_use_trans,它在不支持xattrs的文件系统上启用了类型转换。

现在,假设我们想安装另一个 ramdisk,会发生什么?因为它被标记了genfscon语句,所以用该类型装载的所有文件系统都应该获得上下文u:object_r:ramdisk:s0。我们将在/ramdisk2安装这个文件系统,并验证这个行为:

root@udoo:/ # mkdir ramdisk2
root@udoo:/ # mount -t ramfs -o size=20m ramfs /ramdisk2

此外,检查上下文:

root@udoo:/ # ls -laZ | grep ramdisk
drwxr-xr-x root root u:object_r:ramdisk:s0 ramdisk
drwxr-xr-x root root u:object_r:ramdisk:s0 ramdisk2

如果我们想编写允许规则来分隔对这些文件系统的访问,我们需要将它们的目标文件分为不同的类型。为此,我们可以使用上下文选项挂载新的 ramdisk。但首先,我们需要创造新的类型;让我们转到sepolicy文件,file.te并添加一个名为ramdisk2的新类型:

type ramdisk2, file_type, fs_type;

现在,使用命令mmm构建sepolicy,然后使用命令abd push推送策略,并使用setprop命令触发重新加载:

$ mmm external/sepolicy/
$ adb push out/target/product/udoo/root/sepolicy /data/security/current/sepolicy542 KB/s (86703 bytes in 0.155s)
$ adb shell setprop selinux.reload_policy 1

现在,让我们卸载/ramdisk2并用context=选项重新装载它:

root@udoo:/ # umount /ramdisk2/ 
root@udoo:/ # mount -t ramfs -osize=20m,context=u:object_r:ramdisk2:s0 ramfs /ramdisk2

现在,验证上下文:

root@udoo:/ # ls -laZ | grep ramdisk 
drwxr-xr-x root root u:object_r:ramdisk:s0 ramdisk
drwxr-xr-x root root u:object_r:ramdisk2:s0 ramdisk2

我们可以用mount选项context=<context>覆盖genfscon上下文。事实上,如果我们看看dmesg,我们可以看到一些很棒的信息。当我们在没有上下文选项的情况下安装ramfs时,我们得到:

<7>SELinux: initialized (dev ramfs, type ramfs), uses genfs_contexts

当我们使用context=<context>选项安装它时,我们得到:

<7>SELinux: initialized (dev ramfs, type ramfs), uses mountpoint labeling

我们可以看到 SELinux 给了我们一些有用的信息,同时试图找出它的标签来自哪里。

现在,让我们继续使用xattr支持来标记文件系统,例如ext4。我们将从chcon工具箱命令开始。chcon命令允许您显式设置文件系统对象的上下文,它不咨询file_contexts

让我们来看看/system/bin和其中的前 10 个文件:

$ adb shell ls -laZ /system/bin | head -n10
-rwxr-xr-x root shell u:object_r:system_file:s0 InputDispatcher_test
-rwxr-xr-x root shell u:object_r:system_file:s0 InputReader_test
-rwxr-xr-x root shell u:object_r:system_file:s0 abcc
-rwxr-xr-x root shell u:object_r:system_file:s0 adb
-rwxr-xr-x root shell u:object_r:system_file:s0 am
-rwxr-xr-x root shell u:object_r:zygote_exec:s0 app_process
-rwxr-xr-x root shell u:object_r:system_file:s0 applypatch
-rwxr-xr-x root shell u:object_r:system_file:s0 applypatch_static
drwxr-xr-x root shell u:object_r:system_file:s0 asan
-rwxr-xr-x root shell u:object_r:system_file:s0 asanwrappe

我们可以看到它们中的许多都有system_file标签,这是该文件系统的默认标签;让我们将am类型更改为am_exec。同样,我们需要通过向sepolicy文件file.te添加以下内容来创建新类型:

type am_exec, file_type;

现在,重建策略文件,将其推送到 UDOO,并触发重新加载。之后,让我们开始重新安装系统,因为它是只读的:

root@udoo:/ # mount -orw,remount /system

现在执行chcon:

root@udoo:/ # chcon u:object_r:am_exec:s0 /system/bin/am 

验证结果:

root@udoo:/ # la -laZ /system/bin/am 
-rwxr-xr-x root shell u:object_r:am_exec:s0 am

此外,restorecon命令将使用file_contexts,并将该文件恢复到file_contexts文件中设置的内容,该文件应为system_file:

root@udoo:/ # restorecon /system/bin/am 
root@udoo:/ # la -laZ /system/bin/am 
-rwxr-xr-x root shell u:object_r:system_file:s0 am

如您所见,restorecon能够查阅file_contexts并恢复该对象上的指定上下文。

安卓系统的文件系统是在构建期间构建的,因此,它的所有文件对象都是在这个过程中标记的。我们也可以在构建时通过改变file_contexts来改变这一点。改变后,系统分区重建,重新刷新系统后,我们应该会看到am_exec类型的am文件。我们可以通过修改sepolicy文件file_contexts来测试这一点,方法是在system/bin部分的末尾添加这一行:

/system/bin/am u:object_r:am_exec:s0

通过以下方式重建整个系统:

$ make -j8 2>&1 | tee logz

现在闪存并重启,让我们看看/system/bin/am的上下文如下:

root@udoo:/ # ls -laZ /system/bin/am 
-rwxr-xr-x root shell u:object_r:am_exec:s0 am

这表明系统分区尊重构建时标记的文件上下文,以及我们如何控制这些标记。

修复/数据

另外在审核日志中,我们看到了一堆未标记的文件,例如,下面的否认:

type=1400 msg=audit(86559.780:344): avc: denied { append } for pid=2668 comm="UsbDebuggingHan" name="adb_keys" dev=mmcblk0p4 ino=42 scontext=u:r:system_server:s0 tcontext=u:object_r:unlabeled:s0 tclass=file

我们可以看到设备是mmcblk0p4,它会发出挂载命令,并在其输出中告诉我们这个挂载到了什么文件系统:

root@udoo:/ # mount | grep mmcblk0p4
/dev/block/mmcblk0p4 /data ext4 rw,seclabel,nosuid,nodev,noatime,nodiratime,errors=panic,user_x0

那么为什么/data文件系统有这么多未标记的文件呢?原因是 SELinux 应该从一个空设备打开,也就是说,从第一次引导开始。安卓按需构建数据目录结构。因此,/data的所有标签都由file_contexts文件处理,因为它是ext4。此外,它由创建/data文件和目录的系统处理。这些系统已经过修改,以根据file_contexts规范标记数据分区。因此,这提供了两个选项:擦除/data和重新启动,或restorecon -R /data

选项一有点苛刻,但是如果你弹出 SD 卡,把数据分区partition 4上的所有文件都去掉,安卓就会重建,你就看不到更多未标注的问题了。但是,在升级时,不建议部署的设备使用这种方法;你会毁掉所有用户的数据。

选项二在已部署的场景中更受欢迎,但也有其局限性。值得注意的是,执行restorecon -R /data将花费很长时间,并且必须在引导早期完成,就在挂载之后。然而,这确实是目前唯一的选择。然而,谷歌在这方面做了大量工作,并创建了一个系统,在策略更新时智能地重新标记/data。对于我们的使用,我们将选择选项二的变体,特别是在考虑到/data文件系统有多稀疏之后;我们确实还没有安装或生成大量的用户数据。既然如此,执行:

root@udoo:/ # restorecon -R /data
root@udoo:/ # reboot

由于我们的系统处于许可模式,并且我们不在已部署的场景中,因此我们不必在引导的早期执行restorecon。现在,让我们提取audit.log文件,并将其与已经提取的audit.log进行比较:

$ adb pull /data/misc/audit/audit.log audit_data_relabel.log
170 KB/s (14645 bytes in 0.084s)

让我们用grep来统计每个文件中出现的次数:

$ grep -c unlabeled audit.log 
185
$ grep -c unlabeled audit_data_relabel.log 
0

太好了,我们在/data上解决了所有未标记的问题!

关于安全的附带说明

请注意即使我们正在运行所有这些命令并更改所有这些内容,这也不是 SELinux 中的安全漏洞。能够更改类型标签、装载文件系统以及将文件系统与类型相关联,都需要允许规则。如果你浏览审计日志,你会看到一系列的否认;提供了一个示例:

type=1400 msg=audit(90074.080:192): avc: denied { associate } for pid=3211 comm="touch" name="foo" scontext=u:object_r:ramdisk_newfile:s0 tcontext=u:object_r:ramdisk:s0 tclass=filesystem
type=1400 msg=audit(90069.120:187): avc: denied { mount } for pid=3205 comm="mount" name="/" dev=ramfs ino=1992 scontext=u:r:init_shell:s0 tcontext=u:object_r:ramdisk:s0 tclass=filesystem

如果我们处于强制模式,我们将无法执行这里显示的任何实验。

总结

在本章中,我们看到了如何通过重新标记文件来将文件放入上下文中。我们使用了多种技术来完成这个任务,从工具箱命令如chconrestorecon,到挂载选项和动态转换。使用这些工具,我们可以确保所有文件系统对象都被正确标记。这样,我们就有了正确的目标环境,这样我们制定的政策就有效了。在下一章中,我们将关注流程,确保它们处于正确的领域或环境中。*