15. Android SELinux(四):neverallow规则、策略调试与审计

各位同学,今天我们来聊聊SELinux策略里最“硬核”的部分——neverallow规则、策略调试与审计。说实话,这部分内容在平时开发中可能不常用,但一旦遇到问题,它就是你的救命稻草。我自己在项目里就吃过不少亏,今天把这些经验分享给你。

15.1 neverallow规则:策略的“红线”

neverallow规则,说白了就是SELinux策略里的“红线”。它定义了什么操作是绝对不允许的。比如,你写了一条allow规则,允许某个域访问某个资源,但如果这条规则触发了neverallow,编译就会直接报错。

为什么会这样?因为neverallow是策略编译时的硬性检查。它不是为了运行时拦截,而是为了在策略开发阶段就堵住漏洞。我刚开始接触时,总觉得它多此一举,直到有一次我写了一条过于宽松的规则,差点让系统暴露在风险中。

核心要点:neverallow规则是策略的“安全底线”,任何违反它的规则都无法通过编译。

15.1.1 neverallow的语法

neverallow的语法和allow很像,但含义完全相反。看个例子:

neverallow { domain -untrusted_app } self:process execmem;

这条规则的意思是:除了untrusted_app之外,任何域都不能执行可执行内存(execmem)。如果你试图给system_server添加execmem权限,编译就会失败。

我个人习惯在写策略前,先看看已有的neverallow规则。这样能避免走弯路。你想想看,如果一条规则注定通不过,何必浪费时间呢?

15.1.2 常见的neverallow陷阱

我在项目中遇到过这样一个坑:给一个系统服务添加了访问网络设备的权限,结果编译报错。查了半天,发现是neverallow规则禁止了非网络域直接访问网络设备。解决办法是让这个服务通过netd来间接访问。

避坑指南:我曾经因为没仔细看neverallow规则,写了一条看似合理但实际违规的规则。结果编译失败,排查了整整一天。建议你在写新规则前,先用checkpolicy工具检查一下。

15.2 策略调试:从“黑盒”到“白盒”

策略调试是SELinux开发中最头疼的部分。系统跑着跑着突然报avc: denied,你根本不知道哪里出了问题。别急,我来教你几招。

15.2.1 启用permissive模式

permissive模式是调试的“神器”。在这个模式下,SELinux只记录违规操作,但不拦截。你可以通过以下命令启用:

adb shell setenforce 0

但要注意,permissive模式会降低安全性。我建议只在开发机上使用,千万别在生产环境里这么干。

15.2.2 分析avc日志

avc日志是调试的核心。当SELinux拒绝一个操作时,会打印类似这样的日志:

avc: denied { read } for pid=1234 comm="app_process" name="secret.txt" dev="mmcblk0p1" ino=5678 scontext=u:r:untrusted_app:s0 tcontext=u:object_r:secret_file:s0 tclass=file

这条日志告诉你:untrusted_app想读取secret_file,但被拒绝了。你需要添加一条allow规则:

allow untrusted_app secret_file:file read;

嗯,这里要注意:avc日志里的scontext和tcontext一定要看仔细。我见过有人把源和目标搞反,结果规则写错了,问题依旧。

15.2.3 使用audit2allow工具

audit2allow是个自动化工具,能根据avc日志生成对应的allow规则。用法很简单:

adb shell dmesg | audit2allow -p policy.conf

但我不建议完全依赖它。因为audit2allow生成的规则往往过于宽松。我曾经用它生成了一条规则,结果允许了不必要的权限,差点引入安全漏洞。所以,用它作为参考,然后手动优化。

15.3 审计:谁在做什么?

审计是SELinux的“监控摄像头”。它能记录系统中所有敏感操作,帮你发现异常行为。

15.3.1 配置审计规则

审计规则通过auditallow语句定义。比如:

auditallow untrusted_app secret_file:file read;

这条规则会让SELinux在untrusted_app读取secret_file时,打印一条审计日志。注意,auditallow不会阻止操作,它只是记录。

我个人习惯在关键资源上开启审计。比如,系统配置文件、用户数据目录等。这样一旦有人越权访问,我能第一时间发现。

15.3.2 审计日志分析

审计日志和avc日志类似,但多了audit字样。你可以用logcatdmesg查看。举个例子:

audit: avc: granted { read } for pid=1234 comm="app_process" name="secret.txt" dev="mmcblk0p1" ino=5678 scontext=u:r:untrusted_app:s0 tcontext=u:object_r:secret_file:s0 tclass=file

这条日志告诉你:untrusted_app成功读取了secret_file。如果你发现这个操作不应该发生,那就说明策略有问题,需要收紧。

小技巧:审计日志会占用系统资源,别开太多。我建议只审计那些真正敏感的操作,比如访问密码文件、修改系统设置等。

15.4 知识体系图:neverallow、调试与审计的关系

下面这张图展示了本章的核心逻辑。你可以看到,neverallow是策略的“红线”,调试是解决问题的“工具”,审计是监控的“眼睛”。三者相辅相成,共同保障系统安全。

SELinux策略核心:neverallow、调试与审计 neverallow规则 策略编译时的“红线” 策略调试 permissive + avc日志 审计 auditallow + 日志监控 关键特性 • 编译时检查 • 定义安全底线 • 违反即编译失败 • 语法类似allow • 用于限制过度授权 • 常见陷阱:网络、内存 调试方法 • permissive模式 • 分析avc日志 • audit2allow工具 • 手动编写规则 • 注意scontext/tcontext • 避免过度宽松 审计要点 • auditallow语句 • 记录敏感操作 • 不阻止操作 • 日志格式分析 • 资源开销控制 • 发现异常行为 三者结合:策略开发 → 调试 → 审计 → 安全闭环

15.5 实战:一个完整的调试案例

最后,我给你分享一个我实际遇到的案例。有一次,系统启动时总是报avc: denied,导致某个服务无法正常运行。我通过以下步骤解决了问题:

  1. 启用permissive模式:先让系统跑起来,收集所有违规日志。
  2. 提取avc日志:用dmesg | grep avc过滤出所有拒绝记录。
  3. 分析日志:发现是system_server试图访问一个自定义的socket文件。
  4. 编写规则:手动添加了一条allow规则,并检查是否违反neverallow。
  5. 编译测试:重新编译策略,部署后问题解决。

你看,整个过程并不复杂。关键是你要理解neverallow、调试和审计这三者的关系。嗯,今天的内容就到这里。记住,SELinux策略开发没有捷径,多动手、多分析日志,你也能成为专家。