Android-SELinux

版权声明:转载请注明出处,谢谢配合 https://blog.csdn.net/qq_33750826/article/details/80831431

前一篇文章已经介绍了SELinux相关知识,
https://blog.csdn.net/qq_33750826/article/details/80743035

基于 Android 4.3(宽容模式)和 Android 4.4(部分强制模式)的 Android 5.0 版本,开始全面强制执行 SELinux。

一、政策格式

政策规则采用以下形式:
allow domains types:classes permissions;
其中:

  • Domain - 一个进程或一组进程的标签。也称为域类型,因为它只是指进程的类型。
  • Type - 一个对象(例如,文件、套接字)或一组对象的标签。
  • Class - 要访问的对象(例如,文件、套接字)的类型。
  • Permission - 要执行的操作(例如,读取、写入)。

使用政策规则时将遵循的结构示例

allow appdomain app_data_file:file rw_file_perms;

这表示所有应用域都可以读取和写入带有 app_data_file 标签的文件。请注意,该规则依赖于在 global_macros 文件中定义的宏,您还可以在 te_macros 文件中找到一些其他非常实用的宏。这两个文件均位于 AOSP 源代码树的 system/sepolicy 目录中,其中提供了一些适用于常见的类、权限和规则分组的宏。应尽可能使用这些宏,以便降低因相关权限被拒而导致失败的可能性。

除了在规则中逐个列出域或类型之外,还可以通过属性引用一组域或类型。简单来说,属性是一组域或类型的名称。每个域或类型都可以与任意数量的属性相关联。当编写的规则指定了某个属性名称时,该名称会自动扩展为列出与该属性关联的所有域或类型。例如,domain 属性与所有进程域相关联,file_type 属性与所有文件类型相关联。

使用上述语法可以创建构成 SELinux 政策基本内容的 avc 规则。规则采用以下形式:

RULE_VARIANT SOURCE_TYPES TARGET_TYPES : CLASSES PERMISSIONS

该规则指明了,当带有任何 source_types 标签的主体尝试对某个对象执行与任何 permissions 对应的操作时,如果该对象包含带有任何 target_types 标签的任何 classes 类,会发生什么情况。这些规则的一个最常见示例是 allow 规则,例如:

allow domain null_device:chr_file { open };

该规则允许具有与“domain”属性关联的任何域的进程对 target_type 标签为“null_device”的“chr_file”类(字符设备文件)的对象执行“open”权限所描述的操作。在实际中,该规则可能会扩展为包含其他权限:

allow domain null_device:chr_file { getattr open read ioctl lock append write};

当了解到“domain”是分配给所有进程域的属性,并且 null_device 是字符设备 /dev/null 的标签时,该规则基本上会允许对 /dev/null 进行读写操作。

一个 domain 通常对应一个进程,而且具有与其关联的标签。

例如,典型的 Android 应用会在自己的进程中运行,并且具有 untrusted_app 标签(用于向其授予特定受限权限)。

系统中内置的平台应用会以单独的标签运行,并会被授予一组不同的权限。作为核心 Android 系统的一部分,系统 UID 应用以表示另一组权限的 system_app 标签运行。

在任何情况下,都不应直接允许域访问以下通用标签;而应为一个或多个对象创建一个更具体的类型:

  • socket_device
  • device
  • block_device
  • default_service
  • system_data_file
  • tmpfs

