Android/SELinux 添加 AVC 權限
背景
在Android應用層中編寫c/c++應用時,發現接口調用出現問題,logcat
才知道是因為:權限不夠。
type=1400 audit(0.0:4): avc: denied { create } for scontext=u:r:bootanim:s0 tcontext=u:r:bootanim:s0 tclass=netlink_socket permissive=0
參考:
- https://blog.csdn.net/qq_27256793/article/details/105245908
- https://blog.csdn.net/m0_46211029/article/details/105157587
介紹
安全增強型 Linux(Security-Enhanced Linux)簡稱 SELinux,它是 Linux 的一個安全子系統。SELinux 主要作用是最大限度地減小系統中服務進程可訪問的資源(最小權限原則)。對資源的訪問控制分為兩類: DAC和MAC
SELinux工作模式
SELinux 有三種工作模式,分別為:
- enforcing: 強制模式, 執行SELinux規則, 違反的行為會被阻止
- permissive: 寬容模式, 執行SELinux規則, 違反的行不會被阻止
- disabled: 關閉SELinux
avc denied
在Android系統開發中, 可能會遇到SELinux的權限不足而引起的各種問題;可以嘗試將SELinux工作模式臨時改為寬容模式看問題是否解決, 來判定是否是SELinux引起的問題
標志性 log: avc: denied { 操作權限 } for pid=7201 comm=“進程名” scontext=u:r:源類型:s0 tcontext=u:r:目標類型:s0 tclass=訪問類型 permissive=0
- 源類型:授予訪問的類型,通常是進程的域類型
- 目標類型:客體的類型,它被授權可以訪問的類型
- 訪問類型
- 操作權限:表示主體對客體訪問時允許的操作類型(也叫做訪問向量)。
舉例
打開三方apk出問題,其中錯誤log如下:
03-28 10:18:07.990 11883 11883 W re-initialized>: type=1400 audit(0.0:2918):
avc: denied { read } for name="u:object_r:mtk_amslog_prop:s0"
dev="tmpfs" ino=7544 scontext=u:r:system_app:s0
tcontext=u:object_r:mtk_amslog_prop:s0
tclass=file permissive=0
解釋:
- read:表示沒有 read 權限
- system_app:system_app 中缺少權限
- mtk_amslog_prop:mtk_amslog_prop 文件系統缺少權限
- file:file 類型的文件
- 分析:adb shell setenforce 0。設置SELinux 成為permissive模式。若apk可正常使用,則進行規避此權限問題
修改selinux規則
type=1400 audit(0.0:4): avc: denied { create } for
scontext=u:r:bootanim:s0
tcontext=u:r:bootanim:s0
tclass=netlink_socket
permissive=0
type=1400 audit(0.0:41): avc: denied { read } for
name="u:object_r:media_prop:s0"
dev="tmpfs" ino=11511
scontext=u:r:mediacodec:s0
tcontext=u:object_r:media_prop:s0
tclass=file
permissive=0
節點 | 尋找點 | 結果 |
---|---|---|
缺少的權限 | denied { 權限 } |
create |
誰(哪個進程)缺少權限 | scontext=u:r:進程:s0 |
bootanim |
對哪個節點缺少權限 | tcontext=u:r:節點:s0 ; |
bootanim 或 self |
缺少要訪問的對象 | tclass=被訪問對象 |
netlink_socket |
技巧:
- 如果
tcontext
中的節點與scontext
中的進程名相同,則可以寫成self
- 因為有時候avc denied 的log不是一次性顯示所有問題,要等解決一個權限問題后,才會顯示另外一個權限問題,比如顯示缺失某個目錄的read權限。所以建議是先參考其他可能出現訪問權限的問題的有關接口關鍵字,進行多次添加。
- 要加入的權限很多時,可以用中括號,比如:
allow untrusted_app vfat:dir { search write }
由於,scontext對應的是我們要修改的文件(這里是bootanim
),根據type=1400 audit(0.0:4): avc: denied { create } for scontext=u:r:bootanim:s0 tcontext=u:r:bootanim:s0 tclass=netlink_socket permissive=0
的提示;因此,需要在bootanim.te
最后面中添加所對應的權限是:
# file : bootanim.te
allow bootanim self:netlink_socket {create};
再看一個例子:
# type=1400 audit(0.0:41): avc: denied { read } for
# name="u:object_r:media_prop:s0"
# dev="tmpfs" ino=11511
# scontext=u:r:mediacodec:s0
# tcontext=u:object_r:media_prop:s0
# tclass=file permissive=0
# file: mediacodec.te
allow mediacodec media_prop:file {read}
修改系統selinux模式
臨時修改
adb shell setenforce 0 #設置SELinux 成為permissive模式
adb shell setenforce 1 #設置SELinux 成為enforcing模式
adb shell getenforce #獲取SELinux狀態(permissive,enforcing,disabled)
永久修改
安卓7
路徑:system/core/init/init.cpp
static selinux_enforcing_status selinux_status_from_cmdline() {
selinux_enforcing_status status = SELINUX_ENFORCING; // 強制模式, 執行SELinux規則, 違反的行為會被阻止
//selinux_enforcing_status status = SELINUX_PERMISSIVE;// 寬容模式, 執行SELinux規則, 違反的行不會被阻止
import_kernel_cmdline(false, [&](const std::string& key, const std::string& value, bool in_qemu) {
if (key == "androidboot.selinux" && value == "permissive") {
status = SELINUX_PERMISSIVE;
}
});
return status;
}
安卓9/10
路徑:system/core/init/selinux.cpp
EnforcingStatus StatusFromCmdline() {
// EnforcingStatus status = SELINUX_ENFORCING;// 強制模式, 執行SELinux規則, 違反的行為會被阻止
EnforcingStatus status = SELINUX_PERMISSIVE; // 寬容模式, 執行SELinux規則, 違反的行不會被阻止
import_kernel_cmdline(false,
[&](const std::string& key, const std::string& value, bool in_qemu) {
if (key == "androidboot.selinux" && value == "permissive") {
status = SELINUX_PERMISSIVE;
}
});
return status;
}
附錄A:在permissive模式下,有些app仍然無法訪問具體節點
敏感權限的特征:log中帶有c512,c768字樣
試着在untrusted_app.te 中添加了allow untrusted_app audio_device:chr_file { open write read };
還是報如下權限錯誤:
[ 141.935275] type=1400 audit(1546939304.786:43): avc: denied { write } for
pid=1836 comm="Thread-4"
name="pcmC0D1c"
dev="tmpfs"
ino=11947
scontext=u:r:untrusted_app:s0:c512,c768
tcontext=u:object_r:audio_device:s0
tclass=chr_file
permissive=1
先確認需要訪問的節點是否為audio_device
,這個節點屬於敏感權限,可以的話請修改訪問的目錄和文件,縮小audio_device的范圍
方法為:
1、確定訪問的節點位置,通過源碼或者log確定到底訪問的哪一個具體的節點, 例如 /dev/pcmc00xx
2、在相應的te文件中新聲明一個節點名稱, 如 file.te: type test_audio_device, dev_type;
3、在file_context中將具體節點綁定新的節點名稱, 如: file_context: /dev/pcmc00xx test_audio_device
4、增加或修改需要的權限: allow untrusted_app test_audio_device:chr_file { open write read };
如果不過GMS認證,敏感權限(c512,c768)可以直接把對象關聯mlstrustedobject, 但不推薦這樣修改,會造成嚴重的安全問題。 例如: typeattribute audio_device mlstrustedobject;
附錄B:DAC和MAC有什么區別?
DAC
DAC(Discretionary Access Control,自主訪問控制)
- DAC是傳統的Linux的訪問控制方式,DAC可以對文件、文件夾、共享資源等進行訪問控制。
- 在DAC這種模型中,文件客體的所有者(或者管理員)負責管理訪問控制。
- DAC使用了ACL(Access Control List,訪問控制列表)來給非管理者用戶提供不同的權限,而root用戶對文件系統有完全自由的控制權。
MAC
MAC(Mandatory Access Control,強制訪問控制)
- SELinux在內核中使用MAC檢查操作是否允許。
- 在MAC這種模型中,系統管理員管理負責訪問控制,用戶不能直接改變強制訪問控制屬性。
- MAC可以定義所有的進程(稱為主體)對系統的其他部分(文件、設備、socket、端口和其它進程等,稱為客體)進行操作的權限或許可。
DAC和MAC的其它區別
① DAC的主體是真實有效的用戶和組ID,MAC的主體是安全上下文,兩者的UID是各自獨立的。
② DAC的訪問控制模式是rwxrwxrwx,MAC的訪問控制模式是user:role:type。
附錄C:高通平台修改selinux權限路徑
Android 7 :device/qcom/sepolicy/common/
Android 9:其中的某一個
device/qcom/sepolicy/vendor/common/
device/sprd/sharkl3/common/sepolicy/
Android 10:其中的某一個
device/qcom/sepolicy/vendor/common/
device/qcom/sepolicy/legacy/vendor/common/
device/sprd/sharkl3/common/sepolicy/