Android 開(kāi)放源代碼項(xiàng)目 (AOSP) 針對(duì)所有 Android 設(shè)備中常用的應(yīng)用和服務(wù)提供了一個(gè)可靠實(shí)用的基本政策。AOSP 的貢獻(xiàn)者會(huì)定期完善該政策。核心政策應(yīng)占設(shè)備上最終政策的 90-95%,而剩下的 5-10% 則為設(shè)備專用自定義政策。本文重點(diǎn)介紹這些設(shè)備專用自定義政策、編寫(xiě)設(shè)備專用政策的方法,以及在編寫(xiě)此類政策時(shí)要避免的一些陷阱。
<devsite-heading text="設(shè)備啟動(dòng)" for="device_bringup" level="h2" link="" toc="" class="" back-to-top="">## 設(shè)備啟動(dòng)</devsite-heading> <devsite-heading text="設(shè)備啟動(dòng)" for="device_bringup" level="h2" link="" toc="" class="" back-to-top=""></devsite-heading>
在編寫(xiě)設(shè)備專用政策時(shí),請(qǐng)按照下列步驟操作。
<devsite-heading text="在寬容模式下運(yùn)行" for="run_in_permissive_mode" level="h3" link="" toc="" class="">### 在寬容模式下運(yùn)行</devsite-heading>
當(dāng)設(shè)備處于寬容模式時(shí),拒絕事件會(huì)被記錄下來(lái),但不會(huì)被強(qiáng)制執(zhí)行。寬容模式非常重要,原因有以下兩點(diǎn):
- 寬容模式可確保政策啟用不會(huì)延誤其他早期設(shè)備啟動(dòng)任務(wù)。
- 被強(qiáng)制執(zhí)行的拒絕事件可能會(huì)掩蓋其他拒絕事件。例如,文件訪問(wèn)通常會(huì)涉及目錄搜索、文件打開(kāi)和文件讀取操作。在強(qiáng)制模式下,只會(huì)發(fā)生目錄搜索拒絕事件。寬容模式可確保所有拒絕事件都會(huì)顯示出來(lái)。
要使設(shè)備進(jìn)入寬容模式,最簡(jiǎn)單的方法是使用內(nèi)核命令行來(lái)實(shí)現(xiàn)。相應(yīng)命令可以添加到設(shè)備的 BoardConfig.mk 文件中:platform/device/<vendor>/<target>/BoardConfig.mk。修改命令行之后,執(zhí)行 make clean,接著執(zhí)行 make bootimage,然后刷寫(xiě)新的啟動(dòng)映像。
在此之后,通過(guò)以下命令確認(rèn)寬容模式:
<devsite-code><pre class="devsite-terminal" is-upgraded="">adb shell getenforce
</pre></devsite-code>
將處于全局寬容模式的時(shí)間設(shè)為兩周比較合理。在解決大多數(shù)拒絕事件之后,返回到強(qiáng)制模式,并在出現(xiàn)錯(cuò)誤時(shí)加以解決。對(duì)于仍然不斷出現(xiàn)拒絕事件的域或仍處于密集開(kāi)發(fā)階段的服務(wù),可以暫時(shí)使其進(jìn)入寬容模式,但要盡快使其返回到強(qiáng)制模式。
<devsite-heading text="提早采用強(qiáng)制模式" for="enforce_early" level="h3" link="" toc="" class="">### 提早采用強(qiáng)制模式</devsite-heading>
在強(qiáng)制模式下,拒絕事件會(huì)被記錄下來(lái),并且會(huì)被強(qiáng)制執(zhí)行。最佳做法是盡早使您的設(shè)備進(jìn)入強(qiáng)制模式。如果花時(shí)間等待創(chuàng)建和強(qiáng)制執(zhí)行設(shè)備專用政策,通常會(huì)導(dǎo)致有問(wèn)題的產(chǎn)品和糟糕的用戶體驗(yàn)。要提前足夠長(zhǎng)的時(shí)間開(kāi)始參與 dogfooding,確保對(duì)實(shí)際使用中涉及的功能進(jìn)行全面測(cè)試。提早開(kāi)始有助于確保安全問(wèn)題能夠在相關(guān)人員做出設(shè)計(jì)決策時(shí)被考慮在內(nèi)。相反,僅根據(jù)觀察到的拒絕事件來(lái)授予權(quán)限是一種不安全的做法??梢岳眠@段時(shí)間對(duì)設(shè)備進(jìn)行安全審核,并針對(duì)不應(yīng)被允許的行為提出錯(cuò)誤。
<devsite-heading text="移除或刪除現(xiàn)有政策" for="remove_or_delete_existing_policy" level="h3" link="" toc="" class="">### 移除或刪除現(xiàn)有政策</devsite-heading>
之所以要在新設(shè)備上從頭開(kāi)始創(chuàng)建設(shè)備專用政策,有很多合理的理由,其中包括:
- 安全審核
- 過(guò)度寬容的政策
- 政策規(guī)??s小
- Dead 政策
<devsite-heading text="解決核心服務(wù)生成的拒絕事件" for="address_denials_of_core_services" level="h3" link="" toc="" class="">### 解決核心服務(wù)生成的拒絕事件</devsite-heading>
核心服務(wù)生成的拒絕事件通常是通過(guò)為文件添加標(biāo)簽來(lái)解決的。例如:
<devsite-code><pre is-upgraded="">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
</pre></devsite-code>
是完全通過(guò)為 /dev/kgsl-3d0 添加適當(dāng)?shù)臉?biāo)簽來(lái)解決的。在此示例中,tcontext 是 device。這表示默認(rèn)環(huán)境,在該環(huán)境中,/dev 中的內(nèi)容都會(huì)獲得 device 標(biāo)簽(被分配了更具體的標(biāo)簽的內(nèi)容除外)。直接在此處接受來(lái)自 audit2allow 的輸出會(huì)導(dǎo)致不正確且過(guò)度寬容的規(guī)則。
要解決這種問(wèn)題,可以為文件添加更具體的標(biāo)簽,在此示例中為 gpu_device。由于 mediaserver 在核心政策中已有訪問(wèn) gpu_device 所需的必要權(quán)限,因此不再需要更多權(quán)限。
其他需要以核心政策中預(yù)定義的類型作為標(biāo)簽的設(shè)備專用文件:
一般情況下,向默認(rèn)標(biāo)簽授予權(quán)限的做法是錯(cuò)誤的。其中許多權(quán)限都是 neverallow 規(guī)則所不允許的,但即使該規(guī)則并未明確禁止這些權(quán)限,也最好是提供具體標(biāo)簽。
<devsite-heading text="為新服務(wù)添加標(biāo)簽并解決拒絕事件" for="label_new_services_and_address_denials" level="h3" link="" toc="" class="">### 為新服務(wù)添加標(biāo)簽并解決拒絕事件</devsite-heading>
通過(guò) init 啟動(dòng)的服務(wù)需要在各自的 SELinux 域中運(yùn)行。以下示例會(huì)將服務(wù)“foo”放入它自己的 SELinux 域中并為其授予權(quán)限。
該服務(wù)是在設(shè)備的 init.<var>device</var>.rc 文件中啟動(dòng)的,如下所示:
<devsite-code><pre class="" is-upgraded="">service foo /system/bin/foo
class core
</pre></devsite-code>
-
創(chuàng)建一個(gè)新域“foo”
創(chuàng)建包含以下內(nèi)容的文件
device/<var>manufacturer</var>/<var>device-name</var>/sepolicy/foo.te:<devsite-code><pre class="" is-upgraded=""># foo service
type foo, domain;
type foo_exec, exec_type, file_type;init_daemon_domain(foo)
</pre></devsite-code>
-
這是 foo SELinux 域的初始模板,您可以根據(jù)該可執(zhí)行文件執(zhí)行的具體操作為該模板添加規(guī)則。
- 為
/system/bin/foo添加標(biāo)簽
將以下內(nèi)容添加到
device/<var>manufacturer</var>/<var>device-name</var>/sepolicy/file_contexts:<devsite-code><pre class="" is-upgraded="">/system/bin/foo u:object_r:foo_exec:s0
</pre></devsite-code> - 為
這可確保為該可執(zhí)行文件添加適當(dāng)?shù)臉?biāo)簽,以便 SELinux 在適當(dāng)?shù)挠蛑羞\(yùn)行相應(yīng)服務(wù)。
編譯并刷寫(xiě)啟動(dòng)映像和系統(tǒng)映像。
-
優(yōu)化相應(yīng)域的 SELinux 規(guī)則。
根據(jù)拒絕事件確定所需的權(quán)限。audit2allow 工具提供了一些實(shí)用的指南,但該工具僅適用于提供編寫(xiě)政策時(shí)所需的信息。切勿只是復(fù)制輸出內(nèi)容。
<devsite-heading text="切換回強(qiáng)制模式" for="enforcing_mode" level="h3" link="" toc="" class="">### 切換回強(qiáng)制模式</devsite-heading>
可以在寬容模式下進(jìn)行問(wèn)題排查,但要盡早切換回強(qiáng)制模式,并盡量保持該模式。
<devsite-heading text="常見(jiàn)錯(cuò)誤" for="common_mistakes" level="h2" link="" toc="" class="" back-to-top="">## 常見(jiàn)錯(cuò)誤</devsite-heading> <devsite-heading text="常見(jiàn)錯(cuò)誤" for="common_mistakes" level="h2" link="" toc="" class="" back-to-top=""></devsite-heading>
下面介紹了在編寫(xiě)設(shè)備專用政策時(shí)發(fā)生的常見(jiàn)錯(cuò)誤的一些解決方法。
<devsite-heading text="過(guò)度使用否定" for="overuse_of_negation" level="h3" link="" toc="" class="">### 過(guò)度使用否定</devsite-heading>
以下示例規(guī)則類似于鎖著前門(mén),但開(kāi)著窗戶:
<devsite-code><pre is-upgraded="">allow { domain -untrusted_app } scary_debug_device:chr_file rw_file_perms</pre></devsite-code>
該規(guī)則的意圖很明確:除了第三方應(yīng)用之外,其他所有應(yīng)用都可以訪問(wèn)調(diào)試設(shè)備。
該規(guī)則存在幾個(gè)方面的缺陷。排除 untrusted_app 所起到的效果微不足道,因?yàn)樗袘?yīng)用都可以選擇在 isolated_app 網(wǎng)域中運(yùn)行服務(wù)。同樣,如果第三方應(yīng)用的新域被添加到了 AOSP,它們也可以訪問(wèn) scary_debug_device。該規(guī)則過(guò)于寬容。對(duì)于大多數(shù)域來(lái)說(shuō),能夠訪問(wèn)該調(diào)試工具并不能使它們獲益。該規(guī)則應(yīng)編寫(xiě)為僅允許需要訪問(wèn)該調(diào)試工具的域。
<devsite-heading text="正式版中的調(diào)試功能" for="debugging_features_in_production" level="h3" link="" toc="" class="">### 正式版中的調(diào)試功能</devsite-heading>
調(diào)試功能及其政策不應(yīng)存在于正式版中。
最簡(jiǎn)單的替代方案是,僅當(dāng) eng/userdebug 版本中停用了 SELinux 時(shí),才允許使用調(diào)試功能,例如 adb root 和 adb shell setenforce 0。
另一種安全的替代方案是在 userdebug_or_eng 聲明中包含調(diào)試權(quán)限。
<devsite-heading text="政策規(guī)模擴(kuò)張" for="policy_size_explosion" level="h3" link="" toc="" class="">### 政策規(guī)模擴(kuò)張</devsite-heading>
在 Wild 中描述 SEAndroid 政策中介紹了一個(gè)令人關(guān)注的設(shè)備政策自定義發(fā)展趨勢(shì)。設(shè)備專用政策應(yīng)占設(shè)備上運(yùn)行的所有政策的 5-10%。如果自定義政策所占的比例超過(guò) 20%,則幾乎肯定會(huì)包含超特權(quán)域和 Dead 政策。
過(guò)大的政策:
- 由于此類政策位于 ramdisk 中,并且還會(huì)加載到內(nèi)核內(nèi)存中,因此會(huì)占據(jù)兩倍的內(nèi)存。
- 需要較大的啟動(dòng)映像,浪費(fèi)磁盤(pán)空間。
- 影響運(yùn)行時(shí)政策查詢次數(shù)。
以下示例顯示了制造商專用政策分別占設(shè)備上政策 50% 和 40% 的兩種設(shè)備。重寫(xiě)政策大幅提高了安全性,而且功能方面沒(méi)有任何損失,如下所示。(AOSP 設(shè)備 Shamu 和 Flounder 也包含在了該示例中,以便進(jìn)行比較。)

在兩種設(shè)備中,政策的規(guī)模和權(quán)限數(shù)量都大大減小了。政策規(guī)模的減小幾乎完全是因?yàn)橐瞥瞬槐匾臋?quán)限,其中許多權(quán)限可能是由 audit2allow 生成且被隨意添加到政策中的規(guī)則。對(duì)于這兩種設(shè)備來(lái)說(shuō),Dead 域也是一個(gè)問(wèn)題。
<devsite-heading text="授予 dac_override 權(quán)限" for="granting_the_dac_override_capability" level="h3" link="" toc="" class="">### 授予 dac_override 權(quán)限</devsite-heading>
dac_override 拒絕事件意味著違規(guī)進(jìn)程正在嘗試使用錯(cuò)誤的 unix user/group/world 權(quán)限訪問(wèn)某個(gè)文件。正確的解決方案幾乎從不授予 dac_override 權(quán)限,而是更改相應(yīng)文件或進(jìn)程的 unix 權(quán)限。有些域(例如 init、vold 和 installd)確實(shí)需要能夠替換 unix 文件權(quán)限才能訪問(wèn)其他進(jìn)程的文件。要查看更深入的講解,請(qǐng)?jiān)L問(wèn) Dan Walsh 的博客。