Android的Sepolicy實際上是對SELinux安全策略的描述和設置。
1.什么是SELinux
安全增強型 Linux(Security-Enhanced Linux)簡稱 SELinux,它是 Linux 的一個安全子系統。SELinux 主要作用是最大限度地減小系統中服務進程可訪問的資源(最小權限原則)。對資源的訪問控制分為兩類: DAC和MAC.
DAC
在未使用SELinux的系統上, 對資源的訪問是通過權限位來確定, 比如一個文件對所屬用戶是否有讀、寫、執行權限, 其他用戶的訪問可由所屬用戶進行配置. 這種由所屬用戶自己決定是否將資源的訪問權或部分訪問權授予其他用戶,這種控制方式是自主的,即自主訪問控制(Discretionary Access Control, DAC).
ls -l可查看權限
> ls -l note -rw-rw-r-- 1 ifantsai ifantsai 37 6月 17 13:36 note
MAC
在使用了 SELinux 的系統上,對資源的訪問除了通過權限位判定外,還需要判斷每一類進程是否擁有對某一類資源的訪問權限。這種方式對資源的訪問控制, 稱之為強制訪問控制(Mandatory Access Control, MAC).只給每個進程開放所需要的資源, 將權限開放到最小, 當進程出現漏洞時也只會影響到該進程所涉及的資源, 這大大提升了安全性.
SELinux工作模式
SELinux 有三種工作模式,分別為:
- enforcing: 強制模式, 執行SELinux規則, 違反的行為會被阻止
- permissive: 寬容模式, 執行SELinux規則, 違反的行不會被阻止
- disabled: 關閉SELinux
通過執行getenforce
命令可以獲取當前SELinux的工作模式。
SELinux與Android
SELinux是Google從android 5.0開始,強制引入的一套非常嚴格的權限管理機制,主要用於增強系統的安全性。然而,在Android系統開發中,我們經常會遇到由於SELinux造成的各種權限不足,即使擁有“萬能的root權限”,也不能獲取全部的權限。
為了澄清是否因為SELinux導致的問題,可以嘗試將SELinux工作模式臨時改為寬容模式看問題是否解決, 來判定是否是SELinux引起的問題.
# 修改工作模式為寬容模式
setenforce permissive
# 臨時禁用SELinux
setenforce 0
# 獲取當前sepolicy配置
getenforce
遇到權限問題時, 在log中會打印avc denied提示缺少什么權限, 可以通過dmesg | grep avc
過濾出所有avc denied.
如果問題消失了,基本可以確認是SELinux造成的權限問題,需要通過正規的方式來解決權限問題。
遇到權限問題,在logcat或者kernel的log中一定會打印avc denied提示缺少什么權限,可以通過命令過濾出所有的avc denied,再根據這些log各個擊破:
cat /proc/kmsg | grep avc
或
dmesg | grep avc
例如:
audit(0.0:67): avc: denied { write } for path="/dev/block/vold/93:96" dev="tmpfs" ino=1263 scontext=u:r:kernel:s0 tcontext=u:object_r:block_device:s0 tclass=blk_file permissive=0
可以看到有avc denied,且最后有permissive=0,表示不允許。
2.解決avc denied
解決原則是:缺什么權限補什么,一步一步補到沒有avc denied為止。
解決權限問題需要修改的權限文件如下位置,以.te結尾
A:Android/devicesoftwinner/astar-common/sepolicy/*.te
B:Android/external/sepolicy/*.te
其中,A是對B的overlay(覆蓋),能在A修改的盡量在A修改,盡量避免修改B,修改B可能會導致CTS fail問題,修改A不會影響CTS測試。
下面給出四個案例:
案例1
audit(0.0:67): avc: denied { write } for path="/dev/block/vold/93:96" dev="tmpfs" ino=/1263 scontext=u:r:kernel:s0 tcontext=u:object_r:block_device:s0 tclass=blk_file permissive=0
分析過程:
缺少什么權限: {undefined write }權限,
誰缺少權限: scontext=u:r:kernel:s0
對哪個文件缺少權限:tcontext=u:object_r:block_device
什么類型的文件: tclass=blk_file
完整的意思: kernel進程對block_device類型的blk_file缺少write權限。
解決方法:在上文A位置,找到kernel.te這個文件,加入以下內容:
allow kernel block_device:blk_file write;
make installclean后重新編譯,刷boot.img才會生效
萬能公式
通過案例,我們可以總結出一般規律,以下面為例
audit(1441759284.810:5): avc: denied { read } for pid=1494 comm="sdcard" name="0" dev="nandk" ino=245281 scontext=u:r:sdcardd:s0 tcontext=u:object_r:system_data_file:s0 tclass=dir permissive=0
某個scontext對某個tclass類型的tcontext缺乏某個權限,我們需要允許這個權限:
我們的log重新排列一下,
scontext = u:r:sdcardd
tcontex t= u:object_r:system_data_file:s0
tclass = dir
avc: denied { read }
在scontext所指的.te文件(例如sdcardd.te)中加入類似如下allow內容:
allow <scontext> <tcontext>:<tclass> {<avc: denied>}
案例2
在RK Android9.0上進行操作, 權限文件以.te
為后綴, 涉及到需要修改的路徑:
android/device/rockchip/common/sepolicy
android/device/rockchip/rk3399/sepolicy
Android自帶的進程服務通過以上目錄配置即可 , 自己添加的第三方進程需要添加到自定義的目錄下
以如下所示的avc denied為例講解
# avc: denied { 操作權限 } for pid=7201 comm=“進程名” scontext=u:r:源類型:s0 tcontext=u:r:目標類型:s0 tclass=訪問類別 permissive=0 avc: denied { read } for pid=7517 comm="audio@2.0-servi" name="u:object_r:default_prop:s0" dev="tmpfs" ino=11426 scontext=u:r:hal_audio_default:s0 tcontext=u:object_r:default_prop:s0 tclass=file permissive=0
主要關注以下內容:
denied {read}
: 表示缺少read權限scontext=u:r:hal_audio_default:s0
: 表示hal_audio_default缺少了權限tcontext=u:object_r:default_prop:s0
: 表示是對default_prop缺少了權限tclass=file
: 表示缺少權限的資源類型為file
因此只要在hal_audio_default.te文件中加入下面內容即可
allow hal_audio_default tcontext:file read;
如果需要賦予read, open權限, 當有多個權限時用{}
包裹
allow hal_audio_default tcontext:file { read open };
或者參考android/system/sepolicy/public/global_macros
中賦予一個復合權限, 如r_file_perms
表示{ getattr open read ioctl lock map }
3.常用命令
$ adb shell setenforce 0 #把Selinux設為permissive模式,該模式下selinux被臨時禁用,對應的文件不會有selinux的權限限制 #但同時,avc denied的log依舊會打印出來,此時雖然avc denied的log還有,但確實時permissive狀態,不會被Selinux限制讀寫等權限。 #可以觀察avc denied的log最后permissive的值來看狀態,permissive=1時代表當前處在兼容模式 $ adb shell getenforce #查看selinux的狀態 #enforcing代表selinux enable #permissive代表selinux 臨時 disable
4.SELinux avc權限規則快速生成配置
step1: 打印avc denied的SELinux相關Log
$ adb logcat | grep avc > avc.log
step2: 在源代碼中找到audit2allow,並用audit2allow生成selinux avc權限配置信息
$ find . -name "audit2allow" $ ./audit2allow -i avc.log > selinux_config_info.log #selinux_config_info.log中會是類似如下的信息(以system_server為例) allow system_server sysfs:file { read write };
step3: 在生成的selinux avc權限配置信息的基礎上加上 open 和 attr 權限,因為一般都需要open和attr的權限,如果不加可能會繼續報attr或者open權限的avc denied錯誤
allow system_server sysfs:file { read write open attr };
step4: 在源代碼的對應位置加上sepolicy的權限
一般在 Android_Source/device/<vendor>/<device>/sepolicy目錄下,file_context, system_server.te等文件中
tips:
決定image生成哪些binary/so,放在 device/<vendor>/<device>/device-common.mk等mk文件中,比如EGL的設置等等。
修改權限問題,除了device/<vendor>/<device>/sepolicy目錄下的te文件,還可以修改external/sepolicy目錄下的te文件,但是前者是device specify的,意味着可以覆蓋后者,一般我們改前者。並且改后者可能會導致CTS fail,因為把google默認的規則改了
5.Nerverallow問題與SELinux權限自定義
nerverallow問題概述
添加了selinux權限后,代碼進行編譯時,編譯失敗並報 neverallow 錯誤,例如添加`allow system_app sysfs:file {write};`權限后編譯報錯 原因是 Google 不允許應用進程寫 sysfs 類型的文件,這是Google規范,安全考慮,部分權限不允許給, 當然也可以修改domain.te來修改Google的規范,雖然可以解決問題,但是這是不被允許的,在送測的時候會導致GMS測試失敗。 例如domain.te擁有以下代碼: neverallow { appdomain -bluetooth -nfc }sysfs:dir_file_class_set write 那么則不允許給dir_file_class_set標簽賦予write權限。 那又必須要這個權限怎么辦?那就可以客制化SELiunx權限。
SELinux權限自定義
根據上面所說,Google規則需遵守,部分權限不允許通過,所以需要自定義權限規則.
type定義
type分為了property.te、file.te、system.te,有很多類型,不止這三種,使用哪個文件取決於avc權限中的tclass屬性,目前只用file.te舉例,打開file.te添加如下type
/system/sepolicy/private/file.te # {parameter1,parameter2,parameter3} # type固定格式,custom_battery_file自定義的名稱,file_type定義為文件類型 # 逗號分割,后面可以繼續跟類型 例如:data_file_type type custom_battery_file, file_type;
配置安全上下文
安全上下文分為了 genfs_contexts、file_contexts、property_contexts ,當然不止這幾種,例舉了一些常用的
打開 /system/sepolicy/private/file_contexts 文件,打開哪個文件取決於avc權限中的tclass屬性
(三個xxx_contexts,需要自行判斷缺少的權限的客體資源是目錄還是文件 或是 屬性值):
/system/sepolicy/private/file_contexts
# 添加如下代碼
# 第一個參數寫文件節點(讀取這個文件節點沒權限,就添加這個節點)
# 第二個參數固定寫法u:object_r:custom_battery_file(這里寫file.te里定義的類型):s0(這些參數不做詳細解釋)
/vendor/custom/product/battery u:object_r:custom_battery_file:s0
/xxx/xxx (目標文件路徑或文件) u:object_r:file.te里面自定義的名稱:s0
配置原進程訪問權限
根據avc log中的scontext(主體進程)的值來決定在哪個文件下添加訪問權限
例如 scontext=u:r:system_server:s0,那則打開system_server.te即可
/system/sepolicy/private/system_server.te # 1.allow固定格式 # 2.system_server固定格式取決於scontext的值 # 3.custom_battery_file(file.te里自定義的類型) # 4.冒號后面的file,取決於avc log中的tclass類型是什么(例如tclass=file) # 5.{要給的權限類型} allow system_server custom_battery_file:file {read open};
這樣就客制化好了一個節點的寫權限
6.Sepolicy的file_contexts與*.te文件解析
前文提到“對哪個文件缺少權限:tcontext=u:object_r:block_device”,其中的block_device是一個標簽,可以有很多文件/設備都屬於這個標簽
那么file_contexts的內容就是來描述哪些設備/文件屬於哪個標簽的,一般存在Android源碼中device/<vendor-name>/<board-name>/sepolicy/file_contexts中
以google cuttlefish中的文件為例:google/cuttlefish/shared/virgl/sepolicy/file_contexts
/vendor/lib(64)?/libglapi.so u:object_r:same_process_hal_file:s0 /vendor/lib(64)?/dri/.* u:object_r:same_process_hal_file:s0 #這里意思是/vendor/lib和/vendor/lib64/里的libglapi.so和dri目錄下的所有文件/設備都屬於same_process_hal_file這個標簽下
而app.te中有下列內容,即te文件描述allow等規則時是以標簽為粒度的
... allow appdomain same_process_hal_file:file { read execute_no_trans }; ... #意思是所有應用都有對屬於same_process_hal_file標簽的文件/設備的讀和執行權限
這樣就串起來了,所有應用都有libglapi.so和dri下文件的讀、執行權限。
總結起來,file_contexts負責描述標簽,*.te文件負責描述規則
政策格式規則如下:
政策規則采用以下形式: allow domains types:classes permissions; 其中: Domain - 一個進程或一組進程的標簽。也稱為域類型,因為它只是指進程的類型。 Type - 一個對象(例如,文件、套接字)或一組對象的標簽。 Class - 要訪問的對象(例如,文件、套接字)的類型。 Permission - 要執行的操作(例如,讀取、寫入)。
使用政策規則時結構示例:
allow appdomain app_data_file:file rw_file_perms; #這表示所有應用域都可以讀取和寫入帶有 app_data_file 標簽的文件
參考鏈接:
SELinux之解決avc denied: https://www.caiyifan.cn/p/38175be7.html
Android SELinux avc dennied權限問題解決方法: https://blog.csdn.net/tung214/article/details/72734086
Android修改Selinux avc權限的方法: https://blog.csdn.net/jppipai/article/details/129093013
android selinux打開 安卓selinux開啟: https://blog.51cto.com/u_14191/6601762
Android進階-SELinux https://blog.csdn.net/qq_33750826/article/details/80831431
Selinux Android 官方鏈接:https://source.android.com/docs/security/features/selinux?hl=zh-cn