韌館-LearnHouse

[轉]Android SELinux詳解

資料來源:http://www.vaststargames.com/read.php?tid=168

此篇寫得很完整且易懂,為了預防之後失聯,因此在這裡備份

一、SELinux是什麼?
    SELinux是一套完整的安全策略,最開始是美國國家安全域和一些公司聯合設計為了針對Linux系統的安全隱患而產生的一套系統,它為每一個處理程序,每一個檔案,每一個屬性都定義了標籤,用來控制處理程序對檔案的操作的權限控制!
在Android裡面,SELinux有三種狀態:

enforce模式:強制模式,必須有組態權限才能訪問/操作
permissive模式:寬容模式,列印記錄出現的越權行為,但是不禁止該訪問/操作
disable:關閉模式,關閉SELinux,可以自由訪問

SELinux默認是打開的。我們可以在窗口命令列下輸入命令getenforce來查看我們的裝置的SELinux狀態。我們可以在命令列下輸入命令:setenforce 0暫時性的將SELinux關閉,如果需要永久關閉,需要我們編譯軟體之前,在uboot和kernel程式碼中將androidboot.selinux屬性設定為disable。

二、安全上下文標籤
    上面有提到SELinux系統中,每一個對象(處理程序和檔案或者裝置)都有自己的安全標籤。在序列埠下,通過ps -Z命令可以查看處理程序的安全上下文標籤,可以通過ls -Z查看檔案的安全上下文標籤,例如:

  1. LABEL                          USER     PID   PPID  NAME
  2. u:r:init:s0                    root 1 0 /init
  3. u:r:kernel:s0                  root 2 0 kthreadd
  4. ...
  5. u:r:kernel:s0                  root 258 2 irq/322-HPH_R O
  6. u:r:logd:s0                    logd 259 1 /system/bin/logd
  7. u:r:healthd:s0                 root 260 1 /sbin/healthd
  8. u:r:lmkd:s0                    root 261 1 /system/bin/lmkd
  9. u:r:servicemanager:s0          system 262 1 /system/bin/servicemanager
  10. u:r:vold:s0                    root 263 1 /system/bin/vold
  11. u:r:surfaceflinger:s0          system 264 1 /system/bin/surfaceflinger

看到一個完整的安全上下文它由四部分組成:u,r,init,s0
u:表示使用者,Android系統的SELinux中之定義了一個使用者,所有的處理程序都屬於同一個安全使用者,叫做:u
r:表示角色,表示這是一個處理程序的安全上下文,和檔案的安全上下文(object_r),屬性上下文和其他上下文彼此區分
init:這個欄位對於處理程序或者服務來說,是表示處理程序運行的域,也叫:domain(後面組態規則的時候會遇到),而對於檔案或者其他對象來說,可以理解為類型type。根據不同的對象(有的是檔案,有的是裝置,有的是處理程序),Android定義了100多個不同的關鍵字來區別這些處理程序的域和檔案和裝置的類型。
s0:安全等級,第四列專為多級安全功能(擴展 MLS)而設計,MLS 是一種訪問機制,可增加安全上下文和格式敏感度 [: 類別列表] [-敏感度 [: 類別列表]],例如 s0 - s15: c0 - c1023,而在當前Android 版本中不需要類別。
在Android系統中,我們只需要重點關注第二和第三個欄位,就是角色和域/類型。

三、如何組態SELinux的策略
    SEAndroid 策略源位於 SDK system/sepolicy 目錄。該策略包括用於生成 SELinux 核心策略檔案的原始檔:file_contexts 組態、property_contexts 組態、seapp_contexts 組態和 mac_permissions.xml 組態。

* file_contexts 組態用於在建構(例如,系統分區)和執行階段(例如,裝置節點、服務套接字檔案
和由 init.rc 建立的/資料目錄等)標記檔案。
* property_contexts 被用於指定 Android 屬性的安全上下文,供查看權限。
* seapp_contexts 組態被用於標記應用處理程序和應用程式包目錄。
* mac_permissions.xml 組態是中介軟體 MAC 策略

與裝置相關的策略檔案位於 device/xxxxxxx/common/sepolicy 目錄中。

在組態規則之前,我們首先要知道什麼是規則?

規則就是針對domain對象的,上面我們說過,每一個處理程序都屬於一個domain(域),規則就是設定哪個domain(域)的對象(就是處理程序)能夠對哪些type(類型)的目標對象(檔案或者屬性或者裝置)具有哪有操作(刪除啊,訪問啊等等),這就是規則!

組態規則我們一般寫在device/xxxxxxx/common/sepolicy目錄下的te檔案中。可以看到這個目錄下有很多的te檔案。
那麼具體應該如何來編寫呢?這裡我們舉一個例子來說明:假如我們要在init.rc檔案中啟動一個service,他的功能是在開機的時候安裝一些APP,我們已經編譯好了這個service的可執行檔案:preinstall。
1.首先剛剛上面講了,每一個對象不管是檔案還是屬性還是裝置,在SELinux下都有一個安全上下文,我們首先要定義我們的可執行檔案preinstall的安全上下文,這個上面有介紹是在device/xxxxxxx/common/sepolicy/file_contexts檔案中設定的:按照上面介紹的安全上下文格式,4個部分,我們定義為:

