目 錄
案例一、OTA升級過程中出現/system/etc/ppp/ip-up-vpn無法添加scontext 15
Selinux梳理總結
前緒
該文檔只是將selinux中最基礎的知識點進行了整理羅列,其他還有很多高級用法及昇華拓展部分,如SElinux域轉換、角色轉換以及SElinux框架等,並未收錄,有需要了解的可以參照參考文獻部分中的《SElinux詳解》文檔,後續會繼續補充完善。
一、Selinux基礎概述
SELinux則是由美國NSA(國安局)和一些公司(RedHat、Tresys)設計的一個針對Linux的安全加強系統。
NSA最初設計的安全模型叫FLASK,全稱爲Flux Advanced Security Kernel(由Uta大學和美國國防部開發,後來由NSA將其開源),當時這套模型針對DTOS系統。後來,NSA覺得Linux更具發展和普及前景,所以就在Linux系統上重新實現了FLASK,稱之爲SELinux。
Linux Kernel中,SELinux通過Linux Security Modules實現。在2.6之前,SElinux通過Patch方式發佈。從2.6開始,SELinux正式入駐內核,成爲標配。後來,鑑於Selinux的安全性和使用性,Google在Android 4.4上正式推出的一套以SELinux爲基礎於核心的系統安全機制,且命名爲SEandroid,自此,selinux便被"移植"到了Android上了。
二、什麼是Selinux?
SELinux出現之前,Linux上的安全模型叫DAC,全稱是Discretionary Access Control(自主訪問控制)。DAC的核心思想很簡單,就是:進程理論上所擁有的權限與執行它的用戶的權限相同。比如,以root用戶啓動Browser,那麼Browser就有root用戶的權限,在Linux系統上能幹任何事情。
顯然,DAC太過寬鬆了,所以各路高手想方設法都要在Android系統上搞到root權限。那麼SELinux如何解決這個問題呢?原來,它在DAC之外,設計了一個新的安全模型,叫MAC(Mandatory Access Control),翻譯爲強制訪問控制。MAC的處世哲學非常簡單:即任何進程想在SELinux系統中幹任何事情,都必須先在安全策略配置文件中賦予權限。凡是沒有出現在安全策略配置文件中的權限,進程就沒有該權限。來看一個SEAndroid中設置權限的例子:
[external/sepolicy/netd.te]
/*
允許(allow )netd域(domain)中的進程寫(write)類型爲proc的文件
*/
allow netd proc:file write
如果沒有在netd.te中使用上例中的權限配置allow語句,則netd就無法往/proc目錄下得任何文件中寫數據,即使netd具有root權限。
顯然,MAC比DAC在權限管理這一塊要複雜,要嚴格,要細緻得多。
關於DAC和MAC:
-
Linux系統先做DAC檢查。如果沒有通過DAC權限檢查,則操作直接失敗。通過DAC檢查之後,再做MAC權限檢查。
-
SELinux中也有用戶的概念,但它和Linux中原有的user概念不是同一個概念。比如,Linux中的超級用戶root在SELinux中可能就是一個沒權限,沒地位,打打醬油的"路人甲"。當然,這一切都由SELinux安全策略的制定者來決定。
這裏只是借用一個例子先對selinux做一個初步的瞭解,下面將對selinux特有的語法做簡單介紹。
三、SELinux Policy語言
在網上看到一種比喻的說法,個人感覺很有道理:Linux中有兩種東西,一種死的(Inactive),一種活的(Active)。活的東西就是進程,而死的東西就是資源(eg:文件、套接字等)。他們之間就是一種操作和被操作,或者說使用與被使用的關係。進程能發起動作,例如它能打開文件並操作它。而文件只能被進程操作。SElinux Policy語言就是將死的和活的東西都給打上"標籤",通過"標籤"將系統內的資源(包括進程)分成不同的角色(比如:用戶、客體),進而對整個系統資(包括進程)進行合理安全的管控。
1、安全屬性——SContext
在SELinux世界裏,每種東西都會被賦予一個安全屬性,官方說法叫Security Context,也可以理解爲上文所提到的"標籤"。通過每個資源的屬性,對他們進行歸類管理。Security Context(以後用SContext表示)是一個字符串,由四部分組成,被分爲兩類:進程SContext和資源SContext。根據SELinux規範,完整的SContext字符串爲:
user:role:type[:range]
注意,[]中的內容表示可選項。s0屬於range中的一部分,表示安全級別(這裏不做討論)。
首先,我們通過下面例子看下進程SContext。如SEAndroid中,進程的SContext可通過ps -Z命令查看,如圖1所示:
最左邊的那一列就是進程的SContext,以第一個進程/system/bin/logwrapper的SContext爲例,其值爲u:r:init:s0,其中:
-
u——user,用戶,SEAndroid中僅定義了一個SELinux用戶,值爲u。
-
r——role,role是角色之意,它是SELinux中一種比較高層次,更方便的權限管理思路,即Role Based Access Control(基於角色的訪問控制,簡稱爲RBAC)。簡單點說,一個u可以屬於多個role,不同的role具有不同的權限。RBAC我們到最後再討論。
-
init——tyoe,代表該進程所屬的Domain爲init。MAC的基礎管理思路其實不是針對上面的RBAC,而是所謂的Type Enforcement Accesc Control(簡稱TEAC,一般用TE表示)。對進程來說,Type就是Domain。比如init這個Domain有什麼權限,都需要通過[例子1]中allow語句來說明。
-
S0——和SELinux爲了滿足軍用和教育行業而設計的Multi-Level Security(MLS)機制有關。簡單點說,MLS將系統的進程和文件進行了分級,不同級別的資源需要對應級別的進程才能訪問。限於篇幅,這裏不做過多探討。
接着,我們看下文件的SContext,在SEandriod中,文件的SContext可以通過ls -Z查看:
如上圖所示,右邊數第二列就是所對應的SContext信息。以root目錄爲例,其對應SContext爲:u:object_r:rootfs:s0:
-
u——同樣是user之意,它代表創建這個文件的SELinux user。
-
object_r——role,這裏指文件,在SELinux中,使用object_r表示它的role。
-
rootfs——死的東西的Type,和進程的Domain其實是一個意思。它表示root目錄對應的Type是rootfs。
-
s0——MLS的級別。
2、TE簡介
有了SContext就可以對系統內所有資源進行統一管理了。SElinux的安全模型(MAC)有兩種管理機制,分別是TEAC(Type Enforcement Accesc Control,後文簡稱TE)和RBAC(Role Based Accesc Control)。RBAC是更高級的一種管理機制(由於SEandroid涉及不多,這裏不予展開談論),不過也是基於TE的。可見,TE是SElinux的管理核心,下面我們就對TE語法做詳細解讀。
在沒有介紹具體的語法之前,首先需要了解下與TE語句相關的另外幾個概念定義:
1). 客體類別和許可:
客體類別及其許可是 SELinux 中訪問控制的基礎,客體類別代表資源的範疇,如文件和
套接字,許可代表對這些資源的訪問權限,如讀或發送。一個客體類別代表某個確定類型(如文件或套接字)的所有資源,一個客體類別的實例(如某個特定的文件或套接字)被稱爲一個客體。客體類別指的是資源(文件)的所有範疇,客體指的是客體類別的某個特定實例(如: /etc/passwd)。
【聲明定義方法】
聲明許可有兩種方法,第一種叫做通用許可(被多個客體類別所共用),它允許我們創建與客體類別一起作爲一個組的許可,通用許可在類似的客體類別(如文件和符號連接)共享一套訪問許可時很有用;第二種方法叫做特定類別許可(由某個客體類別所獨用),它允許我們單獨爲客體類別聲明特定的許可,正如將會看到的,有一些客體類別只有特定的許可,有一些只有通用許可,還有一些是這兩者都有。客體類別和許可定義方法如下:
聲明客體類別:class 類別名字
通用許可:common 通用名 {許可集}
聯合許可和客體類別:class 類別名 [inherits 通用許可集名] [{許可集}]
此外,既然有客體,肯定還有主體一說,即誰去使用、操作客體。主體和客體只是邏輯上的區別,只有放在安全控制語句中才能看出誰是主體誰是客體。但,既然是控制,主體一般來說肯定是"活的"纔有可能去控制"死的"東西,所以,在SEandriod中,主體一般指的就是進程,客體可以是文件等資源,也可以是進程(因爲進程一樣可以被進程操作)。
2). 訪問向量規則:
訪問向量規則又稱AV規則,屬於SElinux策略語言的控制部分。常用的通用AV規則有如下4個:
-
allow:表示允許主體對客體執行允許的操作
-
dontaudit:表示不記錄違反規則的決策信息,且違反規則不影響運行(允許操作且不記錄)
-
auditallow:表示允許操作並記錄訪問決策信息(允許操作且記錄)
-
neverallow:表示不允許主體對客體執行指定的操作
好了,到此,我們就可以組合成最基本的SElinux策略語句了。一般,完整的基本安全控制語句格式爲:
AV規則 主體 客體:客體類別 許可
還拿之前的例子做說明:
allow netd proc:file write
其中:
allow——AV規則
netd——主體
proc——客體
file——客體類別
write——許可
當然,後面還有很多其他策略語句,將在介紹系統文件時,一併概述。
3). AV規則
對於AV規則,前文已經有所提及,比如allow。SElinux中主要的有效規則有allow,auditallow,auditdeny,dontaudit和neverallow,下面逐一介紹。
-
允許規則allow
我們使用allow規則指出了所有運行時授予的許可,它們是SELinux策略中允許許可的唯一方法,注意:默認情況下,不允許任何訪問,我們指定了兩個類型列表(源和目標類型),根據列出的客體類別的許可指定訪問權,如:
allow user_t bin_t : file { read execute };
這個規則允許任何安全上下文中類型具有user_t的進程對任何安全上下文中具有類型爲bin_t的普通文件所有read和execute訪問權。allow規則共享了通用AV規則的的所有語法,並且也沒有增加任何額外的語法了。
-
審覈規則auditallow/auditdeny/dontaudit
SELinux有大量的工具記錄日誌信息,或審覈、訪問嘗試被策略允許或拒絕的信息。審覈消息通常叫做"AVC消息",它提供了詳細了關於訪問嘗試的信息,包括是允許還是拒絕,源和目標的安全上下文,以及其它一些訪問嘗試涉及到資源信息。AVC消息與其它內核消息類似,都是存儲在/var/log目錄下的日誌文件中,它是策略開發、系統管理和系統監視不可缺少的工具。在此,我們檢查是哪一個訪問嘗試產生了審覈消息。
默認情況下,SELinux不會記錄任何允許的訪問檢查,只會記錄被拒絕的訪問檢查。這並沒什麼奇怪的,在大多數系統上,每秒會允許成千上萬的訪問,只有很少的一部分會被拒絕,允許的訪問通常是在預料之中的,通常不需要審覈,被拒絕的訪問通常是(但不總是)非預期的訪問,對它們進行審覈便於管理員發現策略的bug和可能的入侵嘗試。策略語言允許我們取消這些默認的預料之中的拒絕審覈消息,改爲記錄允許的訪問嘗試審覈消息。
SELinux提供兩個AV規則允許我們控制審覈哪一種訪問嘗試:dontaudit和auditallow。使用這兩條規則我們就可以改變默認的審覈類型了,最常用的是dontaudit規則,它指出哪一個訪問嘗試被拒絕時不審覈,這樣就覆蓋了SELinux默認的審覈所有拒絕的訪問嘗試的行爲。
dontaudit httpd_t etc_t : dir search;
注意,審覈(audit)規則讓我們覆蓋了默認的審覈設置,allow規則指出了什麼訪問是允許的,auditallow規則不允許訪問,它只審覈允許的許可。
-
禁止規則neverallow
最後一個AV規則是neverallow規則,我們使用這個規則來指定永遠不會被allow規則執行的訪問,你可能會疑惑,爲什麼會有這個規則?因爲默認情況下所有的訪問都是被拒絕的,設計這個規則的主要目的是爲了幫助編寫策略時,可以明確地指出不想要的訪問許可,因此可以預防意外發生,回想一下,在一個SELinux策略中可能包含成千上萬條規則,可能不小心加入了我們本不想授予的訪問權,此時,neverallow規則就可以幫助預防這種情況發生,如:
neverallow user_t shadow_t : file write;
這條neverallow規則可以有效地阻止我們在策略中添加一條允許user_t對類型爲shadow_t的文件進行寫操作的規則,如果添加了這樣的規則在編譯時就會報錯,這條規則不會移除訪問權,它只是會產生編譯錯誤。我們在編寫策略時,neverallow規則往往放在allow規則前面,首先聲明哪些訪問是明確地被拒絕的,然後再聲明哪些訪問是可以接受的,這樣就可以預防我們人爲出錯了。
neverallow規則支持一些特殊的其它AV規則不支持的語法,在neverallow規則中的源和目標類型列表中可以使用通配符(*)和求補算操作符(~),如:
neverallow * domain : dir ~{ read getattr };
這條規則指出沒有哪條allow可以授予任何類型對具有domain屬性的類型的目錄有任何訪問權,除了read和getattr訪問權外(即讀訪問權),這條規則的中通配符意味着所有的類型,在真實的策略中,類似這樣的規則很常見,它們用來阻止對/proc/目錄適當的訪問。
我們從前面這個例子中看出,在源類型列表中需要使用通配符,因爲我們想要指出任何類型或所有類型,包括那些還沒有創建的類型,使用通配符可以預防我們未來犯錯。
另一個常見的neverallow規則是:
neverallow domain ~domain : process transition;
這條neverallow規則增強了domain屬性,它指出了進程不能轉換到無domain屬性的類型,這就使得要爲一個類型無doamin屬性的進程創建一個有效的策略是不可能的。
四、SElinux策略文件
SElinux的策略文件主要集中在external\sepolicy下,這裏也是我們添加修改安全策略的主要地方。這裏所有的安全策略在編譯後最終都會彙集到out/target/product/generic/obj/ETC/sepolicy_intermediates/policy.conf文件中,然後再經過轉化load到linux kernel。下面我們就對external\sepolicy目錄下的文件進行逐一解析說明。
打開external\sepolicy目錄,我們會發現裏面有很多.te文件,這些te文件就是我們定義的策略文件,除了策略文件之外,還有很多沒有後綴的,這些沒有後綴的文件就是專門定義策略語句元素的。由於所有的te文件中的規則語句都是基於這些語句元素組成的,這裏我們先來看下這些定義性文件:
1、定義性文件
users
該文件中定義了SElinux中唯一的用戶u:
user u roles { r } level s0 range s0 - mls_systemhigh;
roles
該文件定義了SElinux中唯一的角色r:
role r;
role r types domain;
security_classes
該文件定義了所有通用客體類別:
# FLASK
#
# Define the security object classes
#
# Classes marked as userspace are classes
# for userspace object managers
class security
class process
class system
class capability
....
attributes
該文件定義了所有SContext中的通用type:
######################################
# Attribute declarations
#
# All types used for devices.
attribute dev_type;
# All types used for processes.
attribute domain;
# All types used for filesystems.
attribute fs_type;
....
access_vectors
該文件定義了資源的所有通用許可:
#
# Define common prefixes for access vectors
#
# common common_name { permission_name ... }
#
# Define a common prefix for file access vectors.
#
common file
{
ioctl
read
write
create
getattr
setattr
lock
relabelfrom
relabelto
append
unlink
link
rename
execute
swapon
quotaon
mounton
}
.....
file.te
該文件自定義了file_contexts中的所有文件類型的type:
# Filesystem types
type labeledfs, fs_type;
type pipefs, fs_type;
type sockfs, fs_type;
type rootfs, fs_type;
type proc, fs_type;
....
device.te
該文件自定義了file_contexts中的所有設備節點的type:
# Device types
type device, dev_type, fs_type;
type alarm_device, dev_type, mlstrustedobject;
type adb_device, dev_type;
file_contexts
....
該文件定義了所有文件資源的SContext:
#/##########################################
# Root
/ u:object_r:rootfs:s0
# Data files
/adb_keys u:object_r:adb_keys_file: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
/ueventd\..* u:object_r:rootfs:s0
....
genfs_contexts
定義了虛擬文件的SContext:
# Label inodes with the fs label.
genfscon rootfs / u:object_r:rootfs:s0
# proc labeling can be further refined (longest matching prefix).
genfscon proc / u:object_r:proc:s0
genfscon proc /net u:object_r:proc_net:s0
....
fs_use
定義了所有文件系統類型的SContext:
# Label inodes via getxattr.
fs_use_xattr yaffs2 u:object_r:labeledfs:s0;
fs_use_xattr jffs2 u:object_r:labeledfs:s0;
fs_use_xattr ext2 u:object_r:labeledfs:s0;
....
service.te
定義了所有service_contexts中SContext的type:
type bluetooth_service, service_manager_type;
type default_android_service, service_manager_type;
type drmserver_service, service_manager_type;
....
service_contexts
定義了所有所有系統service的SContext:
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
....
mac_permissions.xml
與seapp_contexts一起負責爲接下來安裝的不同類型的app添加SContext。
<?xml version="1.0" encoding="utf-8"?>
<policy>
....
<!-- Platform dev key in AOSP -->
<signer signature="@PLATFORM" >
<seinfo value="platform" />
</signer>
<!-- All other keys -->
<default>
<seinfo value="default" />
</default>
</policy>
seapp_contexts
與mac_permissions.xml一起負責爲接下來安裝的不同類型的app添加SContext。
isSystemServer=true domain=system_server
user=system seinfo=platform domain=system_app type=system_app_data_file
user=bluetooth seinfo=platform domain=bluetooth type=bluetooth_data_file
user=nfc seinfo=platform domain=nfc type=nfc_data_file
....
2、其他te文件
剩餘的.te文件中主要是各個進程的具體策略控制。這裏我們拿recovery.te來舉例說明。
recovery.te
# recovery console (used in recovery init.rc for /sbin/recovery)
# Declare the domain unconditionally so we can always reference it
# in neverallow rules.
type recovery, domain;
# But the allow rules are only included in the recovery policy.
# Otherwise recovery is only allowed the domain rules.
recovery_only(`
allow recovery self:capability { chown dac_override fowner fsetid setfcap setuid setgid sys_admin sys_tty_config };
# Set security contexts on files that are not known to the loaded policy.
allow recovery self:capability2 mac_admin;
# Run helpers from / or /system without changing domain.
allow recovery rootfs:file execute_no_trans;
allow recovery system_file:file execute_no_trans;
.....
allow recovery kernel:process setsched;
')
....
neverallow recovery data_file_type:file { no_w_file_perms no_x_file_perms };
neverallow recovery data_file_type:dir no_w_dir_perms;
這裏定義了有關recovery的所有策略控制。
首先,文件開頭定義了進程域名recovery,繼承自domain,說明這裏新定義的recovery域爲進程域。接下來在recovery_only中制定了很多策略控制。
如:allow recovery system_file:file execute_no_trans;表示允許SContext標誌爲recovery的進程不可傳遞域地去執行SContext爲system_file的文件。
最後還有neverallow檢查語句,
如:neverallow recovery data_file_type:dir no_w_dir_perms;表示不允許SContext標誌爲recovery的進程去no_w_dir_perms SContext爲data_file_type的目錄。
五、如何配置SElinux
在支持SElinux的Android版本中,有關selinux策略文件有兩處,一個是google原生的,另一個是產品平臺配置的,他們分別對應如下目錄:
google原生目錄:s912/external/sepolicy
平臺配置目錄:s912/device/amlogic/common/sepolicy
在編譯時, 系統會以合併的方式,將平臺配置目錄下的policy 附加到Google 原生的policy 上,而非替換。一般情況下,不建議修改google原生目錄下的策略控制。接下來看下如何配置平臺的selinux。
找到平臺下的BoardConfig.mk,其相關變量配置就是BOARD_SEPOLICY_DIRS,目的就是指定平臺sepolicy的目錄。
BoardConfig.mk中直接包含了sepolicy的mkfile文件:
include device/amlogic/common/sepolicy.mk
打開該sepolicy.mk文件可以看到BOARD_SEPOLICY_DIRS變量的配置:
BOARD_SEPOLICY_DIRS := \
device/amlogic/common/sepolicy
六、SElinux調試方法
1、selinux的編譯
首先我們關注下selinux的編譯,大家知道編譯整個Android工程是個很耗時的工作。爲了避免耗時,在我們修改或者添加了selinux策略之後,建議先獨自對sepolicy模塊進行一次編譯,如果通過了,然後再進行整個Android的編譯。具體指令如下:
如:mmm external/sepolicy
2、如何快速確認是selinux引起?
目前所有的SELinux check 失敗,在kernel log 或者android log(L版本後)中都有對應的" avc: denied"的LOG 與之對應。舉例如下:
<5>[ 17.285600].(0)[503:idmap]type=1400 audit(1356999072.320:202): avc: denied { create } for pid=503 comm="idmap" name="overlays.list" scontext=u:r:zygote:s0 tcontext=u:object_r:resource_cache_data_file:s0 tclass=file
即表明idmap 這個process, 使用zygote 的source context, 訪問/data/resource_cache 目錄,並創建文件時,被SELinux 拒絕訪問。
但反過來,有此LOG,並非就會直接失敗,還需要確認當時SELinux 的模式, 是enforcing mode 還是 permissve mode.這裏需要提到的是selinux有3中模式:permissive、enforcing和disabled。
-
enforcing:強制模式,代表 SELinux 運作中,且已經正確的開始限制 domain/type 了;
-
permissive:寬容模式:代表 SELinux 運作中,不過僅會有警告訊息並不會實際限制 domain/type 的存取。這種模式可以運來作爲 SELinux 的 debug 之用;
-
disabled:關閉,SELinux 並沒有實際運作。
如果問題容易復現,我們可以先將SELinux 模式調整到Permissive mode,然後再測試確認是否與SELinux 約束相關.
指令如下:
查看selinux當前模式:
[root@python bin]# getenforce
Permissive
更改當前的SELINUX值 ,後面可以跟 enforcing,permissive 或者 1, 0。
[root@python bin]# setenforce permissive
[root@python bin]# setenforce enforcing
3、修改/添加selinux策略
首先, 務必確認對應進程訪問系統資源是否正常, 是否有必要?如果本身是異常非法訪問,那麼就要自行消除訪問。
其次,如果確認訪問是必要,並且正常的,那麼就要對對應的process/domain 增加新的policy.
具體步驟大致如下:
1).簡化方法
1.1 提取所有的avc LOG. 如 adb shell "cat /proc/kmsg | grep avc" > avc_log.txt
1.2 使用 audit2allow tool 直接生成policy. audit2allow -i avc_log.txt 即可自動輸出生成的policy
1.3 將對應的policy 添加到selinux policy 規則中,對應MTK Solution, 您可以將它們添加在KK: mediatek/custom/common/sepolicy, L: device/mediatek/common/sepolicy 下面,如
allow zygote resource_cache_data_file:dir rw_dir_perms;
allow zygote resource_cache_data_file:file create_file_perms;
===> mediatek/custom/common/sepolicy/zygote.te (KK)
===> device/mediatek/common/sepolicy/zygote.te (L)
注意audit2allow 它自動機械的幫您將LOG 轉換成policy, 而無法知道你操作的真實意圖,有可能出現權限放大問題。
2).按需確認方法
此方法需要工程人員對SELinux 基本原理,以及SELinux Policy Language 有了解.
2.1 確認是哪個進程訪問哪個資源,具體需要哪些訪問權限,read ? write ? exec ? create ? search ?
2.2 當前進程是否已經創建了policy 文件? 通常是process 的執行檔.te,如果沒有,並且它的父進程即source context 無須訪問對應的資源,則創建新的te 文件.
在L 版本上, Google 要求維護關鍵 security context 的唯一性, 比如嚴禁zygote, netd, installd, vold, ueventd 等關鍵process 與其它process 共享同一個security context.
2.3 創建文件後,關聯它的執行檔,在file_contexts 中, 關聯相關的執行檔.
比如 /system/bin/idmap 則是 /system/bin/idmap u:object_r:idmap_exec:s0
2.4 填寫policy 到相關的te 文件中
如果沿用原來父進程的te 文件,則直接添加.
如果是新的文件,那麼首先:
#==============================================
# Type Declaration
#==============================================
type idmap, domain;
type idmap_exec, exec_type, file_type;
#==============================================
# Android Policy Rule
#==============================================
#permissive idmap;
domain_auto_trans(zygote, idmap_exec, idmap);
然後添加新的policy
# new policy
allow idmap resource_cache_data_file:dir rw_dir_perms;
allow idmap resource_cache_data_file:file create_file_perms;
3).權限放大情況處理
如果直接按照avc: denied 的LOG 轉換出SELinux Policy, 往往會產生權限放大問題. 比如因爲要訪問某個device, 在這個device 沒有細化SELinux Label 的情況下, 可能出現:
<7>[11281.586780] avc: denied { read write } for pid=1217 comm="mediaserver" name="tfa9897" dev="tmpfs" ino=4385 scontext=u:r:mediaserver:s0 tcontext=u:object_r:device:s0 tclass=chr_file permissive=0
如果直接按照此LOG 轉換出SELinux Policy: allow mediaserver device:chr_file {read write}; 那麼就會放開mediaserver 讀寫所有device 的權限. 而Google 爲了防止這樣的情況, 使用了neverallow 語句來約束, 這樣你編譯sepolicy 時就無法編譯通過.
爲了規避這種權限放大情況, 我們需要細化訪問目標(Object) 的SELinux Label, 做到按需申請. 通常會由三步構成
3.1 定義相關的SELinux type.
比如上述案例, 在 device/mediatek/common/sepolicy/device.te 添加
type tfa9897_device, dev_type;
3.2 綁定文件與SELinux type.
比如上述案例, 在 device/mediatek/common/sepolicy/file_contexts 添加
/dev/tfa9897(/.*)? u:object_r:tfa9897_device:s0
3.3 添加對應process/domain 的訪問權限.
比如上述案例, 在 device/mediatek/common/sepolicy/mediaserver.te 添加
allow mediaserver tfa9897_device:chr_file { open read write };
那麼,哪些訪問對象通常會遇到此類呢?(以L 版本爲例)
*device類型
-- 類型定義: external/sepolicy/device.te;device/mediatek/common/sepolicy/device.te
-- 類型綁定: external/sepolicy/file_contexts;device/mediatek/common/sepolicy/file_contexts
*File類型:
-- 類型定義: external/sepolicy/file.te;device/mediatek/common/sepolicy/file.te
-- 綁定類型: external/sepolicy/file_contexts;device/mediatek/common/sepolicy/file_contexts
*虛擬File類型:
-- 類型定義: external/sepolicy/file.te;device/mediatek/common/sepolicy/file.te
-- 綁定類型: external/sepolicy/genfs_contexts;device/mediatek/common/sepolicy/genfs_contexts
*Service類型:
-- 類型定義: external/sepolicy/service.te; device/mediatek/common/sepolicy/service.te
-- 綁定類型:external/sepolicyservice_contexts;device/mediatek/common/sepolicy/service_contexts
*Property類型:
-- 類型定義: external/sepolicy/property.te;device/mediatek/common/sepolicy/property.te
-- 綁定類型: external/sepolicy/property_contexts;device/mediatek/common/sepolicy/property_contexts;
七、案例分析
案例一、OTA升級過程中出現/system/etc/ppp/ip-up-vpn無法添加scontext
【問題現象】
製作好的差分包在升級過程中出現如下錯誤而導致升級失敗,且重啓後一直處於recovery模式下:
Symlinks and permissions...
ApplyParsedPerms: lsetfilecon of /system/etc/ppp/ip-up-vpn to u:ppp_system_file:s0 failed:Permission denied
set_metadata:some changes failed
E:Error in /udisk/update.zip
(Status 7)
該問題與問題一的提示信息基本一致,爲何還是將本問題摘出來?這是因爲他們的改動方案有所不同。
【問題分析】
出現上述錯誤後,根據提示信息可以確定升級包校驗以及系統校驗已經完成。這裏是由於recovery中涉及selinux方面在向/system/etc/ppp/ip-up-vpn文件添加"u: ppp_system_file:s0"上下文(scontext)時因權限問題被拒絕了。
【解決方案】
方案1、3同問題一。
方案2、將META-INF\com\google\android\updater-script中的下列條目刪除:
set_metadata_recursive("/system/etc/ppp ", "uid", 0, "gid", 0, "dmode", 0755, "fmode", 0555, "capabilities", 0x0, "selabel", "u:object_r:ppp_system_file:s0");
方案4、根據方案2中所刪除的額條目中的function可以看出,該條目目的是要在system/etc/ppp目錄下進行遞歸加scontext,但不知爲何沒有生效。由於system/etc/ppp目錄下只有ip-up-vpn一個文件,該方案仿照問題一中方案4按照直接操作ip-up-vpn文件的思路進行添加修改,具體修改如下:
a、ip-up-vpn也是可執行文件,首先定義出該文件的selinux權限規則:
在device/amlogic/common/sepolicy目錄下創建ip-up-vpn.te文件,並添加如下內容:
type ip-up-vpn, domain;
type ip-up-vpn_exec, exec_type, file_type;
init_daemon_domain(ip-up-vpn)
# Third line is important
allow ip-up-vpn input_device:chr_file rw_file_perms;
allow ip-up-vpn ppp_exec:file { execute_no_trans execute getattr read open };
allow ip-up-vpn ip-up-vpn_exec:file { entrypoint read setattr execute };
並更改其傳統linux權限爲777:
chmod 777 ip-up-vpn.te
b、在file_contexts中定義ip-up-vpn上下文屬性(scontext):
/system/etc/ppp/ip-up-vpn u:object_r:ip-up-vpn_exec:s0
c、在recovery.te中添加操控ip-up-vpn的權限:
allow recovery ip-up-vpn_exec:file {create_file_perms relabelfrom relabelto create write setattr};
【結果確認】updater-script中該條目通過selinux操作,ota差分升級成功。
案例二、recovery刪除data目錄下的文件
在recover升級的時候,我們有個需要從服務器上下載升級包,然後放在data/download,這部分工作是放在一個systemapp中完成的。
然後重啓進入recoveyr模式,在recovery升級完之後,我們需要在recovery中刪除這個文件,這部分是在recovery中完成。
功能很簡單,但是在android6.0上碰到selinux的問題。
我們先來看recovery.te中是不允許recovery操作data下面的目錄,也就是下面這個neverallow原則
# Recovery should never touch /data.
#
# In particular, if /data is encrypted, it is not accessible
# to recovery anyway.
#
# For now, we only enforce write/execute restrictions, as domain.te
# contains a number of read-only rules that apply to all
# domains, including recovery.
#
# TODO: tighten this up further.
neverallow recovery data_file_type:file { no_w_file_perms no_x_file_perms };
neverallow recovery data_file_type:dir no_w_dir_perms;
我們再來看下system_data_file在file_contexts.te中的定義
/data(/.*)? u:object_r:system_data_file:s0
而在file.te定義了system_data_file文件屬於data_file_type類型,因此recovery也是不能操作data下面的文件
[html] view plain copy 在CODE上查看代碼片派生到我的代碼片
./file.te:56:type system_data_file, file_type, data_file_type;
在我們可以自己定義自己的file類型,我們可以定義data/download屬於download_data_file類型
/data/download(/.*)? u:object_r:download_data_file:s0
然後在file.te中定義download_data_file類型, 注意是隻屬於file_type類型的。
type download_data_file, file_type;
然後在recovery.te中增加對download_data_file的權限
allow recovery download_data_file:dir { write search remove_name };
allow recovery download_data_file:file { read getattr unlink };
另外我們還需在一個systemapp中先下載升級包,所以需要在system_app.te中增加對如下權限:
allow system_app download_data_file:dir { search write add_name getattr remove_name };
allow system_app download_data_file:file { create read write open getattr unlink };
但是最後還是失敗了,爲什麼呢,因爲一開始沒有data/download這個目錄,而是我們再app中自己下載的時候創建的目錄,所以是system_data_file類型的。那麼我們就需要在一開始就有data/download目錄,所以在init.rc中添加如下代碼:
mkdir /data/download 0771 system system
最後我們還需要在uncrypt.te中增加如下,因爲data目錄有可能需要進行加密處理。
allow uncrypt download_data_file:dir { search getattr };
allow uncrypt download_data_file:file { getattr read open };
八、常用指令
SELinux 是個經過安全強化的Linux操作系統,實際上,基本上原來的運用軟件沒有必要修改就能在它上面運行。真正做了特別修改的RPM包只要50多個。像文件系統EXT3都是經過了擴展。對於一些原有的命令也進行了擴展,另外還增加了一些新的命令,接下來我們就來看看這些命令。
1、文件操作
1)ls命令
在命令後加個 -Z 或者加 –context
[root@python azureus]# ls -Z
-rwxr-xr-x fu fu user_u:object_r:user_home_t azureus
-rw-r--r-- fu fu user_u:object_r:user_home_t Azureus2.jar+
-rw-r--r-- fu fu user_u:object_r:user_home_t Azureus.png
2)chcon
更改文件的標籤
[root@python tmp]# ls --context test.txt
-rw-r--r-- root root root:object_r:staff_tmp_t test.txt
[root@python tmp]# chcon -t etc_t test.txt
[root@python tmp]# ls -lZ test.txt
-rw-r--r-- root root root:object_r:etc_t test.txt
3)restorecon
當這個文件在策略裏有定義是,可以恢復原來的 文件標籤。
4)setfiles
跟chcon一樣可以更改一部分文件的標籤,不需要對整個文件系統重新設定標籤。
5)fixfiles
一般是對整個文件系統的, 後面一般跟 relabel,對整個系統 relabel後,一般我們都重新啓動。如果,在根目錄下有.autorelabel空文件的話,每次重新啓動時都調用 fixfiles relabel
6)star
就是tar在SELinux下的互換命令,能把文件的標籤也一起備份起來。
7)cp
可以跟 -Z, --context=CONTEXT 在拷貝的時候指定目的地文件的security context
8)find
可以跟 –context 查特定的type的文件。
例子:
find /home/fu/ --context fu:fu_r:amule_t -exec ls -Z {} \:
9)run_init
在sysadm_t裏手動啓動一些如Apache之類的程序,也可以讓它正常進行,domain遷移。
2、進程domain的確認
程序現在在那個domain裏運行,我們可以在ps 命令後加 -Z
[root@python /]# ps -eZ
LABEL PID TTY TIME CMD
system_u:system_r:init_t 1 ? 00:00:00 init
system_u:system_r:kernel_t 2 ? 00:00:00 ksoftirqd/0
system_u:system_r:kernel_t 3 ? 00:00:00 watchdog/0
3、ROLE的確認和變更
命令id能用來確認自己的 security context
[root@python ~]# id
uid=0(root) gid=0(root) groups=0(root),1(bin),2(daemon),3(sys),4(adm),6(disk),10(wheel) context=root:staff_r:staff_t
這裏,雖然是ROOT用戶,但也只是在一般的ROLE和staff_t裏運行,如果在enforcing模式下,這時的ROOT對於系統管理工作來說什麼也幹不了。
[root@python ~]# newrole -r sysadm_r
Authenticating root.
口令:
[root@python ~]# id
uid=0(root) gid=0(root) groups=0(root),1(bin),2(daemon),3(sys),4(adm),6(disk),10(wheel) context=root:sysadm_r:sysadm_t
4、模式切換
1)getenforce
得到當前的SELINUX值
[root@python bin]# getenforce
Permissive
2)setenforce
更改當前的SELINUX值 ,後面可以跟 enforcing,permissive 或者 1, 0。
[root@python bin]# setenforce permissive
3)sestatus
顯示當前的 SELinux的信息
[root@python bin]# sestatus -v
SELinux status: enabled
SELinuxfs mount: /selinux
Current mode: permissive
Mode from config file: permissive
Policy version: 20
Policy from config file: refpolicy
Process contexts:
Current context: user_u:user_r:user_t
Init context: system_u:system_r:init_t
/sbin/mingetty system_u:system_r:getty_t
/usr/sbin/sshd system_u:system_r:sshd_t
File contexts:
Controlling term: user_u:object_r:user_devpts_t
/etc/passwd system_u:object_r:etc_t
/etc/shadow system_u:object_r:shadow_t
/bin/bash system_u:object_r:shell_exec_t
/bin/login system_u:object_r:login_exec_t
/bin/sh system_u:object_r:bin_t -> system_u:object_r:shell_exec_t
/sbin/agetty system_u:object_r:getty_exec_t
/sbin/init system_u:object_r:init_exec_t
/sbin/mingetty system_u:object_r:getty_exec_t
5、其他重要命令
1)Audit2allow
很重要的一個以python寫的命令,主要用來處理日誌,把日誌中的違反策略的動作的記錄,轉換成 access vector,對開發安全策略非常有用。在refpolicy裏,它的功能比以前有了很大的擴展。
[root@python log]# cat dmesg | audit2allow -m local > local.te
2)checkmodule
編譯模塊
[root@python log]# checkmodule -m -o local.mod local.te
checkmodule: loading policy configuration from local.te
checkmodule: policy configuration loaded
checkmodule: writing binary representation (version 5) to local.mod
3)semodule_package
創建新的模塊
[root@python log]# semodule_package -o local.pp -m local.mod
4)semodule
可以顯示,加載,刪除 模塊
加載的例子:
[root@python log]# semodule -i local.pp
5)semanage
這是一個功能強大的策略管理工具,有了它即使沒有策略的源代碼,也是可以管理安全策略的。因爲我主要是介紹用源代碼來修改策略的,詳細用法大家可以參考它的man頁。
九、參考文獻
1. Security-Enhanced Linux in Android
https://source.android.com/security/selinux/index.html
2. 深入理解SELinux SEAndroid
http://blog.csdn.net/innost/article/details/19299937
3.SElinux詳解.pdf
4.類型強制(TE編寫規則)
http://blog.csdn.net/myarrow/article/details/10105961