二、关键文件

  • file_contexts - 位于 sepolicy子目录中。该文件用于为文件分配标签,并且可供多种用户空间组件使用。在创建新政策时,请创建或更新该文件,以便为文件分配新标签。要应用新的file_contexts,您必须重新构建文件系统映像,或对要重新添加标签的文件运行 restorecon。在升级时,对file_contexts所做的更改会在升级过程中自动应用于系统和用户数据分区。此外,还可以通过以下方式使这些更改在升级过程中自动应用于其他分区:在以允许读写的方式装载相应分区后,将restorecon_recursive 调用添加到 init.board.rc 文件中。

  • genfs_contexts - 位于 sepolicy 子目录中。该文件用于为不支持扩展属性的文件系统(例如,procvfat)分配标签。此配置会作为内核政策的一部分进行加载,但更改可能对核心内 inode无效。要全面应用更改,需要重新启动设备,或卸载后重新装载文件系统。此外,通过使用 context=mount选项,还可以为装载的特定系统文件(例如 vfat)分配特定标签。

  • property_contexts - 位于 sepolicy 子目录中。该文件用于为 Android 系统属性分配标签,以便控制哪些进程可以设置这些属性。在启动期间,init 进程会读取此配置。

  • service_contexts - 位于 sepolicy 子目录中。该文件用于为 Android Binder 服务分配标签,以便控制哪些进行可以为相应服务添加(注册)和查找(查询)Binder 引用。在启动期间,servicemanager 进程会读取此配置。
    seapp_contexts - 位于 sepolicy 子目录中。该文件用于为应用进程和 /data/data 目录分配标签。在每次应用启动时,zygote 进程都会读取此配置;在启动期间,installd 会读取此配置。

  • mac_permissions.xml - 位于 sepolicy 子目录中。该文件用于根据应用签名和应用软件包名称(后者可选)为应用分配 seinfo 标记。然后,分配的 seinfo 标记可在 seapp_contexts 文件中用作密钥,以便为带有该 seinfo 标记的所有应用分配特定标签。在启动期间,system_server 会读取此配置。

三、政策示例

以下是一个完整的 DHCP 政策示例,我们将在下文中对其进行分析:

type dhcp, domain;
permissive dhcp;
type dhcp_exec, exec_type, file_type;
type dhcp_data_file, file_type, data_file_type;

init_daemon_domain(dhcp)
net_domain(dhcp)

allow dhcp self:capability { setgid setuid net_admin net_raw net_bind_service
};
allow dhcp self:packet_socket create_socket_perms;
allow dhcp self:netlink_route_socket { create_socket_perms nlmsg_write };
allow dhcp shell_exec:file rx_file_perms;
allow dhcp system_file:file rx_file_perms;
# For /proc/sys/net/ipv4/conf/*/promote_secondaries
allow dhcp proc_net:file write;
allow dhcp system_prop:property_service set ;
unix_socket_connect(dhcp, property, init)

type_transition dhcp system_data_file:{ dir file } dhcp_data_file;
allow dhcp dhcp_data_file:dir create_dir_perms;
allow dhcp dhcp_data_file:file create_file_perms;

allow dhcp netd:fd use;
allow dhcp netd:fifo_file rw_file_perms;
allow dhcp netd:{ dgram_socket_class_set unix_stream_socket } { read write };
allow dhcp netd:{ netlink_kobject_uevent_socket netlink_route_socket
netlink_nflog_socket } { read write };

下面我们来分析一下该示例:

在第一行(即类型声明)中,该政策声明 DHCP 守护进程将沿用基本的安全政策 (domain)。从前面的声明示例中,我们知道 DHCP 可以从 /dev/null 读取数据以及向其写入数据。

在第二行中,DHCP 被声明为宽容域。

init_daemon_domain(dhcp) 这一行中,该政策声明 DHCP 是从 init 衍生而来的,并且可以与其进行通信。

net_domain(dhcp) 这一行中,该政策允许 DHCP 使用 net 域中的常用网络功能,例如读取和写入 TCP 数据包、通过套接字进行通信,以及执行 DNS 请求。

allow dhcp proc_net:file write; 这一行中,该政策声明 DHCP 可以向 /proc 中的特定文件写入数据。这一行显示了 SELinux 的详细文件标签。它使用 proc_net 标签来限定 DHCP 仅对 /proc/sys/net 中的文件具有写入权限。

该示例的最后一部分以 allow dhcp netd:fd use; 开头,描述了允许应用之间如何进行交互。该政策声明 DHCP 和 netd 之间可通过文件描述符、FIFO 文件、数据报套接字以及 UNIX 信息流套接字进行通信。DHCP 只能从数据报套接字和 UNIX 信息流套接字中读取数据以及向它们写入数据,但不能创建或打开此类套接字。

可用控件
这里写图片描述

