筆者在工作中多次遇到和SELinux相關的問題,初次遇到時一頭霧水,走了很多彎路,也耗費了很多時間精力。後來看了不少資料和博客,也研究了相關代碼,對SELinux有了些認識。所以用本文來做個總結,加深理解。
本文將從下面五個方面來逐步認識和理解Android 下SELinux。
- 什麼是SELinux
- 爲什麼需要SELinux
- SElinux 工作原理
- android 上的實現
- 曾經遇到過的問題及解決辦法
SELinux 是 2.6 版本的 Linux 內核中提供的強制訪問控制(MAC)系統。對於目前可用的 Linux安全模塊來說,SELinux 是功能最全面,而且測試最充分的,它是在 20 年的 MAC 研究基礎上建立的。SELinux 在類型強制服務器中合併了多級安全性或一種可選的多類策略,並採用了基於角色的訪問控制概念。SELinux是一種基於 域-類型 模型(domain-type)的強制訪問控制(MAC)安全系統,它由NSA編寫並設計成內核模塊包含到內核中,相應的某些安全相關的應用也被打了SELinux的補丁,最後還有一個相應的安全策略。任何程序對其資源享有完全的控制權。
attribute dev_type;
# All types used for processes.
attribute domain;
# All types used for filesystems.
attribute fs_type;
# All domains used for apps.
attribute appdomain;
更多的策略配置實現在下節對照android上的實現來分析。
..........
union selinux_callback cb;
cb.func_log = log_callback;
selinux_set_callback(SELINUX_CB_LOG, cb);
cb.func_audit = audit_callback;
selinux_set_callback(SELINUX_CB_AUDIT, cb);
selinux_initialize();
.......
}
device/xxx/sepolicy
所添加的策略文件
BOARD_SEPOLICY_UNION := \
bluetooth.te \
# 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
common file
{
ioctl
read
write
create
getattr
setattr
lock
relabelfrom
relabelto
append
unlink
link
rename
execute
swapon
quotaon
mounton
}class dir
inherits file
{
add_name
remove_name
reparent
search
rmdir
open
audit_access
execmod
}external/sepolicy/global_macros全局宏變量定義文件,也就是用一個宏變量同時表示幾個屬性。如下例子define(`x_file_perms', `{ getattr execute execute_no_trans }')
define(`r_file_perms', `{ getattr open read ioctl lock }')
define(`w_file_perms', `{ open append write }')
define(`rx_file_perms', `{ r_file_perms x_file_perms }')
define(`ra_file_perms', `{ r_file_perms append }')
define(`rw_file_perms', `{ r_file_perms w_file_perms }')
define(`rwx_file_perms', `{ rw_file_perms x_file_perms }')
define(`link_file_perms', `{ getattr link unlink rename }')
define(`create_file_perms', `{ create setattr rw_file_perms link_file_perms }')external/sepolicy/te_macros 宏規則變量定義文件,也就是用一個變量來表示連續執行幾條規則。如下例子define(`domain_trans', `
# Old domain may exec the file and transition to the new domain.
allow $1 $2:file { getattr open read execute };
allow $1 $3:process transition;
# New domain is entered by executing the file.
allow $3 $2:file { entrypoint open read execute getattr };
# New domain can send SIGCHLD to its caller.
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 };
')external/sepolicy/file_contexts 文件安全上下文定義文件。就是說對於一個文件或者目錄,它的安全上下文應該在這裏定義,如果沒有定義,那麼默認和父目錄的安全上下文一樣。比如說我們在data目錄下建了一個log目錄存放系統運行時的log,那麼我們可能希望這個log目錄的的安全上下文是u:object_r:data_log_file:s0那麼就應該在file_contexts文件裏做如下定義:/data/log(/.*)?u:object_r:data_log_file:s0external/sepolicy/property_contexts 屬性安全上下文定義文件。比如說新加了一個persist.log. * 開頭的屬性,那麼就需要給它添加對應的安全上下文,如果不添加或者添加的上下文不對,那麼在的某個進程裏寫入時就寫不進去。比如做了如下定義,那麼是具有system_prop域安全上下文,只能在system進程裏修改值,在shell裏是不能訪問的。persist.log. u:object_r:system_prop:s0如果希望在shell裏能修改值,就應該這麼寫persist.log. u:object_r:shell_prop:s0 ,當然如果通過修改相關*.te 文件也能實現,但是不是很規範。external/sepolicy/property.te 文件允許我們定義自己的prop 類型域,還有seapp_contexts、service_contexts 文件等,都是類似的,都可以根據需要修改。下面幾個是常用到的規則操作allow:賦予某項權限。
allowaudit:audit含義就是記錄某項操作。默認情況下是SELinux只記錄那些權限檢查失敗的操作。allowaudit則使得權限檢查成功的操作也被記錄。注意,allowaudit只是允許記錄,它和賦予權限沒關係。賦予權限必須且只能使用allow語句。
dontaudit:對那些權限檢查失敗的操作不做記錄。
neverallow:前面講過,用來檢查安全策略文件中是否有違反該項規則的allow語句規則命令格式:規則名 主體域 客體域:類型權限屬性 ; 或者 規則名 主體域 客體域:類型 {權限屬性集合} ;比如:allow domain system_data_file:dir { search getattr };
allow domain system_file:file r_file_perms;規則都定義在以te爲後綴的文件裏面,比如見到的init.te installd.te system_app.te platform_app.te shell.te 等等。init.te 是針對init進程定義的規則,system_app.te 針對的是app進程是system的應用定義的規則,shell.te 針對的是shell進程定義的規則,以此類推。如果我們對某種類型的進程定義了新的規則,那麼我們應該把新規則添加到它對應的te文件裏面。比如我們在data目錄下加了一個log目錄,並且它的DAC權限爲777,安全上下文是u:object_r:data_log_file:s0,我們希望系統運行過程通過shell命令查看log,那麼我們就可以往shell.te 文件添加如下策略:allow shelldata_log_file:dir r_dir_perms;
allow shell data_log_file:file r_file_perms;如果我們新啓了一個進程,並且希望它運行在自己定義的安全上下文裏面,那麼我們應該給它添加對應的te規則文件。
五、 曾經遇到過的問題和解決辦法
1. 在init.rc 文件裏啓動一個log服務,一直屬於restarting 狀態,無法啓動。系統輸出如下warning log。
W/init ( 9795): type=1400 audit(0.0:284): avc: denied { execute_no_trans } for path="/system/bin/start_log.sh" dev="dm-0" ino=430 scontext=u:r:init:s0 tcontext=u:object_r:logsvc_exec:s0 tclass=file permissive=0
主體是:init進程,對應域是u:r:init:s0
客體是:system/bin/start_log.sh 對應域是u:object_r:logsvc_exec:s0
這個log的意思就是init進程在執行system/bin/start_log.sh 時沒有 execute_no_trans權限。那我們就給它init加權限。
找到init.te 文件添加如下到末尾:
allow init logsvc_exec:file execute_no_trans;
2. 在開發一個自動壓力測試程序,其中有一項是測試休眠喚醒相關的,會通過讀取/d/suspend_stats 設備節點來判斷是否有休眠失敗情況。但是什麼也讀取不到這個值。打log發現如下warning。
09-07 12:22:52.480 W/AutoStressTools(32594): type=1400 audit(0.0:11): avc: denied { read } for name="suspend_stats" dev="debugfs" ino=10246 scontext=u:r:system_app:s0 tcontext=u:object_r:debugfs:s0 tclass=file permissive=0
主體是:AutoStressTools應用,屬於system app進程,對應域是u:r:system_app:s0
客體是:/d/suspend_stats 對應域是u:object_r:debugfs:s0
這個log的意思就是system讀取 /d/suspend_stats 時沒有 read權限。那我們就給它system app加權限。
找到system_app.te 文件添加如下到末尾:
allow system_app debugfs:file r_file_perms;
3. CTS認證相關的,在init.rc 添加一個服務後,CTS 測試failed掉。
-- testInitDomain |
junit.framework.AssertionFailedError: Expected 1 process in SELinux domain "u:r:init:s0" Found "[pid: "1" proctitle: "/init" label: "u:r:init:s0" vsize: 4161536, pid: "211" proctitle: "/system/bin/memsicp" label: "u:r:init:s0" vsize: 3977216]" expected:<1> but was:<2> at junit.framework.Assert.fail(Assert.java:50) |
那麼我們就另外給這個服務添加域就可以,並且添加對應的規則te文件。