[Android]通过’avc denied’日志信息添加SELinux规则

前言

本文仅讨论如果通过’avc denied’日志信息,确定缺失的SELinux规则。
暂不讨论:

  • 添加的SELinux规则是否存在权限放大;
  • 添加的SELinux规则是否触犯neverallow;

(有时间再补上这两部分,挖坑+1…)

背景

最近在跑CTS,有些Fail项与SELinux有关,这里举个例子:

cts-tf > run cts -m CtsNetTestCases -t android.net.ipv6.cts.PingTest#testLoopbackPing
...
12-28 14:05:50 I/ModuleListener: [1/1] android.net.ipv6.cts.PingTest#testLoopbackPing fail:

android.system.ErrnoException: socket failed: EACCES (Permission denied)
    at libcore.io.Linux.socket(Native Method)
    at libcore.io.BlockGuardOs.socket(BlockGuardOs.java:311)
    at android.system.Os.socket(Os.java:549)
    at android.net.ipv6.cts.PingTest.createPingSocket(PingTest.java:90)
    at android.net.ipv6.cts.PingTest.testLoopbackPing(PingTest.java:162)
    at java.lang.reflect.Method.invoke(Native Method)
    at junit.framework.TestCase.runTest(TestCase.java:168)
    at junit.framework.TestCase.runBare(TestCase.java:134)
    at junit.framework.TestResult$1.protect(TestResult.java:115)
    at androidx.test.internal.runner.junit3.AndroidTestResult.runProtected(AndroidTestResult.java:73)
    at junit.framework.TestResult.run(TestResult.java:118)
    at androidx.test.internal.runner.junit3.AndroidTestResult.run(AndroidTestResult.java:51)
    at junit.framework.TestCase.run(TestCase.java:124)
    at androidx.test.internal.runner.junit3.NonLeakyTestSuite$NonLeakyTest.run(NonLeakyTestSuite.java:62)
    at androidx.test.internal.runner.junit3.AndroidTestSuite$2.run(AndroidTestSuite.java:101)
    at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:458)
    at java.util.concurrent.FutureTask.run(FutureTask.java:266)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
    at java.lang.Thread.run(Thread.java:764)
    ...
12-28 14:05:51 I/ConsoleReporter: [*****] armeabi-v7a CtsNetTestCases completed in 9s. 0 passed, 1 failed, 0 not executed

确认是不是SELinux规则拒绝的最直接方法:执行 adb shell setenforce 0后再试一次,如果PASS则说明是SELinux规则问题;

此题即是如此:通过userdebug版本,在执行完 adb shell setenforce 0后再次测试,结果PASS;

抓取信息

分析SELinux规则拒绝的问题,优先考虑过滤日志中的avc denied字段;

$ adb logcat -b all | grep avc > ~/avc.log

顺利的话

可以看到如下字样:

12-28 14:05:50.609  9763  9763 W AndroidTestSuit: type=1400 audit(0.0:8127): avc: denied { create } for scontext=u:r:untrusted_app:s0:c61,c256,c512,c768 tcontext=u:r:untrusted_app:s0:c61,c256,c512,c768 tclass=icmp_socket permissive=0

如果没看到

抓取完整日志:

$ adb logcat -b all > ~/log.txt

然后查看测试项Fail时间段的日志,查看是否存在如下信息:

01-01 00:51:20.421     0     0 W audit   : audit_lost=77629 audit_rate_limit=5 audit_backlog_limit=64
01-01 00:51:20.421     0     0 E audit   : rate limit exceeded

这个表示,audit记录了5条avc denied的行为,略去了后续发生的avc denied,因此logcat没有显示出需要的信息;

要将这些过滤掉的信息打印出来,可以尝试如下修改:

$ nano system/core/logd/libaudit.c
int audit_setup(int fd, pid_t pid) {
	...
    status.mask = AUDIT_STATUS_PID/* | AUDIT_STATUS_RATE_LIMIT*/; //去掉AUDIT_STATUS_RATE_LIMIT这个mask
	...
}

重新编译logd,push进去重启即可生效;

分析

简易版

以Ubuntu为例,如果执行audit2allow提示没有安装,则执行:

$ sudo apt install policycoreutils

安装完成后,执行:

$ audit2allow -i ~/avc.log

即可解析出需要添加的规则:

#============= untrusted_app ==============
allow untrusted_app self:icmp_socket create;

将其添加到对应的untrusted_app.te文件中,编译即可生效;

进阶版

根据日志手动分析:

12-28 14:05:50.609  9763  9763 W AndroidTestSuit: type=1400 audit(0.0:8127): avc: denied { create } for scontext=u:r:untrusted_app:s0:c61,c256,c512,c768 tcontext=u:r:untrusted_app:s0:c61,c256,c512,c768 tclass=icmp_socket permissive=0

格式化成如下信息:

ops={ create } 
scontext=u:r:untrusted_app:s0:c61,c256,c512,c768
tcontext=u:r:untrusted_app:s0:c61,c256,c512,c768
tclass=icmp_socket

拼接格式如下:

allow ${scontext} ${tcontext}:${tclass} ${ops};

即:

allow untrusted_app untrusted_app:icmp_socket {create};

又由于:

  • scontext此处与tcontext一致,因此后者可以用self代替;
  • ${ops}为单一操作,可以不带花括号;

由此可得:

allow untrusted_app self:icmp_socket create;

audit2allow分析出的结果一致;

Logo

更多推荐