blob: 2e0f3783044de01d6d9269d80d3e7d065b70d6bf [file] [log] [blame]
<html devsite><head>
<title>验证 SELinux</title>
<meta name="project_path" value="/_project.yaml"/>
<meta name="book_path" value="/_book.yaml"/>
</head>
<body>
<!--
Copyright 2017 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<p>Android 强烈建议 OEM 全面测试其 SELinux 实现。制造商在实现 SELinux 时,应先在一组测试设备上实施新政策。</p>
<p>实施新政策后,您可以通过执行 <code>getenforce</code> 命令来确认 SELinux 在设备上的运行模式是否正确。</p>
<p>该命令会输出全局 SELinux 模式:强制或宽容。要确定每个域的 SELinux 模式,您必须检查相应的文件,或运行带有相应 (<code>-p</code>) 标记的最新版 <code>sepolicy-analyze</code>(位于 <a href="https://android.googlesource.com/platform/system/sepolicy/+/master/tools/" class="external"><code>/platform/system/sepolicy/tools/</code></a> 中)。</p>
<h2 id="reading_denials">阅读拒绝事件</h2>
<p>检查是否有错误,错误会以事件日志的形式传给 <code>dmesg</code><code>logcat</code>,并可在设备上从本地查看。制造商应先检查这些设备上传给 <code>dmesg</code> 的 SELinux 输出并优化设置,然后再在宽容模式下公开发布,最后切换到强制模式。SELinux 日志消息中包含“<code>avc:</code>”字样,因此可使用 <code>grep</code> 轻松找到。您可以通过运行 <code>cat /proc/kmsg</code> 来获取当前的拒绝事件日志,也可以通过运行 <code>cat /sys/fs/pstore/console-ramoops</code> 来获取上次启动时的拒绝事件日志。</p>
<p>根据这些输出内容,制造商可以轻松发现系统用户或组件违反 SELinux 政策的行为。然后,制造商便可更改相应软件和/或 SELinux 政策,以防范此类恶意行为。</p>
<p>具体来说,这些日志消息会指明在强制模式下哪些进程会失败以及失败原因。示例如下:</p>
<pre>
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
</pre>
<p>该输出的解读如下:</p>
<ul>
<li>上方的 <code>{ connectto }</code> 表示执行的操作。根据它和末尾的 <code>tclass</code> (<code>unix_stream_socket</code>),您可以大致了解是对什么对象执行什么操作。在此例中,是操作方正在试图连接到 UNIX 信息流套接字。
</li><li><code>scontext (u:r:shell:s0)</code> 表示发起相应操作的环境,在此例中是 shell 中运行的某个程序。
</li><li><code>tcontext (u:r:netd:s0)</code> 表示操作目标的环境,在此例中是归 <code>netd</code> 所有的某个 unix_stream_socket。
</li><li>顶部的 <code>comm="ping"</code> 可帮助您了解拒绝事件发生时正在运行的程序。在此示例中,给出的信息非常清晰明了。
</li></ul>
<p>我们再看看另一个示例:</p>
<pre class="devsite-terminal devsite-click-to-copy">adb shell su root dmesg | grep 'avc: '</pre>
<p>输出:</p>
<pre>
&lt;5&gt; 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
</pre>
<p>以下是此拒绝事件的关键元素:</p>
<ul>
<li><em></em>操作 - 试图进行的操作会使用括号突出显示:<code>read write</code><code>setenforce</code>
</li><li><em></em>操作方 - <code>scontext</code>(来源上下文)条目表示操作方;在此例中为<code> rmt_storage</code> 守护进程。
</li><li><em></em>对象 - <code>tcontext</code>(目标上下文)条目表示对哪个对象执行操作;在此例中为 kmem。
</li><li><em></em>结果 - <code>tclass</code>(目标类别)条目表示操作对象的类型;在此例中为 <code>chr_file</code>(字符设备)。
</li></ul>
<h2 id="switching_to_permissive">切换到宽容模式</h2>
<p class="caution"><strong>重要提示</strong>:生产设备不支持宽容模式。CTS 测试会确认是否已启用强制模式。</p>
<p>SELinux 强制模式可以在 userdebug 或 eng 版本中通过 ADB 停用。为此,请先运行 <code>adb root</code> 以将 ADB 切换为 root 权限。然后,要停用 SELinux 强制模式,请运行以下命令:
</p><pre class="devsite-terminal devsite-click-to-copy">
adb shell setenforce 0
</pre>
<p>或在内核命令行中输入以下命令(适用于设备开发初期):</p>
<pre class="devsite-click-to-copy">
<code class="devsite-terminal">androidboot.selinux=permissive</code>
<code class="devsite-terminal">androidboot.selinux=enforcing</code>
</pre>
<h2 id="using_audit2allow">使用 audit2allow</h2>
<p><code>selinux/policycoreutils/audit2allow</code> 工具可以获取 <code>dmesg</code> 拒绝事件并将其转换成相应的 SELinux 政策声明。因此,该工具有助于大幅加快 SELinux 开发速度。
<code>audit2allow</code> 包含在 Android 源代码树中,会在您基于源代码编译 Android 时自动编译。</p>
<p>要使用该工具,请运行以下命令:</p>
<pre class="devsite-click-to-copy">
<code class="devsite-terminal">adb pull /sys/fs/selinux/policy</code>
<code class="devsite-terminal">adb logcat -b all -d | audit2allow -p policy</code>
</pre>
<p class="note"><strong>注意</strong>:运行这些命令不会更改 bugreport.txt,因为所有日志都已经存在,包括上次重新启动之前存在的日志。在设备进行 OTA 更新或向设备刷入开发版系统时,新旧违规行为会混杂在一起,直到下一次重新启动为止。要解决此问题,请重新启动设备,或者从您的错误报告中滤除 console-ramoops 和 LAST_LOGCAT。
</p>
<p>不过,请务必仔细审核要添加到政策中的条目,以免出现权限过宽的情况。例如,如果将上面的 <code>rmt_storage</code> 拒绝事件输入到 audit2allow 中,会生成以下 SELinux 政策声明建议:</p>
<pre>
#============= shell ==============
allow shell kernel:security setenforce;
#============= rmt ==============
allow rmt kmem_device:chr_file { read write };
</pre>
<p>这会授予 <code>rmt</code> 向内核内存写入内容的权限,从而形成明显的安全漏洞。通常情况下,<code>audit2allow</code> 给出的声明建议只是一个大致的基础。在添加这些声明后,您可能需要更改来源域和目标标签,并纳入适当的宏,才能实现良好的政策配置。有时,应对拒绝事件的合理方式不是更改政策,而是更改违规的应用。</p>
</body></html>