/system/bin/preinstall             u:object_r:preinstall_exec:s0

可以看到,我們給preinstall的類型取名叫:preinstall_exec。為啥叫這個名字?我們稍後介紹!
上面說了規則的主體是domain,那麼這裡,我們的domain是什麼呢?我們的preinstall是寫在init.rc中,是fork自init處理程序的,init處理程序的domain系統已經組態了,它的domain就叫做“init”。在SELinux的系統中,子處理程序fork父處理程序,他們的domain也是默認繼承自父處理程序,所以我們的preinstall所在的子處理程序的域默認也是“init”。那我們是不是可以開始針對“init”這個domain開始編寫規則了呢?答案是不能!因為init處理程序域是系統的,負責很多基礎的工作,而我們的preinstall處理程序和init處理程序沒有什麼關係和聯絡,是彼此獨立的兩個功能,在SELinux的原則上,應該將preinstall處理程序域從init處理程序域中單獨出來,僅針對preinstall子處理程序域組態該有的權限,而不是直接在init處理程序域的基礎上新增缺少的權限,這樣也是為了符合SELinux的規範!那麼問題來了,怎麼建立preinstall自己的的domain呢?

我們在device/xxxxxxx/common/sepolicy/目錄下新建一個檔案,取名就叫preinstall.te:

//這句話是聲明一個新的domain,名字叫:preinstall
type preinstall  domain


//這句話是聲明一個新的type,名字叫:preinstall_exec,我們之前在file_contexts中引用了這個type的。
//後面的exec_type就是說把我們新的preinstall_exec和系統定義好的exec_type關聯起來,可以類比於java的繼承,子類繼承父類的屬性的概念。因為我們的preinstall是個可執行檔案,所以我們繼承了系統的exec_type,而不是device_type很好理解吧,也可以同時關聯多個type,例如:type preinstall_exec exec_type vendor_type,就是說我的preinstall_type同時和exec_type和vendor_type具有相同的屬性。
type preinstall_exec exec_type


//這下面這句話的方法實現在原始碼中的te_macros檔案中,意思我大概翻譯一下就是:
當init處理程序中執行了“preinstall_exec” type的檔案時,將新的子處理程序的domain從默認的init轉換到preinstall這個domain
init_daemon_trans(preinstall)

這樣寫完了之後,我們就了自己的domain,可以開始給自己的domain主體設定權限了,安裝APP,我們需要從/data目錄下讀取檔案,那麼這個data目錄的存取權如何寫呢?
首先我們可以在命令列下通過ls -Z查看一下data目錄的安全上下文:

ls -Z
data         u:object_r:vendor_file:s0

其中的vendor_file就是我們的type了。寫權限命令之前,我們把標準公式拿出來:

[section_rule] [section_domain] [section_target] [section_action]

section_rule:規則名稱,常用的有:allow允許,dontaudit不記錄,neverallow不允許。
section_domain:domain域,對哪一個domain進行操作,就寫哪個名字。
section_target:操作的對象type資訊,有安全上下文的中type,後面加冒號,加一個class組成。class有file,dir,fd等等,具體可以在security_classes檔案中可以查看。
section_action:具體的動作,讀read,寫write,建立create等,具體可以在access_vectors檔案中查看!

那麼很簡單了,我們就能寫出我們的完整的te檔案了:

type preinstall  domain
type preinstall_exec exec_type
init_daemon_trans(preinstall)
allow preinstall vendor_file:file read

還有一些我們沒想到的權限,就需要抓取列印dmesg | grep avc查看違反了那些規則:可以使用 linux 工具 allow2audit 可以將違反規則的 avc 記錄生成放行規則 (適合於dmesg 所有輸出,初期的開發階段)。也可以手動來寫規則,例如列印如下:

type=1400audit(1386760471.880:7):avc: denied {entrypoint}for pid=1227comm=``init''path=``/
sbin/healthd''dev=``rootfs''ino=4396scontext=u:r:healthd:s0tcontext=u:object_r:rootfs:s0tclass=file

我們從scontext中獲取到domain:healthd,從tcontext中獲取到type:rootfs,tclass:file
所以我們可以寫出rule如下:

allow healthd rootfs:file entrypoint;

將以上rule語句寫進名字為“healthd.te”的檔案中,重新編譯即可!然後重新測試,在抓取列印看看是否還有缺失的列印,反覆幾次就OK了!

四、App應用的安全上下文標籤
    上面介紹了我們的二進制可執行檔案一般都是通過domain遷移來確定自己的domain,而APP應用的安全上斜紋標籤卻不一樣,這裡我們單獨介紹。
