Android 強烈建議原始設備製造商 (OEM) 徹底測試 SELinux 實作方式。製造商導入 SELinux 時,應先將新政策套用至裝置的測試區。
套用新政策後,請發出 getenforce
指令,確認 SELinux 在裝置上執行的模式正確無誤。
這會列印全域 SELinux 模式:強制執行或寬鬆。如要判斷每個網域的 SELinux 模式,您必須檢查對應的檔案,或執行最新版本的 sepolicy-analyze
,並加上
/platform/system/sepolicy/tools/
中的適當 (-p
) 標記。
讀取拒絕內容
檢查錯誤,這些錯誤會以事件記錄的形式傳送至 dmesg
和 logcat
,並可在裝置上本機查看。製造商應檢查這些裝置的 SELinux 輸出內容是否為 dmesg
,並在以開放模式公開發布前,對設定進行微調,並最終切換為強制模式。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 串流 Socket。 -
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 write
或setenforce
標示。 - Actor:
scontext
(來源內容) 項目代表 Actor,在本例中為rmt_storage
守護程式。 - 物件:
tcontext
(目標背景資訊) 項目代表要執行的物件,在本例中為 kmem。 - 結果:
tclass
(目標類別) 項目代表要執行的物件類型,在本例中為chr_file
(字元裝置)。
傾印使用者和核心堆疊
在某些情況下,事件記錄中包含的資訊不足以指出拒絕的原因。收集呼叫鏈結 (包括核心和使用者空間) 通常有助於進一步瞭解發生拒絕的原因。
近期的核心定義了名為 avc:selinux_audited
的追蹤點。使用 Android simpleperf
啟用這個追蹤點,並擷取呼叫鏈結。
支援的設定
- 支援 Linux 核心 5.10 以上版本,特別是 Android 常見核心分支的 mainline 和 android12-5.10。也支援 android12-5.4 分支。您可以使用
simpleperf
判斷裝置上是否定義了追蹤點:adb root && adb shell simpleperf list | grep avc:selinux_audited
。如要使用其他核心版本,您可以挑選 dd81662 和 30969bc 的版本。 - 您應該可以重現要偵錯的事件。使用 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
陳述式通常只是起點。使用這些陳述式後,您可能需要變更目標的來源網域和標籤,並納入適當的巨集,才能制定良好的政策。有時,審查中的拒登案件並不會導致任何政策變更,而是應變更違規的應用程式。