驗證 SELinux

Android 強烈建議原始設備製造商 (OEM) 徹底測試 SELinux 實作方式。製造商導入 SELinux 時,應先將新政策套用至裝置的測試區。

套用新政策後,請發出 getenforce 指令,確認 SELinux 在裝置上執行的模式正確無誤。

這會列印全域 SELinux 模式:「強制執行」或「寬容模式」。如要判斷每個網域的 SELinux 模式,您必須檢查對應的檔案,或執行最新版本的 sepolicy-analyze,並加上 /platform/system/sepolicy/tools/ 中的適當 (-p) 標記。

讀取拒絕內容

檢查是否有錯誤。這些錯誤會以事件記錄的形式轉送至 dmesglogcat,且可在裝置本機中查看。製造商應檢查這些裝置的 SELinux 輸出內容,並在公開發布時使用寬鬆模式,最後切換至強制模式,以便進一步調整設定。SELinux 記錄訊息包含 avc:,因此可以輕鬆透過 grep 找到。您可以執行 cat /proc/kmsg 來擷取進行中的拒絕記錄,或執行 cat /sys/fs/pstore/console-ramoops 以擷取上次啟動的拒絕記錄。

SELinux 錯誤訊息會在啟動後受到頻率限制,以免造成記錄資料過重。如要確保您可以看到所有相關訊息,您可以執行 adb shell auditctl -r 0 以停用此功能。

有了這項輸出內容,製造商就能輕鬆找出系統使用者或元件違反 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) 會指出哪個結構定義啟動了動作。本例是以殼層執行。
  • 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 Daemon。
  • 物件tcontext (目標背景資訊) 項目代表要執行的物件,在本例中為 kmem。
  • 結果 - tclass (目標類別) 項目代表要執行操作的物件類型,在本例中為 chr_file (字元裝置)。

傾印使用者和核心堆疊

在某些情況下,事件記錄中包含的資訊不足以查明拒絕的來源。收集呼叫鏈 (包括核心和使用者空間) 通常很有幫助,可讓您更瞭解拒絕的原因。

最近的核心會定義名為 avc:selinux_audited 的追蹤點。使用 Android simpleperf 啟用這個追蹤點並擷取呼叫鏈。

支援的設定

  • Linux kernel >= 5.10,尤其是 Android 通用核心分支版本 mainlineandroid12-5.10。另外也支援 android12-5.4 分支版本。您可以使用 simpleperf 判斷裝置是否已定義追蹤點:adb root && adb shell simpleperf list | grep avc:selinux_audited。至於其他核心版本,您可以選取 dd8166230969bc 修訂版本。
  • 您應該可以重現您正在偵錯的事件。使用 simpleperf 時,系統不支援啟動時間事件;不過,您還是可以重新啟動服務來觸發事件。

擷取呼叫鏈

第一步是使用 simpleperf record 記錄事件:

adb shell -t "cd /data/local/tmp && su root simpleperf record -a -g -e avc:selinux_audited"

接著,應該觸發造成拒絕的事件。之後,系統就會停止錄音。在此範例中,只要使用 Ctrl-c,系統應該已擷取範例:

^Csimpleperf I cmd_record.cpp:751] Samples recorded: 1. Samples lost: 0.

最後,simpleperf report 可用於檢查擷取的堆疊追蹤。例如:

adb shell -t "cd /data/local/tmp && su root simpleperf report -g --full-callgraph"
[...]
Children  Self     Command  Pid   Tid   Shared Object                                   Symbol
100.00%   0.00%    dmesg    3318  3318  /apex/com.android.runtime/lib64/bionic/libc.so  __libc_init
       |
       -- __libc_init
          |
           -- main
              toybox_main
              toy_exec_which
              dmesg_main
              klogctl
              entry_SYSCALL_64_after_hwframe
              do_syscall_64
              __x64_sys_syslog
              do_syslog
              selinux_syslog
              slow_avc_audit
              common_lsm_audit
              avc_audit_post_callback
              avc_audit_post_callback

上述呼叫鏈結是統一核心和使用者空間呼叫鏈結。從使用者空間開始到發生遭拒情形的核心,這可讓您更清楚程式碼流程。如要進一步瞭解 simpleperf,請參閱 Simpleperf 可執行指令參考資料

切換為寬鬆模式

在 userdebug 或 eng 版本中,您可以使用 ADB 停用 SELinux 強制執行機制。如要這麼做,請先執行 adb root,將 ADB 切換為 Root 權限。接著,如要停用 SELinux 強制執行功能,請執行:

adb shell setenforce 0

或透過核心指令列 (早期裝置啟動期間):

androidboot.selinux=permissive
androidboot.selinux=enforcing

或是在 Android 12 中使用 bootconfig:

androidboot.selinux=permissive
androidboot.selinux=enforcing

使用 audit2allow

audit2allow 工具會將 dmesg 拒絕項目轉換為相應的 SELinux 政策陳述式。因此可大幅加快 SELinux 的開發速度。

如要使用此功能,請執行以下指令:

adb pull /sys/fs/selinux/policy
adb logcat -b events -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 陳述式只是起點。使用這些陳述式後,您可能需要變更目標的來源網域和標籤,並納入適當的巨集,才能制定良好的政策。有時候,即使遭到檢查的拒絕,也應該不會導致任何政策變更,不應該變更違規的應用程式。