1. DAC和MAC
在了解SELinux之前艇肴,我們先來了解一下Linux的兩種訪問控制策略:DAC和MAC
1.1 Linux DAC
DAC挤渔,自主訪問控制(Discretionary Access control)羊娃。系統(tǒng)只提供基本的驗證, 完整的訪問控制由開發(fā)者自己控制甩卓。
?DAC將資源訪問者分成三類:Owner杉适、Group肛真、Other 层宫。
?將訪問權(quán)限也分成三類:read杨伙、write、execute
資源針對資源訪問者設置不同的訪問權(quán)限萌腿。訪問者通常是各個用戶的進程限匣,有自己的uid/gid,通過uid/gid 和文件權(quán)限匹配, 來確定是否可以訪問毁菱。DAC機制下米死,每一個用戶進程默認都擁有該用戶的所有權(quán)限。
DAC 有兩個嚴重問題:
?問題一:
?因為Root用戶是擁有所有權(quán)限的贮庞,所以DAC對Root用戶的限制是無效的峦筒。并且在Linux Kernel 2.1以后,Linux將Root權(quán)限根據(jù)不同的應用場景劃分成許多的Root Capabilities, 普通用戶也可以被設置某個Root Capability窗慎。普通用戶如果被設置了CAP_DAC_OVERRIDE物喷, 也可以繞過 DAC 限制。
?問題二:
?用戶進程擁有該用戶的所有權(quán)限遮斥,可以修改/刪除該用戶的所有文件資源, 難以防止惡意軟件峦失。
可見,DAC 有明顯的缺陷术吗,一旦被入侵尉辑,取得Root權(quán)限的用戶進程就可以無法無天,胡作非為较屿,早期android版本就深受其害隧魄。
1.2 Linux MAC
MAC卓练, 強制性訪問控制(Mandatory Access control)。 系統(tǒng)針對每一項訪問都進行嚴格的限制, 具體的限制策略由開發(fā)者給出购啄。
Linux MAC 針對DAC 的不足, 要求系統(tǒng)對每一項訪問, 每訪問一個文件資源都需要根據(jù)已經(jīng)定義好了的策略進行針對性的驗證襟企。系統(tǒng)可以針對特定的進程與特定的文件資源來進行權(quán)限的控制。即使是root用戶闸溃,它所屬的不同的進程整吆,并不一定能取得root權(quán)限,而得要看事先為該進程定義的訪問限制策略辉川。如果不能通過MAC 驗證表蝙,一樣無法執(zhí)行相關的操作。
與DAC相比乓旗,MAC訪問控制的“主體”變成了“進程”而不是用戶府蛇。這樣可以限制了Root 權(quán)限的濫用,另外要求對每一項權(quán)限進行了更加完整的細化, 可以限制用戶對資源的訪問行為屿愚。
SELinux就是目前最好的MAC機制汇跨,也是目前的行業(yè)標準。
2. SELinux概述
2.1 SELinux簡介
SELinux妆距,安全增強Linux(Security-Enhanced Linux)穷遂,是由美國國家安全局(NSA)發(fā)起, 多個非營利組織和高校參與開發(fā)的強制性安全審查機制(Mandatory Access control,簡稱MAC)娱据。SELinux最早于2000年12月采用GPL許可發(fā)布蚪黑。目前,Linux Kernel 2.6 及以上的版本都已經(jīng)集成了SELinux中剩。
2.2 SELinux模式
SELinux 分成三種模式:
- Disabled(關閉)忌穿,Selinux并沒有實際運行。
- Permissve(寬容模式)结啼,寬容模式只通過Kernel Audit System 記錄LOG, 但不攔截訪問掠剑。
- Enfocing mode(強制模式),強制模式在記錄LOG 的同時郊愧,還會攔截訪問朴译。
Android 5.x及以上強制開啟,因此属铁,disabled(關閉)模式并沒有什么用了眠寿。 通常在調(diào)試時,我們會啟用Permissve(寬容模式), 以便盡可能的發(fā)現(xiàn)多的問題, 然后一次修正红选。 在量產(chǎn)時啟用Enfocing mode(強制模式)來保護系統(tǒng)澜公。
查看SELinux模式:adb shell getenforce
設置SELinux模式:adb shell setenforce 1 //0是Permissve姆另,1是Enfocing
2.3 SELinux 在Android上的主要變更歷史
- Android 4.3 引入SELinux喇肋,但采用寬容模式(Permissive)
- Android 4.4 部分強制模式(Enforcing)坟乾, 針對netd, installd, zygote, vold 四個原本具有root 權(quán)限的process, 以及它們fork 出的子進程啟用強制模式(Enforcing)。
- Android 5.x 及更高版本蝶防,所有域均處于強制模式(Enforcing)甚侣。
- Android 8.0 Google 為了實現(xiàn)vendor.img、system.img可獨立升級间学,不相互影響殷费,將Vendor和System分離,大幅度的增強了SElinux 的限制, 限制System/Vendor 之間的交叉使用低葫。
3. SELinux 基礎
3.1 SELinux 基本概念
SELinux 的訪問控制示意圖:
- Subject:訪問主體详羡,通常是指能夠產(chǎn)生訪問行為的對象, Linux 中通常是一個process嘿悬。
- Object :訪問對象, Linux 中通常是文件实柠,但process 也可能是一個訪問對象, 比如另外一個進程對它發(fā)送Signal, 比如去對它進行Ptrace 操作。
- Object Manager 對象管理器, 即可以知道Subject 需要訪問哪些資源善涨,并且觸發(fā)驗證機制
- Security Server 即安全服務器, 用來驗證某個Subject 是否可以真正的訪問某個Object, 而這個驗證機制是基于定義好的Security Policy.
- Security Policy 是一種描述SELinux Policy 的語言.
- Access Vector Cache (AVC) 是訪問緩存, 用來記錄以往的訪問驗證情況, 以便提供效率窒盐,快速處理.
通常我們開發(fā)的過程中,就是配置Subject钢拧、Object蟹漓、Security Policy。
3.2 Security Context
SELinux 給Linux 的所有對象都分配一個安全上下文(Security Context)源内, 描述成一個標準的字符串葡粒。
3.2.1 安全上下文基本概念
安全上下文的標準格式: user:role:type[:range]
- User: 用戶, 非Linux UID,在SEAndroid中姿锭,只定義了一個SElinux用戶那就是:u
- Role: 角色塔鳍,一個user可以屬于多個role,不同的role具有不同的權(quán)限呻此。在SEAndroid中轮纫,定義了兩個role: r 和 object_r。文件或?qū)傩缘慕巧ǔJ牵簅bject_r焚鲜,而進程的角色通常是:r掌唾。
- Type: Subject或者Object的類型。每一個對象都有一個類型(Class)忿磅,每個類型(Class)都會根據(jù)實際情況定義權(quán)限類型項, 如: read, write, exec, ioctl, append 等等糯彬。對于作為Object的文件, 它是File類型(Class)。 而對于作為Subject的進程來說葱她,它的類型就是Domain撩扒。
- Range: Multi-Level Security(MLS)的級別。MLS將系統(tǒng)的進程和文件進行了分級吨些,MLS的low_level 是s0, high level 是s0:c0.c1023搓谆。
3.2.2 Security Label
Security Label 用來綁定被訪問資源和安全上下文炒辉,描述它們的對應關系。標準格式為:resource security_context泉手。即:res user:role:type[:range]黔寇。這里也可以使用通配符,例如 net.就可以綁定所有以net.開頭的屬性斩萌,除此之外缝裤,還有類似正則表達式的*、颊郎?等等通配符憋飞。Security Label 都定義在type_contexts當中,例如file的定義在file_contexts中姆吭,service定義在service_contexts中搀崭,property定義在property_contexts中。
舉例:
file_contexts:
###########################################
# Root
/ u:object_r:rootfs:s0
# Data files
/adb_keys u:object_r:adb_keys_file:s0
/build\.prop u:object_r:rootfs:s0
/default\.prop u:object_r:rootfs:s0
/fstab\..* u:object_r:rootfs:s0
/init\..* u:object_r:rootfs:s0
/res(/.*)? u:object_r:rootfs:s0
/selinux_version u:object_r:rootfs:s0
/ueventd\..* u:object_r:rootfs:s0
/verity_key u:object_r:rootfs:s0
service_contexts:
accessibility u:object_r:accessibility_service:s0
account u:object_r:account_service:s0
activity u:object_r:activity_service:s0
alarm u:object_r:alarm_service:s0
android.os.UpdateEngineService u:object_r:update_engine_service:s0
android.security.keystore u:object_r:keystore_service:s0
android.service.gatekeeper.IGateKeeperService u:object_r:gatekeeper_service:s0
appops u:object_r:appops_service:s0
appwidget u:object_r:appwidget_service:s0
3.2.3 查看安全上下文
查看進程安全上下文:ps -AZ猾编。例如瘤睹,查看Settings進程的安全上下文,ps -AZ | grep settings:
??u:r:system_app:s0 system 1381 585 4234504 201072 0 0 S com.android.settings
查看文件安全上下文:ls -Z答倡。例如轰传,查看文件build.prop的安全上下文:
??u:object_r:system_file:s0 build.prop
3.3 Type Enforcement Access Control
Type Enforcement (TE) 是根據(jù)Security Context中的 type 進行權(quán)限審查, 審查 subject type 對 object type 的某個class 類型中某種permission 是否具有訪問權(quán)限,是目前使用最為廣泛的MAC 審查機制, 簡單易用瘪撇。
3.3.1 Type Enforcement基本概念
TE控制語句格式 : rule_name source_type target_type : class perm_set
- rule: 控制類型获茬, 分為:allow 以及 audit
- source_type:也叫subject,通常是domain倔既,可以是一組subject恕曲,用{}包含起來。
- target_type: 代表請求的資源的類型渤涌,可以是一組target佩谣,用{}包含起來。
- class perm_set: 代表對資源訪問的操作实蓬,perm_set可以是一組茸俭,用{}包含起來。
3.3.2 Type Enforcement規(guī)則
Type Enforcement規(guī)則說明:
- allow:賦予某項權(quán)限安皱。
- auditallow:audit含義就是記錄某項操作调鬓。默認SELinux只記錄那些權(quán)限檢查失敗的操作。 auditallow則使得權(quán)限檢查成功的操作也被記錄酌伊。注意腾窝,allowaudit只是允許記錄,它和賦予權(quán)限沒關系。賦予權(quán)限必須且只能使用allow語句虹脯。
- dontaudit:對那些權(quán)限檢查失敗的操作不做記錄辜贵。
- neverallow:用來檢查安全策略文件中是否有違反該項規(guī)則的allow語句
舉個例子,logd.te归形、tombstoned.te中定義的TE規(guī)則:
??allow logd runtime_event_log_tags_file:file rw_file_perms;
??dontaudit domain runtime_event_log_tags_file:file { open read };
??auditallow tombstoned anr_data_file:file { append write };
??neverallow logd { app_data_file system_data_file }:dir_file_class_set write;
3.3.3 Attribute and Type
SELinux 中每一個進程或者文件都對應一個type, 而每一個type 都對應有一個或幾個attribute。所有常見的attribute定義在以下文件中:
??system/sepolicy/public/attributes
??system/sepolicy/prebuilts/api/[build version]/public/attributes
??system/sepolicy/prebuilts/api/[build version]/private/attributes
其中的[build version]即為android版本號鼻由,例如android O為28.0暇榴。常見的attribute定義:
######################################
# Attribute declarations
#
# All types used for devices.
# On change, update CHECK_FC_ASSERT_ATTRS
# in tools/checkfc.c
attribute dev_type;
# All types used for processes.
attribute domain;
# All types used for filesystems.
# On change, update CHECK_FC_ASSERT_ATTRS
# definition in tools/checkfc.c.
attribute fs_type;
# All types used for context= mounts.
attribute contextmount_type;
# All types used for files that can exist on a labeled fs.
# Do not use for pseudo file types.
# On change, update CHECK_FC_ASSERT_ATTRS
# definition in tools/checkfc.c.
attribute file_type;
# All types used for domain entry points.
attribute exec_type;
# All types used for /data files.
attribute data_file_type;
expandattribute data_file_type false;
# All types in /data, not in /data/vendor
attribute core_data_file_type;
expandattribute core_data_file_type false;
# All types in /vendor
attribute vendor_file_type;
# All types used for procfs files.
attribute proc_type;
expandattribute proc_type false;
# All types used for sysfs files.
attribute sysfs_type;
# All types use for debugfs files.
attribute debugfs_type;
Type對應一個或者幾個attribute,Type的定義格式:
??type type_name, attribute1, attribute2蕉世;
Type的定義通常分散在各個te文件中蔼紧。例如,常用普通文件的type定義在file.te中:
# Default type for anything under /system.
type system_file, file_type;
# Default type for anything under /data.
type system_data_file, file_type, data_file_type, core_data_file_type;
# /data/data subdirectory for system UID apps.
type system_app_data_file, file_type, data_file_type, core_data_file_type, mlstrustedobject;
# Default type for anything under /data/vendor{_ce,_de}.
type vendor_data_file, file_type, data_file_type;
3.3.4 Classes and Permissions
SEAndroid對于不同的資源類型狠轻,定義了不同的Class奸例。比如普通的file、socket等等向楼,比如SELinux 使用的security, 比如針對每個process 參數(shù)的process 等定義相關的class查吊。這些class,每一個class 都有相對應的permissions湖蜕。 比如file 就有 read, write, create, getattr, setattr, lock, ioctl 等等. 比如process 就有fork, sigchld, sigkill, ptrace, getpgid, setpgid 等等逻卖。這些相關的class, 以及他們具有那些Permissions都定義在以下文件中:
??system/sepolicy/private/access_vectors
??system/sepolicy/reqd_mask/access_vectors
??system/sepolicy/prebuilts/api/版本號/private/access_vectors
例如:
#
# Define a common prefix for file access vectors.
#
common file
{
ioctl
read
write
create
getattr
setattr
lock
relabelfrom
relabelto
append
map
unlink
link
rename
execute
quotaon
mounton
}
#
# Define a common prefix for socket access vectors.
#
common socket
{
# inherited from file
ioctl
read
write
create
getattr
setattr
lock
relabelfrom
relabelto
append
map
# socket-specific
bind
connect
listen
accept
getopt
setopt
shutdown
recvfrom
sendto
name_bind
}
定義完之后,在以下對應的security_classes 文件中聲明定義的classes昭抒。
??system/sepolicy/private/security_classes
??system/sepolicy/reqd_mask/security_classes
??system/sepolicy/prebuilts/api/版本號/private/security_classes
例如:
#
# Define the security object classes
#
# Classes marked as userspace are classes
# for userspace object managers
class security
class process
class system
class capability
# file-related classes
class filesystem
class file
class dir
class fd
class lnk_file
class chr_file
class blk_file
class sock_file
class fifo_file
注意评也,Classes 和Permissions的定義與Kernel 中相關API是強相關的,普通用戶嚴禁修改灭返。
3.4 Domain and Object Transitions
3.4.1 Domain Transitions
在SELinux 中, 我們通常稱一個進程是一個domain, 一個進程fork 另外一個進程并執(zhí)行(exec) 一個執(zhí)行檔時, 我們往往會涉及到domain 的切換. 比如init 進程, SELinux 給予了它很大的權(quán)限, 而它拉起的服務, 我們要限制這個服務的權(quán)限盗迟,于是就涉及到從一個domain 切換到另外一個domain, 不然默認就使用init 進程的domain.
在SELinux 里面有專門的一條語法: type_transition statement.
在準備切換前我們先要確保有相關的權(quán)限操作:
- 源domain 必須有權(quán)限切換到這個目標domain。(The source domain has permission to transition into the target domain.)
- 源domain 必須要有這個執(zhí)行檔的執(zhí)行權(quán)限熙含。(The application binary file needs to be executable in the source domain.)
- 這個執(zhí)行檔必須是目標domain 的入口罚缕。(The application binary file needs an entry point into the target domain.)
如下面的demo, init 拉起apache 并且切換到 apache 的domain.
(1). 首先,你得讓init_t域中的進程能夠執(zhí)行type為apache_exec_t的文件
??allow init_t apache_exec_t : file {read getattr execute};
(2). 然后怎静,你還得告訴SELinux怕磨,允許init_t做DT切換以進入apache_t域
??allow init_t apache_t : process transition;
(3). 然后,你還得告訴SELinux消约,切換入口(對應為entrypoint權(quán)限)為執(zhí)行apache_exec_t類型 的文件
??allow apache_t apache_exec_t : file entrypoint;
(4).最后肠鲫,Domain Transition
??type_transition init_t apache_exec_t : process apache_t;
可以看到,整個domain切換過程寫起來非常麻煩或粮。因此导饲,Google 為了使用方便, 在system/sepolicy/public/te_macros 文件中定義了宏:
#####################################
# domain_trans(olddomain, type, newdomain)
# Allow a transition from olddomain to newdomain
# upon executing a file labeled with type.
# This only allows the transition; it does not
# cause it to occur automatically - use domain_auto_trans
# if that is what you want.
#
define(`domain_trans', `
# Old domain may exec the file and transition to the new domain.
allow $1 $2:file { getattr open read execute map };
allow $1 $3:process transition;
# New domain is entered by executing the file.
allow $3 $2:file { entrypoint open read execute getattr map };
# New domain can send SIGCHLD to its caller.
ifelse($1, `init', `', `allow $3 $1:process sigchld;')
# Enable AT_SECURE, i.e. libc secure mode.
dontaudit $1 $3:process noatsecure;
# XXX dontaudit candidate but requires further study.
allow $1 $3:process { siginh rlimitinh };
')
#####################################
# domain_auto_trans(olddomain, type, newdomain)
# Automatically transition from olddomain to newdomain
# upon executing a file labeled with type.
#
define(`domain_auto_trans', `
# Allow the necessary permissions.
domain_trans($1,$2,$3)
# Make the transition occur by default.
type_transition $1 $2:process $3;
')
#####################################
# init_daemon_domain(domain)
# Set up a transition from init to the daemon domain
# upon executing its binary.
define(`init_daemon_domain', `
domain_auto_trans(init, $1_exec, $1)
')
我們可以使用這些宏來完成domain切換。
舉例:
kernel啟動init進程切換domain:
??domain_auto_trans(kernel, init_exec, init)
init啟動netd、vold渣锦、zygote硝岗、installd切換domain:
??init_daemon_domain(netd)
??init_daemon_domain(vold)
??init_daemon_domain(zygote)
??init_daemon_domain(installd)
3.4.2 Target Transitions
一個進程創(chuàng)建在一個目錄下創(chuàng)建文件時, 默認是沿用父目錄的Security Context, 如果要設置成特定的Label, 就必須進行Object Transitions.
同樣是使用:type_transition statement.
對應的必須有兩個前提條件:
- 源domain 必須有在這個目錄下添加文件的權(quán)限。(The source domain needs permission to add file entries into the directory)
- 源domain 必須有在這個目錄下創(chuàng)建以這個Security Context 為Label 的文件權(quán)限袋毙。(The source domain needs permission to create file entries)
下面是一個demo, ext_gateway_t 這個domain 在類型為in_queue_t 的目錄下型檀,創(chuàng)建類型為 in_file_t 的文件.
(1). 首先,你得讓ext_gateway_t 對in_queue_t 目錄具備訪問權(quán)限
??allow ext_gateway_t in_queue_t : dir { write search add_name };
(2). 然后听盖,你還得告訴SELinux胀溺,允許ext_gateway_t 訪問in_file_t的文件
??allow ext_gateway_t in_file_t : file { write create getattr };
(3).最后,Object Transition
??type_transition ext_gateway_t in_queue_t : file in_file_t;
同樣的皆看,為了方便使用仓坞,Google 也在system/sepolicy/public/te_macros 文件中定義了宏:
#####################################
# file_type_trans(domain, dir_type, file_type)
# Allow domain to create a file labeled file_type in a
# directory labeled dir_type.
# This only allows the transition; it does not
# cause it to occur automatically - use file_type_auto_trans
# if that is what you want.
#
define(`file_type_trans', `
# Allow the domain to add entries to the directory.
allow $1 $2:dir ra_dir_perms;
# Allow the domain to create the file.
allow $1 $3:notdevfile_class_set create_file_perms;
allow $1 $3:dir create_dir_perms;
')
#####################################
# file_type_auto_trans(domain, dir_type, file_type)
# Automatically label new files with file_type when
# they are created by domain in directories labeled dir_type.
#
define(`file_type_auto_trans', `
# Allow the necessary permissions.
file_type_trans($1, $2, $3)
# Make the transition occur by default.
type_transition $1 $2:dir $3;
type_transition $1 $2:notdevfile_class_set $3;
')
使用舉例:
??file_type_auto_trans(factory, system_data_file, factory_data_file)
4. SELinux實戰(zhàn)
4.1 sepolicy存放目錄
android O 以前sepolicy 集中放在boot image 。前面提到SELinux在Android的主要變更歷史時腰吟,有提到android O 開始无埃,Google將system image 和 vendor image 分離。因此毛雇,sepolicy 也相應的被分離存放到system image 以及 vendor image嫉称。與system 相關的sepolicy 就存放system image, 與SoC vendor 相關的sepolicy 就存放在vendor image。
4.1.1 AOSP sepolicy存放目錄
對于原生AOSP灵疮,Google 設定了不同的存放目錄, 以便進行分離, 以Google 默認的sepolicy 為例澎埠,sepolicy主目錄為 /system/sepolicy,我們主要關注三個子目錄:
public:android 和 vendor 共享的sepolicy 定義, 通常情況下, 意味著vendor 開發(fā)者可能為此新增一些權(quán)限. 一般system/vendor 共用的一些類型和屬性的定義, neverallow 限制等會存放于此.
private:通常意義上的僅限于system image 內(nèi)部的使用, 不對vendor 開放. 這個只會編譯到system image 中.
vendor:它僅僅能引用public 目錄下的相關定義, 這個只會編譯到vendor image 中. 但它依舊可以對system image 里面的module 設定sepolicy(對應module 需要在public 下進行聲明); 在很大程度上繞過了Google CTS 測試.
4.1.2 MTK平臺
對于不同的平臺始藕,不同平臺廠商也設定了不同的存放目錄蒲稳,以MTK平臺為例:
首先,根據(jù)不同的platform共用sepolicy伍派、platform獨有江耀、project獨有,分為:
- /device/mediatek/sepolicy :下面是ALL platform 都需要.
- /device/mediatek/[platform]/sepolicy :其中platform為某個具體的平臺诉植,如mt6763.
- /device/[customer]/[project]/sepolicy :客戶和項目自定義配置, 客戶可根據(jù)更新BroadConfig.mk 中BOARD_SEPOLICY_DIRS 以新增目錄祥国。
對應的,不同版本會導入不同目錄下的sepolicy配置
- Basic 版本 導入 [common]|[platfrom]/basic
- Bsp 版本 導入 [common]|[platfrom]/basic & bsp
- TK 版本(一般情況) 導入 [common]|[platfrom]/basic & bsp & full
以mt6763平臺為例晾腔,導入時:
[common] 路徑為:/device/mediatek/sepolicy
[platfrom] 路徑為:/device/mediatek/mt6763/sepolicy/
具體的定義在BoardConfig.mk文件中:
#SELinux Policy File Configuration
ifeq ($(strip $(MTK_BASIC_PACKAGE)), yes)
BOARD_SEPOLICY_DIRS += \
device/mediatek/mt6763/sepolicy/basic
endif
ifeq ($(strip $(MTK_BSP_PACKAGE)), yes)
BOARD_SEPOLICY_DIRS += \
device/mediatek/mt6763/sepolicy/basic \
device/mediatek/mt6763/sepolicy/bsp
endif
ifneq ($(strip $(MTK_BASIC_PACKAGE)), yes)
ifneq ($(strip $(MTK_BSP_PACKAGE)), yes)
BOARD_SEPOLICY_DIRS += \
device/mediatek/mt6763/sepolicy/basic \
device/mediatek/mt6763/sepolicy/bsp \
device/mediatek/mt6763/sepolicy/full
endif
endif
然后舌稀,basic、bsp灼擂、full又可以主要細分為:
- non_plat:僅用于vendor 目錄的sepolicy 設定. 和Google vendor 目錄類似.
- plat_private:僅用于system 目錄的sepolicy 設定, 和Google 的private 目錄類似.
- plat_public:同時可用于vendor/system 的sepolicy 設定, 和Google 的public 目錄類似.
4.2 Sepolicy修改注意事項
Google 在system/sepolicy 中定義了相關的neverallow 規(guī)則, 對SELinux Policy 的更新進行了限制, 以防止開發(fā)者過度開放權(quán)限壁查,從而引發(fā)安全問題。并且還會通過CTS測試檢測開發(fā)者是否有違法相關的規(guī)則剔应。
因此睡腿,我們需要注意以下幾點:
- 不要直接修改system/sepolicy 下面的相關SELinux Policy, 使之保持與Google 一致, 特別是不要直接刪除或者修改Google 定義的neverallow 規(guī)則语御。
- 添加的sepolicy按需要提供,需要多大權(quán)限就給多大權(quán)限席怪,不可將權(quán)限過于放大应闯。
- 遇到與Google 定義相違背之處, 只能繞道, 或者修改設計。
出現(xiàn)SELinux Policy Exception時常見的兩種解決方案:
(1). 修改對應節(jié)點的SELinux Security Label, 為特定的Subject挂捻,如system_app碉纺、platform_app、priv_app刻撒,例如Settings骨田,SystemUI等內(nèi)置APP開啟權(quán)限, 但嚴禁為untrsted app 開啟權(quán)限。
(2). 通過system server service 或者 init 啟動的service 讀寫操作, 然后app 通過binder/socket 等方式連接訪問. 此類安全可靠, 并且可以在service 中做相關的安全審查, 推薦這種方法.
4.3 Sepolicy常用例子
4.3.1 添加由init進程啟動的服務
情景: 定義由 init 進程啟動的service, factory, 其對應的執(zhí)行檔是 /vendor/bin/factory疫赎。
(1). 在device/mediatek/mt6763/sepolicy/basic/non_plat 目錄下創(chuàng)建一個factory.te , 然后將te文件加入編譯,如放到這種指定目錄下不需要額外配置碎节,sytem/sepolicy/Android.mk中定義的build_policy函數(shù)會遍歷指定目錄導入te文件捧搞。
(2). 在factory.te 中定義factory類型,init 啟動service 時類型轉(zhuǎn)換,
??type factory, domain;
??type factory_exec, exec_type, file_type, vendor_file_type;
??init_daemon_domain(factory)
(3). 在file_contexts中綁定執(zhí)行檔
??/(system/vendor|vendor)/bin/factory u:object_r:factory_exec:s0
(4). 根據(jù)factory需要訪問的文件以及設備, 定義其它的權(quán)限在factory.te 中.
??#Purpose: For key and touch event
??allow factory input_device:chr_file r_file_perms;
??allow factory input_device:dir rw_dir_perms;
4.3.2 添加property
情景: 添加一個自定義的system property: persist.demo狮荔,并為platform_app設置讀寫權(quán)限
(1). 在property.te中定義system property類型
??type demo_prop, property_type
(2). 在property_contexts中綁定system property的安全上下文胎撇。
??persist.demo u:object_r:demo_prop:s0
(3). 在platform_app.te 中新增寫權(quán)限,可以使用set_prop宏殖氏。
??set_prop(platform_app, demo_prop)
(4). 在platform_app.te 中新增讀權(quán)限晚树,可以get_prop 宏。
??get_prop(platform_app, demo_prop)
4.3.3 訪問設備節(jié)點
情景: 有一個設備節(jié)點/dev/demo雅采,有一個platform_app進程需要讀寫這個設備節(jié)點爵憎。
(1). 在device.te中定義device 類型
??type demo_device dev_type;
(2). 在 file_contexts中綁定demo_device
??/dev/demo u:object_r:demo_device:s0
(3). 在platform_app.te中,允許platform_app使用demo device 的權(quán)限
??allow platform_app demo_device:chr_file rw_file_perms;
4.3.4 添加系統(tǒng)服務
情景: 有一個擴展的系統(tǒng)服務demo_service供APP調(diào)用婚瓜。
(1). 在service.te 中定義service 類型
??type demo_service, app_api_service, system_server_service, service_manager_type;
(2). 在service_contexts 中綁定service
??demo u:object_r:demo_service:s0
(3). 在frameworks/base/core/java/android/content/Context.java中定義服務常量
??public static final String DEMO_SERVICE = "demo";
(4). 在frameworks/base/core/java/android/app/SystemServiceRegistry.java中宝鼓,參照其它系統(tǒng)服務注冊demo_service
registerService(Context.DEMO_SERVICE, DemoManager.class,
new CachedServiceFetcher<DemoManager>() {
@Override
public DemoManager createService(ContextImpl ctx) throws ServiceNotFoundException {
IBinder binder = ServiceManager.getServiceOrThrow(Context.DEMO_SERVICE);
IDemoService service = IDemoService.Stub.asInterface(binder);
return new DemoManager(ctx.getOuterContext(), service);
}});
(5). 在frameworks/base/services/java/com/android/server/SystemServer.java中,啟動DemoService巴刻,添加到service_manager進行管理愚铡。
try {
mSystemServiceManager.startService(DemoService.class);
} catch (Throwable e) {
reportWtf("starting DemoService", e);
}
(6). 最后一步,參考其它系統(tǒng)服務胡陪,實現(xiàn)DemoManager沥寥、DemoService,并定義如IDemoService等等的AIDL接口柠座。
4.3.5 使用Local socket
情景: 一個native service 通過init 創(chuàng)建一個socket 并綁定在 /dev/socket/demo, 并且允許某些process 訪問.
(1). 在file.te中定義socket 的類型
??type demo_socket, file_type;
(2). 在file_contexts中綁定socket 的類型
??/dev/socket/demo_socket u:object_r:demo_socket:s0
(3). 允許所有的process 訪問邑雅,使用宏unix_socket_connect(clientdomain, socket, serverdomain)
??unix_socket_connect(appdomain, demo, demo)
4.3.6 init fork新進程
(1). 在device/mediatek/mt6763/sepolicy/basic/non_plat目錄下創(chuàng)建一個demo.te。
(2). 在demo.te 中定義demo 類型妈经,init 啟動service 時類型轉(zhuǎn)換蒂阱。并可以根據(jù)demo 需要訪問的文件以及設備, 定義其它的權(quán)限在demo.te 中锻全。
??type demo, domain;
??type demo_exec, exec_type, file_type;
??init_daemon_domain(demo)
(3). 綁定執(zhí)行檔 file_context 類型
??/vendor/bin/demo u:object_r:demo_exec:s0
(4). 創(chuàng)建demo的入口執(zhí)行檔demo_exec、并配置相應的權(quán)限录煤。
4.4 SELinux常見問題分析
4.4.1 如何判斷是否與SELinux 相關鳄厌?
(1). 將SELinux 調(diào)整到Permissive 模式復測
使用eng/userdebug 版本,adb shell setenforce 0 將SELinux 模式調(diào)整到Permissive 模式妈踊,然后復測了嚎。如果還能復現(xiàn)問題,則與SELinux 無關廊营; 如果原本很容易復現(xiàn), 而Permissive mode 不能再復現(xiàn), 那么就可能與SELinux相關歪泳。
(2). 查看LOG 中是否有標準的SELinux Policy Exception.
在Kernel LOG / Main Log 中查詢關鍵字 "avc: denied" 看看是否有與目標進程相關的SELinux Policy Exception, 并進一步確認這個異常是否與當時的邏輯相關。
4.4.2 如何根據(jù)SELinux exception log配置權(quán)限
一般情況我們在符合Google sepolicy策略及neverallow策略的前提下露筒,根據(jù)LOG中的內(nèi)容呐伞,需要什么權(quán)限就加什么權(quán)限。例如LOG:
2020-03-27 14:11:02.596 1228-1228/com.android.systemui W/FaceIdThread: type=1400 audit(0.0:481): avc: denied { read } for name="als_ps" dev="tmpfs" ino=10279 scontext=u:r:platform_app:s0:c512,c768 tcontext=u:object_r:als_ps_device:s0 tclass=chr_file permissive=0
LOG說明如下:
2020-03-27 14:11:02.596:[time]慎式。
1228-1228/com.android.systemui I/FaceIdThread:[uid]/[package] [log level]/[thread]伶氢。
type=1400:type=[1400|AVC|USER_AVC],1400表示SYSCALL瘪吏,AVC表示kernel events癣防,USER_AVC表示user-space object manager events。
audit(0.0:481): audit(time:serial_number)
avc: denied { read }: read是被拒絕的權(quán)限掌眠,即permission
for name="als_ps" dev="tmpfs" ino=10279 :訪問目標的名稱蕾盯、設備節(jié)點等相關信息。
scontext=u:r:platform_app:s0:c512:主體的安全上下文蓝丙,platform_app就是source type
tcontext=u:object_r:als_ps_device:s0:目標資源的安全上下文级遭,als_ps_device就是target type
tclass=chr_file:目標資源的class類型即target class
permissive=0:當前的selinux模式,1表示permissive渺尘,0表示enforcing
一般我們需要重點關注的是四個:permission装畅、source type、target type沧烈、target class
根據(jù)這四個就可以配置出的所需要的selinux權(quán)限:
??allow [source type] [target type]: [target class] [permission]
例1:
03-27 03:45:22.632 2958 2958 W Camera: type=1400 audit(0.0:314): avc: denied { read } for name="u:object_r:graphics_debug_prop:s0" dev="tmpfs" ino=2649 scontext=u:r:platform_app:s0:c512,c768 tcontext=u:object_r:graphics_debug_prop:s0 tclass=file permissive=0
解決方案:
按正常的套公式掠兄,應該是這樣修改platform_app.te,增加:
??allow platform_app graphics_debug_prop:file r_file_perms;
這里我們利用system/sepolicy/te_macros中定義的宏get_prop:
define(`get_prop', `
allow $1 $2:file r_file_perms;
')
更多相關的宏定義請參考:system/sepolicy/public/te_macros锌雀。
所以最終簡化后蚂夕,修改platform_app.te,增加:
??get_prop(platform_app, graphics_debug_prop)
例2:
03-27 14:11:02.596 1228-1228/com.android.systemui W/BackThread: type=1400 audit(0.0:481): avc: denied { read } for name="als_ps" dev="tmpfs" ino=10279 scontext=u:r:platform_app:s0:c512,c768 tcontext=u:object_r:als_ps_device:s0 tclass=chr_file permissive=0
解決方案:
修改platform_app.te增加:
??allow platform_app als_ps_device:chr_file r_file_perms;
4.3.2 修改或者增加一些SELinux Policy 后無法編譯通過或者CTS測試項failed
(1). 不符合neverallow規(guī)則或者修改了neverallow規(guī)則
編譯報錯:
??neverallow check failed at xxx
CTS測試項failed:
??android.cts.security.SELinuxNeverallowRulesTest#testNeverallowRulesXXX
這類問題在android O vendor和system分離之后腋逆,尤其容易出現(xiàn)婿牍。基本上這類問題都是因為修改或者增加的te配置不符合neverallow規(guī)則惩歉,導致編譯報錯等脂。而為了解決編譯報錯俏蛮,又修改了neverallow規(guī)則,最終在跑CTS時上遥,沒法通過相關的測試項搏屑。
解決思路:
- 盡量不修改neverallow規(guī)則;
- 不符合neverallow規(guī)則的te配置粉楚,要注意vendor sepolicy和system sepolicy分離辣恋,是否為跨分區(qū)使用。另外模软,如果是因為權(quán)限被過于放大伟骨,導致編譯報錯,可細化權(quán)限后再試燃异。
- 其它情況可參考4.2節(jié)中的兩種參考方案修改携狭。
(2). init進程fork新進程沒有做domain切換
CTS測試項failed:
??android.security.cts.SELinuxDomainTest # testInitDomain
解決思路:
fork進程時,參考3.4節(jié)中做domain切換回俐。
5.參考
本文主要參考了MTK-Online的Quick-start中《SELinux 問題快速分析》的內(nèi)容逛腿,感謝原作者們的辛勤付出。另外鲫剿,結(jié)合源碼和自身開發(fā)實踐鳄逾,增加了一些自身理解和實踐內(nèi)容稻轨。