Android sepolicy簡要記

SEAndroid是一種基於安全策略的MAC安全機制。SEAndroid安全機制中的安全策略就是在安全上下文的基礎上進行描述的,也就是說,它通過主體和客體的安全上下文,定義主體是否有權限訪問客體。

安全上下文

安全上下文: 附加在對象上的標籤(label)

由四部分內容組成:SELinux用戶、SELinux角色、類型、安全級別 
格式爲“user:role:type:sensitivity”

文件init.rc的安全上下文類似如下:( $ls -Z /init.rc)

 -rwxr-x--- root     root    u:object_r:rootfs:s0 init.rc

進程init的安全上下文類似如下:($ps -Z)

LABEL                          USER     PID  PPID  NAME

u:r:init:s0                    root         1       0        /init

進程的安全上下文中,用戶固定爲 u,角色固定爲 r

文件的安全上下文中,用戶固定爲 u,角色固定爲 obejct_r

在SELinux中,安全級別是可選的,也就是說,可以選擇啓用或者不啓用。通常在進程及文件的安全上下文中安全級別都設置爲s0

進程的安全上下文的類型稱爲domain,文件的安全上下文中的類型稱爲file_type

上面我們看到進程init的安全上下文爲u:r:init:s0,type爲init
在Android中我們可以通過如下語句定義type:

type init domain;

即將domain設置爲init的屬性,這樣就可以用init爲type來描述進程的安全上下文了。

Seapp_contexts

用於聲明APP進程和創建數據目錄的安全上下文,O上將該文件拆分爲plat和nonplat 前綴的兩個文件,plat前綴的文件用於聲明system app,nonplat前綴的文件用於聲明vendor app。

# Ephemeral Apps must run in the ephemeral_app domain
neverallow isEphemeralApp=true domain=((?!ephemeral_app).)*

isSystemServer=true domain=system_server
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=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=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 isV2App=true isEphemeralApp=true domain=ephemeral_app type=app_data_file levelFrom=all
user=_app isPrivApp=true domain=priv_app type=app_data_file levelFrom=user
user=_app minTargetSdkVersion=28 domain=untrusted_app 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

seinfo可以從mac_permissions.xml查看
對於使用平臺簽名的App來說,它的seinfo爲“platform”。這樣我們就可以知道,使用平臺簽名的App所運行在的進程domain爲“platform_app”,並且它的數據文件的file_type爲“platform_app_data_file”。

File_contexts

用於聲明文件的安全上下文,plat前綴的文件用於聲明system、rootfs、data等與設備無關的文件。Nonplat 用於聲明vendor、data/vendor 等文件。

Service_contexts

用於聲明java service 的安全上下文, O上將該文件拆分爲plat和nonplat 前綴的兩個文件,但nonplat前綴的文件並沒有具體的內容(vendor和system java service不允許binder 操作)。

Property_contexts

用於聲明屬性的安全上下文,plat 前綴的文件用於聲明system屬性,nonplat前綴的文件用於聲明vendor 屬性。ril.開頭的屬性的安全上下文爲u:object_r:radio_prop:s0,這意味着只有有權限訪問Type爲radio_prop的資源的進程纔可以訪問這些屬性。

Hwservice_contexts

O 上新增文件,用於聲明HIDL service 安全上下文。

android.hardware.vibrator::IVibrator                u:object_r:hal_vibrator_hwservice:s0
android.hardware.vr::IVr                            u:object_r:hal_vr_hwservice:s0
android.hardware.weaver::IWeaver                    u:object_r:hal_weaver_hwservice:s0
android.hardware.wifi::IWifi                        u:object_r:hal_wifi_hwservice:s0
android.hardware.wifi.hostapd::IHostapd             u:object_r:hal_wifi_hostapd_hwservice:s0
android.hardware.wifi.offload::IOffload             u:object_r:hal_wifi_offload_hwservice:s0
android.hidl.allocator::IAllocator                  u:object_r:hidl_allocator_hwservice:s0
android.hidl.base::IBase                            u:object_r:hidl_base_hwservice:s0
android.hidl.manager::IServiceManager               u:object_r:hidl_manager_hwservice:s0
android.hidl.memory::IMapper                        u:object_r:hidl_memory_hwservice:s0
android.hidl.token::ITokenManager                   u:object_r:hidl_token_hwservice:s0
android.system.net.netd::INetd                      u:object_r:system_net_netd_hwservice:s0

安全策略

SEAndroid安全機制又稱爲是基於TE(Type Enforcement)策略的安全機制。所有安全策略都存放在.te結尾的文件中,一般放在 /system/sepolicy/private/,廠商定製的一般放在/device/xxx/common/sepolicy/下

一個Type所具有的權限是通過allow語句來描述的,SEAndroid使用的是最小權限原則,也就是說,只有通過allow語句聲明的權限纔是允許的,而其它沒有通過allow語句聲明的權限都是禁止,這樣就可以最大限度地保護系統中的資源。

語句格式爲:allow scontex tcontex:class action

除了allow還有:neverallow (檢查並記錄是否有違反這條策略的)、allowaudit(檢查記錄權限成功及失敗的操作,默認只記錄檢查失敗的)、dontaudit(不記錄權限檢查失敗的記錄),後三個僅是檢查及記錄,並不賦予或禁止權限。

常見錯誤修改

出現違反SELinux安全策略的錯誤時一般會有如下log輸出:

avc:denied { write } for pid=2646 comm="gfslog" name="/" dev="mmcblk0p21" ino=2 scontext=u:r:gfslog:s0 tcontext=u:object_r:system_data_file:s0 tclass=dir permissive=0

一般按照規則 allow scontex tcontex:tclass action 來改即可。即:

allow gfslog system_data_file:dir write

違反規則的同時又neverallow問題修改

Google 在external/sepolicy 中有使用相關neverallow 規則, 對SELinux Policy 的更新進行了限制, 以防止開發者過度開放權限,從而引發安全問題。並且通過CTS Test 檢測開發者是否有違法相關的規則.

嚴格遵守Google 定義的規則,具體即是
* 嚴禁直接修改external/sepolicy 下面的相關SELinux Policy, 使之保持與Google 一致, 特別是嚴禁直接刪除或者修改Google 定義的neverallow 規則.
* 遇到與Google 定義相違背之處, 只能繞道, 或者修改設計.

方式一:更改type

比如recovery升級中,有如下錯誤:

avc: denied { read } for name="mmcblk0p15" dev="tmpfs" ino=3364 scontext=u:r:install_recovery:s0 tcontext=u:object_r:block_device:s0 tclass=blk_file permissive=0  

但是domain.te文件中,明確domain中是沒有對block_device的讀寫權限,除了recovery等。

neverallow { domain -kernel -init -recovery } block_device:blk_file { open read write };

方法一:將 install_recovery 添加到上述neverallow的domain例外中,但後續可能會有cts問題

方法二:將要操作的文件定義爲其他type,然後允許install_recovery來讀寫這個新type的文件

方式二 通過binder/socket 等方式連接APP 訪問

如: Android L APP 如何獲取sys 或者 proc file system 中節點的寫權限
說明: Google 默認禁止app , 包括system app, radio app 等直接寫/sys 目錄以u:object_r:sysfs:s0 爲標籤的文件以及/proc 目錄以u:object_r:proc:s0文件, 認爲這個是有安全風險的。如果直接放開SELinux 權限, 會導致CTS 無法通過.

解決方式:
通常遇到此類情況,你有兩種做法:
(1). 通過system server service 或者 init 啓動的service 讀寫操作, 然後app 通過binder/socket 等方式連接APP 訪問. 此類安全可靠, 並且可以在service 中做相關的安全審查, 推崇這種方法.

方式三 更改Label

(2). 修改對應節點的SELinux Security Label, 爲特定的APP, 如system app, radio, bluetooth 等內置APP開啓權限, 但嚴禁爲untrsted app 開啓權限. 具體的做法下面以 system app 控制/sys/class/leds/lcd-backlight/brightness 來說明.
在device/mediatek/common/sepolicy/file.te 定義brightness SELinux type

type sys_lcd_brightness_file, fs_type,sysfs_type;

在device/mediatek/common/sepolicy/file_contexts 綁定 brightness 對應的label, 注意對應的節點是實際節點,而不是鏈接.

/sys/devices/platform/leds-mt65xx/leds/lcd-backlight/brightness u:object_r:sys_lcd_brightness_file:s0

在device/mediatek/common/sepolicy/system_app.te 中申請權限.

allow system_app sys_lcd_brightness_file:file rw_file_perms;

爲其它的process 申請相關的權限,如system_server, 在device/mediatek/common/sepolicy/system_server.te
allow system_server sys_lcd_brightness_file:file rw_file_perms;

原則上我們都推崇使用第一種處理.

Process 無法訪問某個新增device

說明: Google 默認禁止除unconfineddomain 的進程以及ueventd 之外的進程直接訪問某個普通定義的device, 所謂普通定義即device 對應的SELinux Label 是u:object_r:device:s0.
解決方式: 需要爲新增的device 定義具體的Label, 然後再給對應的process 開啓相關的權限.

Native Process 運行java 程序

說明: 以往我們在上下層通訊中, 比如某個native process/service 通常會借用如am 命令, 向ActivityManagerService 發送broadcast, 或者start activity 等. 通知上層某個事件已經發生了. 在android 5.0 以後, Google 嚴禁非app 以及幾個特別的domain 的程序執行非rootfs or /system 分區的文件, 具體定義如:
neverallow { domain -appdomain -dumpstate -shell -su -system_server -zygote } { file_type -system_file -exec_type }:file execute;

解決方式:

(第一種方法).借用am 命令,實質上還是通過獲取AMS 的binder 引用,再通過binder 向AMS 發送命令, 同樣的你可以直接binder 來操作, 代表案例有: mediaserver 中使用的/frameworks/av/media/libmediaplayerservice/ActivityManager.cpp.
先獲取servicemanager, 然後再獲取activty binder 接口, 然後再封裝parcel 後發送相關的命令給AMS.

具體對應的function cmd 以及parcel 的封裝格式可以參考: frameworks/base/core/java/android/app/ActivityManagerNative.java.

注意:如果這個process 沒有申請binder 權限, 則需要先申請binder 權限, 可以使用 binder_use, binder_call, binder_service
(第二種方法). 將這個process 納入appdomain, 但這個帶來的影響是, 這個process 的權限會受到更大的限制, 因爲Google 對appdomain 有非常嚴格的限制. 比如某個process 是demo
device/mediatek/common/sepolicy/demo.te 裏面新增
app_domain(demo)

system app 讀寫nvram 操作

說明: 以往我們可以通過jni 直接讀寫nvram, 目前Google 已經嚴禁app 去讀寫系統塊設備文件.
解決方式: 已經做好了nvram_agent_binder 這樣一個服務進程,使用時就是去訪問它這個service 即可

UEventObserver 在APP 中使用

說明:
(1). Google 禁止UEventObserver API在第三方APP 使用,即任何不可信任的APP 都禁止使用此API.
(2). 如果是系統APP ,比如phone app, settings 等使用,那麼就要根據app 使用的domain, 添加相關的SELinux 權限.
解決方式: 確保你的APP 是系統的APP, 非untrusted app.
如針對settings app, 在device/mediatek/common/sepolicy/system_app.te 裏面新增
allow system_app self:netlink_kobject_uevent_socket { create ioctl read getattr setattr bind connect getopt setopt shutdown };

如何開機設置SELinux 模式

1 更新配置
bootable/bootloader/lk/platform/mt6xxx/rules.mk

# choose one of following value -> 1: disabled/ 2: permissive /3: enforcing
SELINUX_STATUS := 3

可直接調整這個SELINUX_STATUS這個的值爲2或者1

2 允許USER 版本 disable SELinux (如果你需要在USER Build 上開啓)
修改system/core/init/Android.mk 新增

ifeq ($(strip $(TARGET_BUILD_VARIANT)),user)
LOCAL_CFLAGS += -DALLOW_DISABLE_SELINUX=1
Endif

SELinux Update in O overview

Google android O 8.0 開始, 對android 架構進行大幅度的更新, 一個最爲重要的方面是切割Google/SoC Vendor/ODM 三者的複雜關聯性, 讓android 單獨升級成爲可能.

爲了防止System.img / Vendor.img 中數據, 服務, 應用等相互的依賴, 調用, 引用. Google 大幅度的更新了selinux policy, 進行嚴格的限制.

這裏寫圖片描述

比如嚴格限制了system image 和 vendor image 之間的socket, binder 通訊. 只能通過HIDL, HwBinder 進行交互.

sepolicy 分離

伴隨着system image 和 vendor image 的分離, 自然也會伴隨着sepolicy 的分離, 從以往集中放在bootimage , 到分離存放到system image 以及 vendor image. 原則上與system 相關的sepolicy 就存放system image, 與SoC vendor 相關的sepolicy 就存放在vendor image.

對應sepolicy, Google 也設定了不同的存放目錄, 以便進行分離, 以Google 默認的sepolicy 爲例. /system/sepolicy

public: android 和 vendor 共享的sepolicy 定義, 通常情況下, 意味着vendor 開發者可能爲此新增一些權限. 一般system/vendor 共用的一些類型和屬性的定義, neverallow 限制等會存放於此.

private: 通常意義上的僅限於system image 內部的使用, 不對vendor 開放. 這個只會編譯到system image 中.

vendor: 它僅僅能引用public 目錄下的相關定義, 這個只會編譯到vendor image 中. 但它依舊可以對system image 裏面的module 設定sepolicy(對應module 需要在public 下進行聲明); 在很大程度上繞過了Google GTS 約束測試.

mapping: 爲兼容老版本的sepolicy 而導入, 只有在system image version > vendor version 的時候, 纔可能被用到. 即包括兩方面, 新版本增加的type , 新版本移除的type, 以及老版本public, 新版本private 等變化的設定, 以兼容老版本.

針對性的, MTK 這邊也有類似的操作:

/device/mediatek/sepolicy/basic|bsp|full/

non_plat: 僅用於vendor 目錄的sepolicy 設定. 和Google vendor 目錄類似.
plat_private: 僅用於system 目錄的sepolicy 設定, 和Google 的private 目錄類似.
plat_public: 同時可用於vendor/system 的sepolicy 設定, 和Google 的public 目錄類似.

針對MTK 默認的設定, 可以參考:

/device/mediatek/common/BoardConfig.mk

對於手機來說生成的selinux policy 位於:

System android sepolicy: system/etc/selinux

Vendor ODM sepolicy: vendor/etc/selinux

再說強制性約束(neverallow)

Google 新增的強制性約束主要是針對system/vendor 之間的IPC 約束.
這裏寫圖片描述

具體的定義可以參考:

/system/sepolicy/public/domain.te

新增重要類型/屬性

這裏寫圖片描述

這裏寫圖片描述

新增場景語法HAL/Binder與設計

1.1 How to define sepolicy for HAL process
HAL 的sepolicy 可以分成兩種, 一種是HAL service 默認已經定義好的, 另外一種是完全新增的HAL service. 我們這裏只說後面一種, 前面就是後面的簡化版本.

Google 使用屬性來定義client/server, 以便不同家vendor 能夠綁定屬性來達成.

(0). Define HAL service attribute, xxxx 爲HAL service

/device/mediatek/sepolicy/bsp/non_plat

attribute hal_xxxx;

expandattribute hal_xxxx true;

attribute hal_xxxx_client;

expandattribute hal_xxxx_client true;

attribute hal_xxxx_server;

expandattribute hal_xxxx_server false;

(1). Define new hwservice type yyy 是某家公司的代號, 如mtk

/device/mediatek/sepolicy/bsp/non_plat/hwservice.te

type yyy_hal_xxxx_hwservice, hwservice_manager_type;

(2). Associate HAL interface with new HWService type, yyy 爲某家公司代號, zzz 爲具體的接口.

/device/mediatek/sepolicy/bsp/non_plat/hwservice_contexts

vendor.yyy.hardware.xxxx::zzz u:object_r: yyy_hal_xxxx_hwservice:s0;

(3). Creat HAL TE file

/device/mediatek/sepolicy/bsp/non_plat/hal_xxxx.te

type yyy_hal_xxxx, domain;

hal_server_domain(yyy_hal_xxx, hal_xxxx)

type yyy_hal_xxxx_exec, exec_type, file_type, vendor_file_type;

init_daemon_domain(yyy_hal_xxxx)

(4). Add this new HWService for server and client

/device/mediatek/sepolicy/bsp/non_plat/hal_xxxx.te

add_hwservice(hal_xxxx_server, yyy_hal_xxxx_hwservice)

allow hal_xxxx_client yyy_hal_xxxx_hwservice :hwservice_manager find;

#hwbinder IPC from clients into server, and callbacks

binder_call(hal_xxxx_client, hal_xxxx_server)

binder_call(hal_xxxx_server, hal_xxxx_client)

(6). Add other rules for server or client.

1.2 Binder/HWBinder/VndBinder
O 版本後新增了HWBinder, VndBinder, 即Binder 限於非vendor partition 使用, HWBinder 限於vendor / system 跨分區使用, Vndbinder 則限於非system partition 使用.

(1). Binder

binder_use(domain) // Allow domain to use Binder IPC

binder_service(domain) // Mark a domain as being a Binder service domain

binder_call(clientdomain, servicedomain) // Allow clientdomain to perform binder IPC to servicedomain (include: remote call service’s method, transfer binder object/FD to service)

  • if your domain is a service domain, please add ” binder_service(your_domain) ” to your te file;
  • if your domain is a client domain, please add ” binder_call(your_domain, servicedomain)” to your te file;
  • if you getService/addService/findService/listService from servicemanager, please add ” binder_use(your_domain) ” to your te file;

(2). HWBinder

Allow domain to use HWBinder IPC
hwbinder_use(domain)

Allow a set of permissions required for a domain to be a server which provides a HAL implementation over HWBinder.
hal_server_domain(domain, hal_type)

Allow a set of permissions required for a domain to be a client of a HAL of a specific type.
HAL_client_domain(domain, hal_type)

Allow clientdomain to perform hwbinder IPC to serverdomain, and callbacks
binder_call(hal_type_client, hal_type_server)

binder_call(hal_type_server, hal_type_client) // for callbacks.

(3). vndbinder

Allow domain to use vendor binder IPC (for vendor partition.)

 vndbinder_use(domain)

(4). combine above two in a macro, add_{hw}service().

add_service(domain_name, service_name) /* add/find permission rule to servicemanager */

add_hwservice (domain_name, service_name) /* add/find permission rule to hwservicemanager */

等價於:

    allow domain_name service_name:hwservice_manager { add find };

    neverallow { domain -domain_name } service_name:hwservice_manager add;

審查最終結果/確認更新

sepolicy 分離存儲到system image 和 vendor image, 在編譯完成後, 進行審查確認時, 可以查證:

/system/etc/selinux; /vendor/etc/selinux.

這裏寫圖片描述

注意

  1. 維持external/sepolicy 與Google AOSP一致, 儘量不要修改. 特別是不要去除任何的neverallow 語句.

  2. 嚴禁修改untrusted_app.te, 放開untrusted_app 的權限.

  3. 系統關鍵進程啓動長時間運行的process, 必須進行domain 切換. 比如netd, installd, vold, zygote 等.

  4. Native thread 嚴禁使用kernel 標籤, 而kernel thread 除init 外, 都應當是kernel 標籤.

  5. 在user build 中不能存在如su, recovery, init_shell 標籤的進程.

  6. 在user build 中所有的process 都必須是Enforce Mode

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章