neverallow 规则
SELinux neverallow 规则用于禁止在任何情况下都不应该发生的行为。通过执行兼容性测试,现在各种合作伙伴设备上都会强制执行 SELinux neverallow 规则。

以下准则旨在协助制造商在自定义过程中避免与 neverallow 规则相关的错误。此处使用的规则编号与 Android 5.1 中使用的编号一致,并且会因版本而异。

规则 48:

neverallow { domain -debuggerd -vold -dumpstate -system_server } self:capability sys_ptrace;

请参阅 ptrace 的帮助页面。sys_ptrace 功能用于授予对任何进程执行 ptrace 命令的权限。拥有该权限后,可以对其他进程进行广泛的控制。应该只有该规则中列出的指定系统组件享有该权限。如果需要该功能,则通常表明存在的某些内容不适用于面向用户的版本或存在不需要的功能。请移除不必要的组件。

规则 76:

neverallow { domain -appdomain -dumpstate -shell -system_server -zygote } { file_type -system_file -exec_type }:file execute;

该规则旨在防止执行系统中的任意代码。具体来说就是,该规则声明仅执行 /system 中的代码,以便通过验证启动等机制实现安全保证。通常,在遇到与这个 neverallow 规则相关的问题时,最好的解决办法是将违规代码移到 /system 分区。

四、读取拒绝事件

接下来是检查是否存在错误。错误会以事件日志的形式传给 dmesglogcat,并可在设备上从本地查看。制造商应先检查这些设备上传给 dmesgSELinux 输出并优化设置,然后再在宽容模式下公开发布,最后切换到强制模式。SELinux 日志消息中包含“avc:”字样,因此可使用 grep 轻松找到。您可以使用 cat /proc/kmsg 来获取当前的拒绝事件日志,也可以使用 cat /proc/last_kmsg 来获取上次启动时的拒绝事件日志。

根据这些输出内容,制造商可以轻松发现系统用户或组件违反 SELinux 政策的行为。然后,制造商便可更改相应软件和/或 SELinux 政策,以防范此类恶意行为。

具体来说,这些日志消息会指明在强制模式下哪些进程会失败以及失败原因。示例如下:

avc: denied  { connectto } for  pid=2671 comm="ping" path="/dev/socket/dnsproxyd"
scontext=u:r:shell:s0 tcontext=u:r:netd:s0 tclass=unix_stream_socket

该输出的解读如下:

  • 上方的 { connectto } 表示执行的操作。根据它和末尾的 tclass(unix_stream_socket),您可以大致了解是对什么对象执行什么操作。在此例中,是操作方正在试图连接到 UNIX
    信息流套接字。
  • scontext (u:r:shell:s0) 表示发起相应操作的环境,在此例中是 shell 中运行的某个程序。
  • tcontext (u:r:netd:s0) 表示操作目标的环境,在此例中是归 netd 所有的某个 unix_stream_socket
  • 顶部的 comm="ping 可帮助您了解拒绝事件发生时正在运行的程序。在此示例中,给出的信息非常清晰明了。

下面是另一个示例:

adb shell su root dmesg | grep 'avc: '

输出:

<5> type=1400 audit: avc:  denied  { read write } for  pid=177
comm="rmt_storage" name="mem" dev="tmpfs" ino=6004 scontext=u:r:rmt:s0
tcontext=u:object_r:kmem_device:s0 tclass=chr_file

以下是此拒绝事件的关键元素:

  • 操作 - 试图进行的操作列在花括号中:read writesetenforce
  • 操作方 - scontext(来源环境)条目表示操作方;在此例中为 rmt_storage 守护进程。
  • 对象 - tcontext(目标环境)条目表示是对哪个对象执行操作;在此例中为 kmem。
  • 结果 - tclass(目标类别)条目表示操作对象的类型;在此例中为 chr_file(字符设备)。

使用 audit2allow

selinux/policycoreutils/audit2allow 工具可以获取 dmesg 拒绝事件并将其转换成相应的 SELinux 政策声明。因此,该工具有助于大幅加快 SELinux 开发速度。 audit2allow 包含在 Android 源代码树中,会在您基于源代码编译 Android 时自动编译。

要使用该工具,请运行以下命令:

