驗證 SELinux

Android 強烈鼓勵 OEM 徹底測試其 SELinux 實作。當製造商實施 SELinux 時,他們應該先將新策略應用於裝置測試池。

套用新策略後,透過發出指令getenforce確保 SELinux 在裝置上以正確的模式運作。

這會列印全域 SELinux 模式:Enforcing 或 Permissive。要確定每個網域的 SELinux 模式,您必須檢查對應的檔案或使用對應的 ( -p ) 標誌來執行最新版本的sepolicy-analyze ,該標誌位於/platform/system/sepolicy/tools/中。

閱讀否認

檢查錯誤,這些錯誤會作為事件日誌路由到dmesglogcat ,並且可以在裝置上本地查看。製造商應檢查這些設備上dmesg的 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)告訴您啟動該操作的上下文。在本例中,這是作為 shell 運行的東西。
  • 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
  • Actor - scontext (來源上下文)條目代表參與者,在本例中為rmt_storage守護程式。
  • 物件- tcontext (目標上下文)條目表示正在執行操作的對象,在本例中為 kmem。
  • 結果- tclass (目標類別)條目指示所操作的物件的類型,在本例中為chr_file (字元裝置)。

轉儲用戶和核心堆疊

在某些情況下,事件日誌中包含的資訊不足以找出拒絕的根源。收集呼叫鏈(包括核心和使用者空間)通常很有用,可以更好地理解拒絕發生的原因。

最近的核心定義了一個名為avc:selinux_audited追蹤點。使用 Android simpleperf啟用此追蹤點並捕獲呼叫鏈。

支援的配置

  • Linux 核心 >= 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

儘管如此,必須小心檢查每個潛在的添加是否超出權限。例如,向audit2allow提供前面顯示的rmt_storage拒絕會產生以下建議的SELinux策略聲明:

#============= shell ==============
allow shell kernel:security setenforce;
#============= rmt ==============
allow rmt kmem_device:chr_file { read write };

這將授予rmt寫入內核記憶體的能力,這是一個明顯的安全漏洞。通常, audit2allow語句只是一個起點。使用這些語句後,您可能需要變更來源網域和目標標籤,並合併適當的巨集,以獲得良好的策略。有時,正在審查的拒絕根本不應導致任何政策變更;相反,應該更改有問題的應用程式。