android中SELINUX規則分析和語法簡介
目錄:
SELINUX簡介
SELINUX是可以理解為一種android上面的安全機制,是有美國國家安全域性和一些公司設計的一個針對linux的安全加強系統,我們可以通過配置SELINUX的相關policy,來定製自己的手機的一些許可權,比如,我們可以完全讓root使用者沒有任何的許可權和user一樣
檢視SELINUX許可權
在android裡面,有兩個型別:
- 程序
- 檔案
針對這兩種型別,我們可以先來看看他們的不同。
1. 檢視程序許可權
在android上面,adb shell
之後進入手機,ps -Z
可以檢視當前程序所擁有的selinux的許可權。
$ ps -Z LABEL USER PID PPID NAME u:r:init:s0 root 1 0 /init u:r:kernel:s0 root 2 0 kthreadd ..
. u:r:kernel:s0 root 258 2 irq/322-HPH_R O u:r:logd:s0 logd 259 1 /system/bin/logd u:r:healthd:s0 root 260 1 /sbin/healthd u:r:lmkd:s0 root 261 1 /system/bin/lmkd u:r:servicemanager:s0 system 262 1 /system/bin/servicemanager u:r:vold:s0 root 263 1 /system/bin/vold u:r:surfaceflinger:s0 system 264 1 /system/bin/surfaceflinger u:r:tctd:s0 root 265 1 /system/bin/tctd u:r:rfs_access:s0 system 268 1 /system/bin/rfs_access u:r:tee:s0 system 271 1 /system/bin/qseecomd u:r:kernel:s0 root 280 2 kworker/3:1H u:r:kernel:s0 root 290 2 kauditd u:r:rmt_storage:s0 nobody 291 1 /system/bin/rmt_storage u:r:shell:s0 shell 292 1 /system/bin/sh u:r:netd:s0 root 295 1 /system/bin/netd u:r:debuggerd:s0 root 296 1 /system/bin/debuggerd u:r:tee:s0 system 297 271 /system/bin/qseecomd
在這個例子中,我們可以進行分析。
u
:在android中,只定義了一個user即為u
r
:程序統一定義成r
, 檔案統一定義成object_r
init
:程序所屬的域 , 不唯一。s0
:一種安全等級
2. 檢視檔案許可權
另外就是檔案,檔案想要檢視相關SELINUX許可權的話,需要去執行ls -Z
$ ls -Z
drwxr-x--x root sdcard_r u:object_r:rootfs:s0 storage
drwx--x--x root root u:object_r:tmpfs:s0 synthesis
dr-xr-xr-x root root u:object_r:sysfs:s0 sys
drwxr-xr-x root root u:object_r:system_file:s0 system
drwxrwxr-x system tctpersist u:object_r:tct_persist_file:s0 tctpersist
lrwxrwxrwx root root u:object_r:rootfs:s0 tombstones -> /data/tombstones
-rw-r--r-- root root u:object_r:rootfs:s0 ueventd.qcom.rc
-rw-r--r-- root root u:object_r:rootfs:s0 ueventd.rc
- u:在android中,只定義了一個user即為
u
- object_r:代表是一個檔案
- rootfs:這種代表檔案所屬的域,看有的解釋說可以理解成type
- s0:一種安全等級
如何配置selinux
1.基本語法
很多te檔案集中在external\sepolicy
資料夾下,廠商也是有自己的配置規則在devic
目錄下,例如RK專案中
BOARD_SEPOLICY_DIRS ?= device/rockchip/common/sepolicy
。BOARD_SEPOLICY_DIRS
在系統編譯時會被使用,非本文重點,有興趣的話請自行查閱相關資料。
A. 上下文描述檔案
可以在上下文的描述檔案中為您的物件指定標籤。
檔名 | 歸類 |
---|---|
mac_permissions.xml | App程序 |
seapp_contexts | App資料檔案 |
file_contexts | 系統檔案 |
property_contexts | 系統屬性 |
file_contexts
用於為檔案分配標籤,並且可供多種使用者空間元件使用。在建立新政策時,請建立或更新該檔案,以便為檔案分配新標籤。- genfs_contexts 用於為不支援擴充套件屬性的檔案系統(例如,
proc
或vfat
)分配標籤。 - property_contexts 用於為 Android 系統屬性分配標籤,以便控制哪些程序可以設定這些屬性。
- service_contexts 用於為 Android Binder 服務分配標籤,以便控制哪些程序可以為相應服務新增(註冊)和查詢(查詢)Binder 引用。
- seapp_contexts 用於為app程序和 /data/data 目錄分配標籤。
- mac_permissions.xml 用於根據應用簽名和應用軟體包名稱(後者可選)為應用分配
seinfo
標記。隨後,分配的seinfo
標記可在seapp_contexts
檔案中用作金鑰,以便為帶有該seinfo
標記的所有應用分配特定標籤。
B. 策略檔案te
以te結尾的檔案是SELinux中的策略檔案,它定義了作用域和標籤。
來看一個te檔案:
allow factory ttyMT_device:chr_file { read write open ioctl};
allow factory ttyGS_device:chr_file { read write open ioctl};
allow factory irtx_device:chr_file { read write ioctl open };
上面這幾行就是最基本的te語句了,相似的te語句的會被歸類在一個的te檔案下面。如上面的語句都是作用於factory
,則會在factory.te
檔案裡。external\sepolicy
中集中了很多系統定義的te檔案
例如第一條
allow factory ttyMT_device:chr_file { read write open ioctl};
翻譯一下就是
允許`factory`域裡的程序或服務
對型別為`ttyMT_device`的類別為檔案(`chr_file`)
執行`read`,`write`,`open`,`ioctl`許可權的操作
te檔案的基本語法:
# rule_name source_type target_type:class perm_set
- rule_name:規則名,分別有allow,dontaudit,auditallow,neverallow等
- source_type:源型別,主要作用是用來填寫一個域(domain)
- target_type:目標的型別
- class:類別,目標(客體)是哪種類別,主要有File,Dir,Socket,SEAndroid還有Binder等,在這些基礎上又細分出裝置字元型別(chr_file),連結檔案(lnk_file)等。可以通過ls -l檢視檔案型別
- perm_set:動作集
我們從上到下按順序介紹一下:
B1. rule_name
規則名稱 | 匹配是否允許 | 不匹配是否允許 | 匹配是否記錄 | 不匹配是否記錄 |
---|---|---|---|---|
allow | Yes | No | No | Yes |
dontaudit | No | No | No | No |
auditallow | No | No | Yes | Yes |
neverallow | - | - | - | - |
allow
:允許某個程序執行某個動作auditallow
:audit含義就是記錄某項操作。預設SELinux只記錄那些許可權檢查失敗的操作。 auditallow則使得許可權檢查成功的操作也被記錄。注意,allowaudit只是允許記錄,它和賦予許可權沒關係。賦予許可權必須且只能使用allow語句。dontaudit
:對那些許可權檢查失敗的操作不做記錄。neverallow
:沒有被allow
到的動作預設就不允許執行的。neverallow
只是顯式地寫出某個動作不被允許,如果添加了該動作的allow,則會編譯錯誤。
B2. source_type
指定一個“域”(domain
),一般用於描述程序
,該域內的的程序受該條TE語句的限制。用type
關鍵字,把一個自定義的域與原有的域相關聯
type shell, domain;
上面這句話的意思是,賦予shell
給domain
屬性,同時,shell
域屬於domain
這個集合裡。如果有一個allow domain xxxxx
的語句,同樣地也給了shell xxxxx
的屬性
B3. target_type
指定程序需要操作的客體(檔案,資料夾等)型別,同樣是用type
與一些已有的型別,屬性相關聯
以上面的ttyMT_device為例:
# 定義一個型別,屬於dev_type屬性
type ttyMT_device, dev_type;
屬性dev_type
在external/sepolicyattributes
的定義如下
attribute dev_type;
attribute
關鍵字定義一個屬性,type
可以與一個或多個屬性關聯,例如:
type usb_device, dev_type, mlstrustedobject;
另外,還有一個關鍵字typeattribute
,type有兩個作用:
- 定義(宣告)
- 關聯某個屬性。
可以把這兩個作用分開,type
定義,typeattribute
進行關聯
# 定義httpd_user_content_t,並關聯兩個屬性
type httpd_user_content_t, file_type, httpdcontent;
分成兩條語句進行表述:
#定義httpd_user_content_t
type httpd_user_content_t;
#關聯屬性
typeattribute httpd_user_content_t file_type, httpdcontent;
在external/sepolicy/attributes
裡定義了很多屬性,下面截取了一些常見的定義:
# All types used for devices.
attribute dev_type;
# All types used for processes.
attribute domain;
# All types used for filesystems.
attribute fs_type;
# All types used for files that can exist on a labeled fs.
# Do not use for pseudo file types.
attribute file_type;
# All types used for domain entry points.
attribute exec_type;
# All types used for property service
attribute property_type;
# All service_manager types created by system_server
attribute system_server_service;
# All domains that can override MLS restrictions.
# i.e. processes that can read up and write down.
attribute mlstrustedsubject;
# All types that can override MLS restrictions.
# i.e. files that can be read by lower and written by higher
attribute mlstrustedobject;
# All domains used for apps.
attribute appdomain;
# All domains used for apps with network access.
attribute netdomain;
# All domains used for binder service domains.
attribute binderservicedomain;
B4. class
客體的具體類別。用class
來定義一個客體類別,來看一下external/sepolicy/security_classes檔案
# file-related classes
class filesystem
class file #代表普通檔案
class dir #代表目錄
class fd #代表檔案描述符
class lnk_file #代表連結檔案
class chr_file #代表字元裝置檔案
......
# network-related classes
class socket #socket
class tcp_socket
class udp_socket
......
class binder #Android平臺特有的binder
class zygote #Android平臺特有的zygote
B5. perm_set
具體的操作,系統的定義在external/sepolicy/access_vectors中。有兩種定義方法。
1.用common
命令定義:
格式為:
common common_name { permission_name ... }
common
定義的perm_set
能被另外一種perm_set
命令class
所繼承,而class
定義的perm_set
則不能被繼承
如:
common file {
ioctl read write create getattr setattr lock relabelfrom relabelto
append unlink link rename execute swapon quotaon mounton
}
用class
命令定義:
# class class_name [ inherits common_name ] { permission_name ... }
例如class dir
繼承了file
class dir
inherits file
{
add_name
remove_name
reparent
search
rmdir
open
audit_access
execmod
}
最後是一些特殊的配置檔案:
- external/sepolicy/attributes -> 所有定義的attributes都在這個檔案
- external/sepolicy/access_vectors -> 對應了每一個class可以被允許執行的命令
- external/sepolicy/roles -> Android中只定義了一個role,名字就是r,將r和attribute domain關聯起來
- external/sepolicy/users -> 其實是將user與roles進行了關聯,設定了user的安全級別,s0為最低階是預設的級別,mls_systemHigh是最高的級別
- external/sepolicy/security_classes -> 指的是上文命令中的class,個人認為這個class的內容是指在android執行過程中,程式或者系統可能用到的操作的模組
- external/sepolicy/te_macros -> 系統定義的巨集在te_macros檔案
- external/sepolicy/***.te -> 一些配置的檔案,包含了各種執行的規則
2.幾個例子
# 將init關聯到domain,即將domain設定為init型別的屬性
type init, domain;
# 允許init型別對unlabeled型別的filesystem進行mount的操作
allow init unlabeled:filesystem mount;
# 允許init型別對fotad型別的unix_stream_socket 進行bind和create的操作
allow init fotad:unix_stream_socket { bind create };
# appdomain是定義在te_macros裡面的一個巨集,很多的app規則會使用類似app_domain(shell)的命令將其新增進去
# 允許app去對anr_data_file型別的目錄進行查詢的操作
allow appdomain anr_data_file:dir search;
# 允許app對anr_data_file型別的file進行開啟和新增操作
allow appdomain anr_data_file:file { open append };
#絕不允許app(除了有unconfineddomain屬性的app)對kmem_device型別的字元裝置進行讀寫的操作
neverallow { appdomain -unconfineddomain } kmem_device:chr_file { read write };
# 絕不允許除了unconfineddomain以外的app對self型別的capability2進行任何的操作
neverallow { appdomain -unconfineddomain } self:capability2 *;
# 宣告一個httpd_user_content_t的型別,具有file_type和httpdcontent的屬性
type httpd_user_content_t, file_type, httpdcontent;
# 宣告一個httpd_user_content_t的型別
type httpd_user_content_t;
# 定義httpd_user_content_t具有file_type, httpdcontent的屬性
typeattribute httpd_user_content_t file_type, httpdcontent;
# 允許所有具有app屬性的內容可以去對self屬性的rawip_socket進行create的操作
allow appdomain self:rawip_socket create_socket_perms;
# 允許user_t和domain屬性的類對bin_t, file_type, sbin_t型別的file進行可執行的操作
allow {user_t domain} {bin_t file_type sbin_t}:file execute ;
# 這兩條語句的表述其實是一致的,其實self指的是目標的型別和發起人的型別是一致的
allow user_t user_t:process signal;
allow user_t self:process signal;
# 允許user_t對bin_t型別的file進行除了write setattr ioctl相關的操作
allow user_t bin_t:file ~{ write setattr ioctl };
生成規則檔案的方法
下面介紹一下最簡單的安全策略(se-policy)新增方法,大家碰到SELinux導致的訪問禁止問題,可以參考用這種方法確認和解決。
1.安裝pc上的工具,用於自動生成安全策略
$ sudo apt-get install policycoreutils
2.刷userdebug
/eng
軟體,先將SELinux
設定成Permissive
模式,只輸出警告不阻止操作
使用getenforce命令檢視當前模式:
$ adb shell getenforce
Enforcing
在Enforcing模式下,除安全策略允許外的操作都會被阻止;使用setenforce命令更改當前模式(root許可權需要):
$ adb root
restarting adbd as root
$ adb shell setenforce 0
$ adb shell getenforce
Permissive
開發如果碰到懷疑是SELinux 可以通過這種方法關閉SELiunx( setenforce 0
),以確認是不是SELinux引起的
3.按照流程完成整個操作,抓取log,過濾出警告資訊
如果log較多,可以先用grep工具過濾一下:
$ grep "avc: *denied" log.txt > denied.txt
$ cat denied.txt
<14>[ 389.521062] avc: denied { set } for property=audio.ftm.rcv_reverse scontext=u:r:system_app:s0 tcontext=u:object_r:default_prop:s0 tclass=property_servic
4.使用pc工具audit2allow
生成安全策略
命令audit2allow
用來一次性生成所有安全策略,輸入為前面抓取的 log
$ audit2allow -i denied.txt
#============= system_app ==============
allow system_app default_prop:property_servic set;
使用audit2allow遇到的問題
本地執行audit2allow
時發現一個很奇怪的問題,執行audit2allow -i 123.txt
的時候首行log會失效,解析出來的結果是從第二行開始的,然後跟了下原始碼,發現呼叫到python2.7/dist-packages/sepolgen/audit.py
的__parse_line
方法時因捕獲到ValueError
異常unable to open (null): Bad address
,因而msg
被設定為DaemonStartMessage
型別,導致未能完成之後的解析。
可是奇怪的是哪怕第二行和第一行寫的一模一樣,audit2allow
卻不會報錯。
$ cat 123.txt
<14>[ 389.521062] avc: denied { set } for property=audio.ftm.rcv_reverse scontext=u:r:system_app:s0 tcontext=u:object_r:default_prop:s0 tclass=property_servic
$ audit2allow -i 123.txt
木有輸出。。。
$ cat 456.txt
<14>[ 389.521062] avc: denied { set } for property=audio.ftm.rcv_reverse scontext=u:r:system_app:s0 tcontext=u:object_r:default_prop:s0 tclass=property_servic
<14>[ 389.521062] avc: denied { set } for property=audio.ftm.rcv_reverse scontext=u:r:system_app:s0 tcontext=u:object_r:default_prop:s0 tclass=property_servic
$ audit2allow -i 456.txt
#============= system_app ==============
allow system_app default_prop:property_servic set;
這次正常輸出了。。。
python2.7/dist-packages/sepolgen/audit.py
def __parse_line(self, line):
rec = line.split()
for i in rec:
found = False
if i == "avc:" or i == "message=avc:" or i == "msg='avc:":
msg = AVCMessage(line)
found = True
elif i == "security_compute_sid:":
msg = ComputeSidMessage(line)
found = True
elif i == "type=MAC_POLICY_LOAD" or i == "type=1403":
msg = PolicyLoadMessage(line)
found = True
elif i == "type=AVC_PATH":
msg = PathMessage(line)
found = True
elif i == "type=DAEMON_START":
msg = DaemonStartMessage(list)
found = True
if found:
self.check_input_file = True
try:
msg.from_split_string(rec)
except ValueError:
msg = InvalidMessage(line)
return msg
return None
因為未跟蹤到具體raise
資訊為unable to open (null): Bad address
的ValueError
位置,未能繼續跟進,所以在捕獲到異常時又重新呼叫了一次msg.from_split_string
commit 227845eb421a425568a3bc2a0e92e8ad20a720b3
Author: liuxiuquan <[email protected].com>
Date: Tue Dec 11 16:38:49 2018 +0800
[liuxq]add for audit2allow parse first line error
diff --git a/audit.py b/audit.py
index 56919be..53cd61c 100644
--- a/audit.py
+++ b/audit.py
@@ -370,11 +370,17 @@ class AuditParser:
# and valid audit message.
def __parse_line(self, line):
rec = line.split()
+
+ # 20181211 liuxq add for audit2allow parse first line error begin
+ isAvc = False
+ # 20181211 liuxq add for audit2allow parse first line error end
+
for i in rec:
found = False
if i == "avc:" or i == "message=avc:" or i == "msg='avc:":
msg = AVCMessage(line)
found = True
+ isAvc = True
elif i == "security_compute_sid:":
msg = ComputeSidMessage(line)
found = True
@@ -387,14 +393,33 @@ class AuditParser:
elif i == "type=DAEMON_START":
msg = DaemonStartMessage(list)
found = True
-
+
+
+ # 20181211 liuxq add for audit2allow parse first line error begin
+ # if found:
+ # self.check_input_file = True
+ # try:
+ # msg.from_split_string(rec)
+ # except ValueError:
+ # msg = InvalidMessage(line)
+ # return msg
+
if found:
self.check_input_file = True
try:
msg.from_split_string(rec)
- except ValueError:
+ except ValueError,err: #liuxq add
msg = InvalidMessage(line)
+ #print "liuxq first get ValueError",err.message
+ if isAvc and 'Bad' in err.message:
+ msg = AVCMessage(line)
+ try:
+ msg.from_split_string(rec)
+ except ValueError,err:
+ #print "liuxq second get ValueError",err.message
+ msg = InvalidMessage(line)
return msg
+ # 20181211 liuxq add for audit2allow parse first line error end
return None
# Higher-level parse function - take a line, parse it into an
可以看到修改audit.py
後可以正常解析第一行log了
$ audit2allow -i 123.txt
#============= system_app ==============
allow system_app default_prop:property_servic set;
本地環境:
Ubuntu 14.04.5 LTS
Python 2.7.6
參考文章:
https://source.android.com/security/selinux/implement
https://blog.csdn.net/chaoy1116/article/details/45063629
https://blog.csdn.net/chaoy1116/article/details/45063629
https://blog.csdn.net/jaczen/article/details/73028302