adb pull /sys/fs/selinux/policy
adb logcat -b all -d | audit2allow -p policy

不过,请务必仔细审核要添加到政策中的条目,以免出现权限过宽的情况。例如,如果将上面的 rmt_storage 拒绝事件输入到 audit2allow 中,会生成以下 SELinux 政策声明建议:

#============= shell ==============
allow shell kernel:security setenforce;
#============= rmt ==============
allow rmt kmem_device:chr_file { read write };

这会授予 rmt 向内核内存写入内容的权限,从而形成明显的安全漏洞。通常情况下,audit2allow 给出的声明建议只是一个大致的基础。在添加这些声明后,您可能需要更改来源域和目标标签,并纳入适当的宏,才能实现良好的政策配置。有时,拒绝事件的合理应对方式不是对政策进行更改,而是更改违规的应用。

五、解决核心服务生成的拒绝事件

核心服务生成的拒绝事件通常是通过为文件添加标签来解决的。例如:

avc: denied { open } for pid=1003 comm=”mediaserver” path="/dev/kgsl-3d0”
dev="tmpfs" scontext=u:r:mediaserver:s0 tcontext=u:object_r:device:s0
tclass=chr_file permissive=1

avc: denied { read write } for pid=1003 name="kgsl-3d0" dev="tmpfs"
scontext=u:r:mediaserver:s0
tcontext=u:object_r:device:s0 tclass=chr_file permissive=1

是完全通过为 /dev/kgsl-3d0 添加适当的标签来解决的。在此示例中,tcontextdevice。这表示默认环境,在该环境中,/dev 中的文件除非被分配了更具体的标签,否则都会获得“device”标签。直接在此处接受来自 audit2allow 的输出会导致不正确且过度宽容的规则。

/file_contexts
这里写图片描述

要解决这种问题,可以为文件添加更具体的标签,在此示例中为 gpu_device

/file_contexts
这里写图片描述

相应的政策,mediaservice.te
这里写图片描述

一般情况下,向默认标签授予权限的做法是错误的。其中许多权限都是 neverallow 规则所不允许的,但即使该规则并未明确禁止这些权限,也最好是提供具体标签。


常见错误

过度使用否定

以下示例规则类似于锁着前门,但开着窗户:

allow { domain -untrusted_app } scary_debug_device:chr_file rw_file_perms

该规则的意图很明确:除了第三方应用之外,其他所有应用都可以访问调试设备。

该规则存在几个方面的缺陷。排除 untrusted_app 所起到的效果微不足道,因为所有应用都可以选择在 isolated_app 网域中运行服务。同样,如果第三方应用的新域被添加到了 AOSP,它们也可以访问 scary_debug_device。该规则过于宽容。对于大多数域来说,能够访问该调试工具并不能使它们获益。该规则应编写为仅允许需要访问该调试工具的域


为新服务添加标签并解决拒绝事件

通过 init 启动的服务需要在各自的 SELinux 域中运行。以下示例会将服务“foo”放入它自己的 SELinux 域中并为其授予权限。

该服务是在设备的 init.<target>.rc 文件中启动的,如下所示:

service foo /system/bin/foo
    class core
  1. 创建一个新域“foo”
    创建包含以下内容的文件 device/<oem>/<target>/sepolicy/foo.te
# foo service
type foo, domain;
type foo_exec, exec_type, file_type;

init_daemon_domain(foo)

这是 foo SELinux 域的初始模板,您可以根据该可执行文件执行的具体操作为该模板添加规则。

2、 为 /system/bin/foo 添加标签
将以下内容添加到 device/<oem>/<target>/sepolicy/ file_contexts

/system/bin/foo   u:object_r:foo_exec:s0

这可确保为该可执行文件添加适当的标签,以便 SELinux 在适当的域中运行相应服务。

3、编译并刷写启动映像和系统映像。

4、优化相应域的 SELinux 规则。

根据拒绝事件确定所需的权限。audit2allow 工具提供了一些实用的指南,但该工具仅适用于提供编写政策时所需的信息。切勿只是复制输出内容。

猜你喜欢

转载自blog.csdn.net/qq_33750826/article/details/80831431