seinfo
我們都知道APP都會被簽名,一般有這四種簽名:platform,media,test,shared。而SELinux系統就是根據APP被簽名的不同,定義了不同的seinfo(安全資訊)概念。在system/sepolicy/private/mac_permissions.xml檔案中定義了根據不同的簽名來確定不同的seinfo:

   <!-- Platform dev key in AOSP -->
    <signer signature="@PLATFORM" >
      <seinfo value="platform" />
    </signer>

    <!-- Media key in AOSP -->
    <signer signature="@MEDIA" >
      <seinfo value="media" />
    </signer>

    <signer signature="@NETWORK_STACK" >
      <seinfo value="network_stack" />
    </signer>

可以看到默認這裡之定義了三個seinfo,如果我們APP的簽名不在這幾個裡面,那麼seinfo會取值為“default”。確定了seinfo後,再根據system/sepolicy/private/seapp_contexts檔案確認doamain和type:

neverallow isEphemeralApp=true domain=((?!ephemeral_app).)*

isSystemServer=true domain=system_server_startup

user=_app seinfo=platform name=com.android.traceur domain=traceur_app type=app_data_file levelFrom=all
user=system seinfo=platform domain=system_app type=system_app_data_file
user=bluetooth seinfo=platform domain=bluetooth type=bluetooth_data_file
user=network_stack seinfo=network_stack domain=network_stack levelFrom=all type=radio_data_file
user=nfc seinfo=platform domain=nfc type=nfc_data_file
user=secure_element seinfo=platform domain=secure_element levelFrom=all
user=radio seinfo=platform domain=radio type=radio_data_file
user=shared_relro domain=shared_relro
user=shell seinfo=platform domain=shell name=com.android.shell type=shell_data_file
user=webview_zygote seinfo=webview_zygote domain=webview_zygote
user=_isolated domain=isolated_app levelFrom=all
user=_app seinfo=app_zygote domain=app_zygote levelFrom=all
user=_app seinfo=media domain=mediaprovider name=android.process.media type=app_data_file levelFrom=user
user=_app seinfo=platform domain=platform_app type=app_data_file levelFrom=user
user=_app isEphemeralApp=true domain=ephemeral_app type=app_data_file levelFrom=all
user=_app isPrivApp=true domain=priv_app type=privapp_data_file levelFrom=user
user=_app minTargetSdkVersion=29 domain=untrusted_app type=app_data_file levelFrom=all
user=_app minTargetSdkVersion=28 domain=untrusted_app_27 type=app_data_file levelFrom=all
user=_app minTargetSdkVersion=26 domain=untrusted_app_27 type=app_data_file levelFrom=user
user=_app domain=untrusted_app_25 type=app_data_file levelFrom=user
user=_app minTargetSdkVersion=28 fromRunAs=true domain=runas_app levelFrom=all
user=_app fromRunAs=true domain=runas_app levelFrom=user

以上文中紅色字這行為例:當user是system時,seinfo是platform,那麼它的domain就是system_app,它的檔案type就是system_app_data_file。
這裡的user是指的APP的uid,系統會為每一個應用分配一個uid,定義在system/core/include/private/android_filesystem_config.h檔案中,android系統默認保留了1000-9999之內的uid,例如1000就是表示system:

#define AID_ROOT 0 /* traditional unix root user */
/* The following are for LTP and should only be used for testing */
#define AID_DAEMON 1 /* traditional unix daemon owner */
#define AID_BIN 2    /* traditional unix binaries owner */

#define AID_SYSTEM 1000 /* system server */

#define AID_RADIO 1001           /* telephony subsystem, RIL */
#define AID_BLUETOOTH 1002       /* bluetooth subsystem */
#define AID_GRAPHICS 1003        /* graphics devices */
#define AID_INPUT 1004           /* input devices */
#define AID_AUDIO 1005           /* audio devices */
#define AID_CAMERA 1006          /* camera devices */

那如果我們的APP是第三方APP,安裝後他的user一般是這種形式的:u0_xxx,那麼這些APP在seapp_contexts檔案中怎麼表示呢?Android系統對於所有的不是系統預定義的user的其他,全部表示為:"_app",通過上文中的藍色字型可以看到,這些APP基本上被認定為domain:untrusted_app, untrusted_app_27, untrusted_app_25 。

再從上述綠色的行可以看到,還可以用APP的處理程序name來匹配。
如果我們需要針對我們的某一個預裝在system目錄下使用platform簽名的APP單獨一個安全上下文,我們只需要在我們自己的SELinux目錄device/xxxxxxx/common/sepolicy/private目錄新增seapp_contexts檔案,新增一條:

user=system seinfo=platform domain=mytest_app name=com.mytest.app type=mytest_app_data_file

前提是mytest_app這個domain和mytest_app_data_file這個type也要手動的定義。
有了單獨的domain,我們可以針對這個APP單獨組態權限了。

2023年5 月 posted by admin in 文獻參考 and have No Comments

Place your comment

Please fill your data and comment below.
名稱:
信箱:
網站:
您的評論: