尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

Android SELinux中genfscon规则:虚拟文件系统安全标签配置实战

Android SELinux中genfscon规则:虚拟文件系统安全标签配置实战 1. 项目概述为什么我们需要关注genfscon如果你在Android系统开发或者深度定制ROM的过程中遇到过文件访问被SELinux无情拒绝即使chmod 777也无济于事那么你很可能已经和SELinux的规则打上交道了。在众多SELinux策略语句中genfscon是一个相对低调但至关重要的存在。它不像allow规则那样直接明了也不像type_transition那样充满逻辑趣味但它却是打通内核虚拟文件系统如proc,sysfs与SELinux安全上下文之间桥梁的关键角色。简单来说genfscon用于为那些在磁盘上并不真实存在、由内核动态生成的文件或目录节点预先定义好其安全上下文security context。在Android庞大的设备树和碎片化的硬件生态中各种芯片厂商如Qualcomm, MediaTek会通过sysfs、proc等文件系统暴露大量的设备节点供HAL层或驱动访问。如果这些节点的SELinux标签是null或错误的那么任何访问尝试都会被默认拒绝导致功能失效。因此理解和正确使用genfscon是解决这类“权限不足”问题的核心钥匙之一。本文将从一个实际案例出发拆解genfscon的工作原理、语法细节、在Android源码中的位置以及最重要的——如何根据系统日志和内核代码定位并编写正确的genfscon规则。无论你是正在为你的设备修复一个SELinux拒绝avc denial还是在进行系统级的功能开发这篇文章都将提供一条清晰的排查路径和实操指南。2. genfscon的核心原理为虚拟文件贴上“安全标签”要理解genfscon首先得抛开对普通文件系统的认知。对于/data,/system这类基于磁盘的文件系统ext4, f2fs等它们的文件安全上下文可以通过文件扩展属性xattr在创建时被直接赋予并在文件系统挂载时通过context挂载选项或file_contexts文件进行批量标记。但是像proc、sysfs、cgroup、debugfs、pstore这样的文件系统是特殊的。它们被称为“虚拟文件系统”Virtual File System, VFS其目录和文件并非存储在磁盘上而是由内核在内存中动态创建用于暴露内核状态、硬件信息或提供调试接口。这些文件节点没有持久的存储载体因此无法通过传统的xattr来存储安全上下文。那么SELinux如何知道/sys/class/gpio/gpio10/value这个文件应该具有什么样的类型type呢这就是genfscon的职责所在。它的工作原理可以概括为在内核中为指定的虚拟文件系统类型及其路径预先注册一套安全上下文匹配规则。当进程尝试访问一个虚拟文件系统中的路径时内核的SELinux钩子hook会调用security_genfs_sid()函数。这个函数会遍历所有已注册的genfscon规则进行字符串前缀匹配。一旦找到匹配的规则就会将该规则中定义的安全上下文具体来说是其中的type字段赋予这次访问操作的目标即那个虚拟文件。后续的权限检查avc_has_perm就会使用这个推导出来的type进行。这个过程是静态和声明式的。规则在系统启动、SELinux策略被加载时就已经确定。这也意味着如果你需要为一个新出现的sysfs节点添加规则你必须修改策略源文件重新编译策略并重新启动系统或至少重新加载策略才能生效。一个典型的genfscon规则在策略文件通常是*.te文件同目录下的file_contexts或专门的genfs_contexts中这样表示genfscon sysfs /devices/platform/soc/1234.i2c/i2c-1/1-001a/input/input1 u:object_r:input_device:s0这条规则分解开来genfscon: 关键字。sysfs: 虚拟文件系统类型fs_type。/devices/platform/.../input1: 在该文件系统内的路径前缀。注意这里是内核看到的路径可能与用户空间ls看到的路径因挂载点不同而有差异。u:object_r:input_device:s0: 完整的安全上下文。其中input_device就是赋予该路径下文件的SELinux类型。注意genfscon的路径匹配是“最长前缀匹配”。也就是说如果存在两条规则/devices/platform/a和/devices/platform/a/b那么路径/devices/platform/a/b/c会匹配更具体的第二条规则。这允许我们为一个大目录设置默认标签再为其子目录设置更特定的标签。3. 实战从avc denial日志定位缺失的genfscon规则理论总是枯燥的我们结合一个真实的调试场景。假设你正在开发一个外设驱动在访问/sys/class/uwb/uwb0/power时遇到了如下SELinux拒绝日志通过adb shell dmesg | grep avc或logcat获取avc: denied { read } for pid1234 commmy_hal_service namepower devsysfs ino12345 scontextu:r:hal_uwb_default:s0 tcontextu:object_r:sysfs:s0 tclassfile permissive0这条日志是SELinux权限检查的“判决书”它告诉我们谁scontexthal_uwb_default类型的进程你的HAL服务。想干什么 对目标执行read操作。对什么tcontext 目标是标签为sysfs类型的文件。在哪里tclass 目标类别是file。结果denied拒绝。关键信息是tcontextu:object_r:sysfs:s0。这里的sysfs是一个非常泛化的、默认的SELinux类型通常用于标记那些没有特定规则的sysfs节点。显然我们的目标文件/sys/class/uwb/uwb0/power被系统用默认的sysfs类型标记了而hal_uwb_default进程默认没有被授权读取sysfs类型的文件。我们的任务就是为这个特定的路径创建一个更具体的类型例如uwb_device然后允许hal_uwb_default访问uwb_device。而创建这个新类型与路径关联的第一步就是使用genfscon。步骤一确定精确的内核路径用户空间看到的路径是/sys/class/uwb/uwb0/power但sysfs是一个链接的迷宫。/sys/class下的路径通常是到/sys/devices/下真实位置的符号链接。genfscon规则需要针对真实的、内核导出的路径而不是符号链接。# 在设备上查看真实路径 adb shell ls -l /sys/class/uwb/uwb0 lrwxrwxrwx 1 root root 0 2023-10-01 12:00 power - ../../devices/platform/soc/1234.uwb/uwb0/power这里我们看到真实路径是/sys/devices/platform/soc/1234.uwb/uwb0/power。genfscon规则必须使用这个真实路径前缀。步骤二在策略中定义新类型并创建genfscon规则定义新类型 在hal_uwb_default.te或相关的类型定义文件中添加新类型。type uwb_device, fs_type, sysfs_type;这里将uwb_device声明为fs_type和sysfs_type表明它是一种文件系统类型且属于sysfs家族这有助于继承一些基本的文件操作权限。编写genfscon规则 在genfs_contexts文件中添加规则。这个文件通常位于设备配置目录下如device/manufacturer/device-name/sepolicy/genfs_contexts。# genfs_contexts genfscon sysfs /devices/platform/soc/1234.uwb/uwb0 u:object_r:uwb_device:s0我们为整个uwb0目录设置标签。根据“最长前缀匹配”原则其下的power、state等所有文件都会继承uwb_device类型。步骤三添加对应的allow规则仅有标签还不够还需要允许你的HAL进程访问这个新类型的文件。在hal_uwb_default.te中添加allow hal_uwb_default uwb_device:file { read write open };更细粒度地你可以只授予read权限。如果你需要遍历目录还需要dir类的search权限。步骤四编译并刷入测试将修改后的策略文件放入源码树编译系统镜像make bootimage或make selinux_policy并刷机或者将编译生成的sepolicy文件直接推送到设备的/data/security/current/目录下临时测试。重启后再次执行你的HAL服务之前的avc denial应该就会消失。4. 深入排查当genfscon规则“不生效”时的调试技巧有时候你明明添加了genfscon规则但avc denial日志显示目标文件的tcontext仍然是旧的、默认的类型如sysfs。这通常让人困惑。以下是系统性的排查思路4.1 检查路径匹配的精确性这是最常见的问题。genfscon的路径是大小写敏感的并且必须完全匹配内核导出的路径前缀。使用内核日志确认路径 最可靠的方法是在内核驱动创建该sysfs节点的代码附近添加pr_info(“Creating sysfs at: %s\n”, path);打印。编译内核后通过dmesg查看确切的路径。用户空间的ls -l可能因为命名空间或链接而存在误导。检查路径结尾 规则genfscon sysfs /devices/platform/mydevice会匹配/devices/platform/mydevice/file和/devices/platform/mydevice_sub/file。如果你只想匹配前者可能需要更精确的路径或者利用子目录的规则覆盖。有时需要确认路径末尾是否有/但通常内核处理时会规范化路径。4.2 确认策略文件已正确包含并编译文件位置 确保genfs_contexts文件位于你设备配置的sepolicy目录下并且被Android.bp或Makefile正确引用。对于新版Soong构建系统通常是在/device/.../sepolicy/genfs_contexts。公共规则与设备规则 Android SELinux策略是分层的。平台通用规则在system/sepolicy/private/genfs_contexts。设备特定规则在device/.../sepolicy/genfs_contexts。设备规则会覆盖平台规则。确保你的修改在正确的层级并且没有被更高优先级的规则覆盖。编译产物验证 编译后查看生成的/system/etc/selinux/plat_sepolicy.cil或/vendor/etc/selinux/vendor_sepolicy.cil文件取决于你的分区。用文本编辑器搜索你定义的路径前缀确认genfscon语句已被编译进去。# 在主机上解包提取sepolicy验证 adb pull /vendor/etc/selinux/vendor_sepolicy.cil grep -n “genfscon.*uwb” vendor_sepolicy.cil4.3 检查SELinux模式与策略加载确保非宽容模式Enforcing 在宽容模式Permissive下avc denial仅被记录而不拒绝访问这可能会掩盖规则未生效的问题。使用adb shell getenforce确认是Enforcing。策略版本与兼容性 如果你只是替换了sepolicy文件确保其与当前系统内核的SELinux ABI兼容。不兼容的策略文件可能无法加载系统会回退到上次成功的策略或默认策略。查看内核日志dmesg | grep SELinux有无加载错误。4.4 使用工具辅助诊断ls -lZ 在设备上尝试对目标文件或父目录执行ls -lZ。如果显示的上下文仍然不是你定义的uwb_device则说明genfscon规则确实未生效。如果显示是你定义的上下文但仍有denial则问题出在allow规则上。sesearch 在编译主机上使用sesearch工具查询编译后的策略二进制文件确认你的genfscon和allow规则是否存在。# 在AOSP源码环境下的常用命令 out/host/linux-x86/bin/sepolicy-analyze /path/to/your/sepolicy.bin genfscon | grep uwb out/host/linux-x86/bin/sepolicy-analyze /path/to/your/sepolicy.bin allow -s hal_uwb_default -t uwb_device4.5 一个棘手的案例动态创建的节点有些内核驱动会在运行时动态创建和销毁sysfs节点。genfscon规则在策略加载时生效对于之后创建的节点只要其路径匹配规则前缀依然会被正确标记。但是如果驱动创建的路径完全超出了你定义的任何前缀它就会落入“未定义”区域被标记为默认类型。例如你的规则是genfscon sysfs /devices/platform/uwb0但驱动实际创建的是/devices/virtual/uwb/uwb0。这时就需要根据内核代码修正你的规则路径。5. 高级应用与边界情况处理掌握了基本用法后我们来看一些更复杂的场景和最佳实践。5.1 处理带变量的路径如I2C地址硬件地址经常是动态的。例如一个I2C设备可能在总线i2c-1上地址为0x1a其路径为/devices/platform/.../i2c-1/1-001a/...。其中的001a是十六进制地址。如果你为每个地址都写一条规则将无法维护。解决方案是向上级目录定义更通用的类型。例如为/devices/platform/.../i2c-1目录定义一个类型i2c_device_dir。然后允许你的HAL进程访问i2c_device_dir类型的目录和文件。这样无论其下挂载的设备地址是什么只要在该目录下都能被访问。当然这需要评估安全边界是否可接受。genfscon sysfs /devices/platform/soc/1234.i2c/i2c-1 u:object_r:i2c_device_dir:s05.2 genfscon与其他文件系统虽然sysfs和proc是最常见的用例但genfscon同样适用于其他虚拟文件系统。procfs 常用于标记特定的/proc下的文件如/proc/version、/proc/cmdline等。平台已有大量规则。debugfs 调试文件系统通常只在userdebug或eng版本中挂载并且默认类型可能是debugfs。生产版本中通常会禁用或严格限制debugfs的访问。cgroupfs 用于控制组文件系统。Android对cgroup的访问有严格策略。5.3 与file_contexts的对比与选择特性genfsconfile_contexts目标文件系统虚拟文件系统proc, sysfs, debugfs等基于磁盘的真实文件系统ext4, f2fs等标签存储方式策略中预定义内核动态匹配存储在文件的扩展属性xattr中生效时机策略加载时注册访问时匹配文件创建时或通过restorecon命令显式应用适用场景内核动态创建的节点系统镜像中的静态文件、数据分区文件简单决策流如果一个文件存在于/sys、/proc、/dev部分下首先考虑genfscon。如果存在于/system、/vendor、/data下则使用file_contexts。5.4 性能与安全考量genfscon的匹配发生在内核的权限检查路径上虽然使用哈希表优化但过多的、过于细粒度的规则仍可能带来微小的性能开销。更重要的是安全考量为一个宽路径如/sys/class/*赋予一个宽松的类型可能会意外暴露大量其他设备节点扩大攻击面。因此原则是路径尽可能精确类型尽可能专用。6. 从内核视角理解genfscon的实现对于想深究的开发者了解内核中的实现能让你更自信地调试。关键函数在Linux内核的security/selinux/hooks.c和security/selinux/ss/services.c中。策略加载 在SELinux策略被加载时genfscon语句被解析并存入一个以文件系统类型fs_type为键的哈希表中。上下文查询 当访问虚拟文件时security_genfs_sid()函数被调用。它首先根据文件系统类型找到对应的哈希表然后遍历表中的规则进行路径前缀匹配strncmp。返回SID 找到匹配的规则后将该规则关联的安全上下文字符串转换为内部的SID安全标识符并返回。这个SID就作为目标文件的标识符参与后续的访问向量检查AVC。在驱动代码中创建sysfs节点时通常通过device_create()或sysfs_create_file()等API这些API内部会调用到VFS和SELinux的钩子最终触发上述的上下文查询过程。驱动开发者一般无需关心SELinux标签只要节点创建在标准的sysfs路径下SELinux策略就会通过genfscon机制自动管理其标签。通过本文的梳理你应该对genfscon从概念到实战有了全面的认识。它不像应用层开发那样光鲜却是构建健壮、安全的Android系统底层不可或缺的一环。下次再遇到那些“明明文件存在权限也对就是访问被拒”的灵异事件时不妨先查查avc日志看看是不是genfscon在默默地把守着大门。记住精准的路径匹配和恰当的类型定义是解决这类问题的唯一正道。
返回列表