Android 開源專案 (AOSP) 為所有 Android 裝置上通用的應用程式和服務提供了堅實的基礎策略。 AOSP 的貢獻者定期完善此政策。核心策略預計將佔最終設備上策略的 90-95% 左右,特定於設備的自訂策略將佔剩餘的 5-10%。本文重點介紹這些特定於設備的自訂、如何編寫特定於設備的策略以及在此過程中要避免的一些陷阱。
設備啟動
在編寫特定於設備的策略時,請按照以下步驟操作。
在寬容模式下運行
當設備處於寬容模式時,會記錄拒絕但不會強制執行。寬容模式很重要,原因有二:
- 寬容模式可確保策略啟動不會延遲其他早期設備啟動任務。
- 強制拒絕可能會掩蓋其他拒絕。例如,檔案存取通常需要目錄搜尋、檔案打開,然後檔案讀取。在強制模式下,只會發生目錄搜尋拒絕。寬容模式確保所有拒絕都被看見。
將設備置於許可模式的最簡單方法是使用內核命令列。可以將其新增至裝置的BoardConfig.mk
檔案: platform/device/<vendor>/<target>/BoardConfig.mk
。修改命令列後,執行make clean
,然後make bootimage
,並重新整理新的啟動映像。
之後,透過以下方式確認寬容模式:
adb shell getenforce
兩週是處於全局許可模式的合理時間。解決大部分拒絕問題後,回到強制模式並解決出現的錯誤。仍然產生拒絕的域或仍在大力開發的服務可以暫時置於寬鬆模式,但盡快將其移回強制模式。
及早執行
在強制模式下,拒絕會被記錄並強制執行。最佳做法是儘早讓您的裝置進入強制模式。等待創建和實施特定於設備的策略通常會導致產品有缺陷和糟糕的用戶體驗。儘早開始參與測試並確保在實際使用中功能的完整測試覆蓋。儘早開始可確保安全問題為設計決策提供依據。相反,僅根據觀察到的拒絕授予權限是一種不安全的方法。利用這段時間對設備進行安全審核,並針對不應允許的行為記錄錯誤。
刪除或刪除現有策略
在新設備上從頭開始創建特定於設備的策略有很多充分的理由,其中包括:
解決核心服務被拒絕的問題
核心服務產生的拒絕通常透過檔案標籤來解決。例如:
avc: denied { open } for pid=1003 comm=”mediaserver” path="/dev/kgsl-3d0” dev="tmpfs" scontext=u:r:mediaserver:s0 tcontext=u:object_r:device:s0 tclass=chr_file permissive=1 avc: denied { read write } for pid=1003 name="kgsl-3d0" dev="tmpfs" scontext=u:r:mediaserver:s0 tcontext=u:object_r:device:s0 tclass=chr_file permissive=1
透過正確標記/dev/kgsl-3d0
可以完全解決問題。在此範例中, tcontext
是device
。這表示預設上下文,其中/dev
中的所有內容都會接收“設備”標籤,除非分配了更具體的標籤。這裡簡單地接受audit2allow的輸出會導致不正確且過於寬鬆的規則。
若要解決此類問題,請為檔案指定更具體的標籤,在本例中為gpu_device 。不需要進一步的權限,因為媒體伺服器已經在核心策略中擁有存取 gpu_device 的必要權限。
應使用核心策略中預先定義的類型來標記的其他特定於設備的檔案:
一般來說,向預設標籤授予權限是錯誤的。其中許多權限都被neverallow規則禁止,但即使沒有明確禁止,最佳實踐也是提供特定標籤。
標記新服務並解決拒絕問題
Init 啟動的服務需要在自己的 SELinux 網域中運作。以下範例將服務「foo」放入自己的 SELinux 網域中並授予其權限。
該服務在我們設備的init. device .rc
檔為:
service foo /system/bin/foo class core
- 建立一個新網域“foo”
使用以下內容建立
device/ manufacturer / device-name /sepolicy/foo.te
:# foo service type foo, domain; type foo_exec, exec_type, file_type; init_daemon_domain(foo)
這是 foo SELinux 網域的初始模板,您可以根據該執行檔執行的特定操作向其中新增規則。
- 標籤
/system/bin/foo
將以下內容加入
device/ manufacturer / device-name /sepolicy/file_contexts
:/system/bin/foo u:object_r:foo_exec:s0
這可以確保可執行檔被正確標記,以便 SELinux 在正確的網域中執行該服務。
- 建置並刷新啟動和系統映像。
- 細化域的 SELinux 規則。
使用拒絕來確定所需的權限。 Audit2allow工具提供了很好的指南,但僅用它來為策略編寫提供資訊。不要只複製輸出。
切換回強制模式
可以在寬容模式下進行故障排除,但請儘早切換回強制模式並嘗試保持該模式。
常見錯誤
以下是針對編寫特定於裝置的策略時發生的常見錯誤的一些解決方案。
過度使用否定
以下範例規則就像鎖上前門但保持窗戶打開一樣:
allow { domain -untrusted_app } scary_debug_device:chr_file rw_file_perms
意圖很明確:除了第三方應用程式之外的所有人都可以存取調試設備。
該規則在幾個方面有缺陷。排除untrusted_app
很容易解決,因為所有應用程式都可以選擇在isolated_app
域中執行服務。同樣,如果第三方應用程式的新網域已新增至 AOSP,它們也將有權存取scary_debug_device
。此規則過於寬鬆。大多數網域不會從存取此偵錯工具中受益。該規則應編寫為僅允許需要存取的網域。
生產中的調試功能
偵錯功能不應出現在生產版本中,其策略也不應出現。
最簡單的替代方案是僅在 eng/userdebug 版本上停用 SELinux 時才允許偵錯功能,例如adb root
和adb shell setenforce 0
。
另一種安全的替代方法是將偵錯權限包含在userdebug_or_eng語句中。
保單規模爆炸
對野外 SEAndroid 策略的描述描述了設備策略客製化成長的一個令人擔憂的趨勢。設備特定的策略應佔設備上運作的整體策略的 5-10%。 20% 以上範圍內的自訂幾乎肯定包含過多的特權網域和死策略。
不必要的大政策:
- 由於策略位於 ramdisk 中並且也被載入到核心記憶體中,因此對記憶體造成了雙重打擊。
- 由於需要更大的引導映像而浪費磁碟空間。
- 影響運行時策略查找時間。
以下範例顯示了兩個設備,其中製造商特定的策略佔設備上策略的 50% 和 40%。策略的重寫帶來了顯著的安全性改進,並且沒有功能損失,如下所示。 (AOSP 設備 Shamu 和 Flounder 也包括在內以供比較。)
在這兩種情況下,策略的大小和權限數量都大幅減少。策略大小的減少幾乎完全是由於刪除了不必要的權限,其中許多權限可能是由audit2allow
產生的規則,不加區別地添加到策略中。對於這兩種設備來說,死域也是一個問題。
授予 dac_override 功能
dac_override
拒絕表示違規程序正在嘗試使用不正確的 UNIX 使用者/群組/世界權限存取檔案。正確的解決方案是幾乎從不授予dac_override
權限。相反,更改檔案或進程的 UNIX 權限。有些網域(例如init
、 vold
和installd
確實需要能夠覆蓋 unix 檔案權限以存取其他進程的檔案。請參閱Dan Walsh 的部落格以獲取更深入的解釋。