1. 程式人生 > >深入理解SELinux/SEAndroid

深入理解SELinux/SEAndroid

 

關於SEAndroid的更多官方說明,請參考

http://source.android.com/devices/tech/security/se-linux.html

有了上文的SELinux的基礎知識,本節再來看看Google是如何在Android平臺定製SELinux的。如前文所示,Android平臺中的SELinux叫SEAndroid。

先來看SEAndroid安全策略檔案的編譯。

1. 編譯sepolicy

Android平臺中:

external/sepolicy:提供了Android平臺中的安全策略原始檔。同時,該目錄下的tools還提供了諸如m4,checkpolicy等編譯安全策略檔案的工具。注意,這些工具運行於主機(即不是提供給Android系統使用的)external/libselinux:提供了Android平臺中的libselinux,供Android系統使用。external/libsepol:提供了供安全策略檔案編譯時使用的一個工具checkcon。

對我們而言,最重要的還是external/sepolicy。所以先來看它。

讀者還記得上文提到的如何檢視make命令的執行情況嗎?通過:

mmm external/sepolicy --just-print

,我們可以看到sepolicy編譯時都幹了些什麼。

#以後用SEPOLICY_TEMP代替

# out/target/product/generic/obj/ETC/sepolicy_intermediates字串

#建立臨時目錄

mkdir -p out/target/product/generic/obj/ETC/sepolicy_intermediates/

#----->處理一堆輸入原始檔,最終輸出為policy.conf

#執行m4命令,用來生成plicy.conf檔案。m4命令將擴充套件SEAndroid定義的一些巨集

m4 -D mls_num_sens=1 -D mls_num_cats=1024 -s

#m4的輸入檔案。下面標黑體的是SEAndroid一些系統相關的檔案,一般不會修改它

security_classes initial_sids access_vectors

global_macros mls_macros mls

policy_capabilities te_macros attributes

#Android系統中的te檔案。

adbd.te app.te bluetoothd.te bluetooth.te clatd.te dbusd.te debuggerd.te device.te dhcp.te dnsmasq.te domain.te drmserver.te file.te gpsd.te hci_attach.te healthd.te hostapd.te init_shell.te init.te installd.te isolated_app.te kernel.te keystore.te media_app.te mediaserver.te mtp.te netd.te net.te nfc.te ping.te platform_app.te ppp.te property.te qemud.te racoon.te radio.te release_app.te rild.te runas.te sdcardd.te servicemanager.te shared_app.te shell.te surfaceflinger.te su.te system.te tee.te ueventd.te unconfined.te untrusted_app.te vold.te watchdogd.te wpa_supplicant.te zygote.te

#其他檔案

roles users initial_sid_contexts fs_use genfs_contexts port_contexts

#m4:將上述原始檔處理完後,生成policy.conf

> SEPOLICY_TEMP/policy.conf

#下面這個命令將根據policy.conf中的內容,再生成一個policy.conf.dontaudit檔案

sed '/dontaudit/d'

SEPOLICY_TEMP/policy.conf >

SEPOLICY_TEMP/policy.conf.dontaudit

mkdir -p SEPOLICY_TEMP/

#------>根據policy.conf檔案,生成二進位制檔案。SEAndroid中,它叫sepolicy

#執行checkpolicy,輸入是policy.conf,輸出是sepolicy

#-M選項表示支援MLS

checkpolicy -M -c 26 -o SEPOLICY_TEMP/sepolicy

SEPOLICY_TEMP/policy.conf

#執行checkpolicy,輸入是policy.conf.dontaudit,輸出是sepolicy.dontaudit

checkpolicy -M -c 26 -o

SEPOLICY_TEMP/sepolicy.dontaudit

SEPOLICY_TEMP/policy.conf.dontaudit

#--->將sepolicy拷貝到對應目標平臺的root目錄下

echo "Install: out/target/product/generic/root/sepolicy"

acp -fp SEPOLICY_TEMP/sepolicy

out/target/product/generic/root/sepolicy

#---->生成file_context檔案

#用FILE_CONTEXT_TEMP代替

# out/target/product/generic/obj/ETC/file_contexts_intermediates字串

mkdir -p FILE_CONTEXT_TEMP/

m4 -s external/sepolicy/file_contexts > FILE_CONTEXT_TEMP/file_contexts

checkfc SEPOLICY_TEMP/sepolicy

FILE_CONTEXT_TEMP/file_contexts

echo "Install: out/target/product/generic/root/file_contexts"

acp -fp FILE_CONTEXT_TEMP/file_contexts

out/target/product/generic/root/file_contexts

#--->生成seapp_context檔案,這個是Android平臺特有的,其作用我們下文再介紹

#用SEAPP_CONTEXT_TEMP代替

# out/target/product/generic/obj/ETC/seapp_contexts_intermediates

mkdir -p SEAPP_CONTEXT_TEMP/

checkseapp -p SEPOLICY_TEMP /sepolicy

-o SEAPP_CONTEXT_TEMP/seapp_contexts SEAPP_CONTEXT_TEMP/seapp_contexts.tmp

echo "Install: out/target/product/generic/root/seapp_contexts"

acp -fp SEAPP_CONTEXT_TEMP/seapp_contexts

out/target/product/generic/root/seapp_contexts

#---->和Android平臺中的屬性相關。SEAndroid中,設定屬性也需要相關許可權

#用PROPERTY_CONTEXT_TMP代替:

# out/target/product/generic/obj/ETC/property_contexts_intermediates

mkdir -p PROPERTY_CONTEXT_TMP/

m4 -s external/sepolicy/property_contexts >

PROPERTY_CONTEXT_TMP/property_contexts

checkfc -p TARGET_SEPOLICY_TEMP/sepolicy

PROPERTY_CONTEXT_TMP/property_contexts

echo "Install: out/target/product/generic/root/property_contexts"

acp -fp PROPERTY_CONTEXT_TMP/property_contexts

out/target/product/generic/root/property_contexts

上面展示了sepolicy編譯的執行情況,讀者最好自己嘗試一下。注意,checkfc,checkseapp等都是SEAndroid編譯時使用的工具,它們用來做策略檢查,看看是否有規則不符合的地方。

總結:

sepolicy的重頭工作是編譯sepolicy安全策略檔案。這個檔案來源於眾多的te檔案,初始化相關的檔案(initial_sid,initial_sid_context,users,roles,fs_context等)。file_context:該檔案記載了不同目錄的初始化SContext,所以它和死貨打標籤有關。seapp_context:和Android中的應用程式打標籤有關。property_contexts:和Android系統中的屬性服務(property_service)有關,它為各種不同的屬性打標籤。

下面我們來看看和SEAndroid相關的程式碼,故事從init開始。

2. init的SEAndroid定製

Android平臺中,SEAndroid的初始化由程序的祖先init的main函式完成,相關程式碼如下所示:

[-->init.c:main]

process_kernel_cmdline();

//向SELinux設定兩個回撥函式,主要是列印log

union selinux_callback cb;

cb.func_log = klog_write;

selinux_set_callback(SELINUX_CB_LOG, cb);

cb.func_audit = audit_callback;

//selinux_set_callback由libselinux提供。讀者可google libselinux各個API

//的作用

selinux_set_callback(SELINUX_CB_AUDIT, cb);

//①初始化SEAndroid

selinux_initialize();

//②給下面幾個目錄打標籤!

restorecon("/dev");

restorecon("/dev/socket");

restorecon("/dev/__properties__");

restorecon_recursive("/sys");

上述程式碼中的兩個重要函式:

selinux_initialize:初始化SEAndroid:一堆的restoercon,全稱應該是restore context:就是根據file_contexts中的內容給一些目錄打標籤。

先來看selinux_initialize:

2.1 selinux_initialize分析

[-->init.c:: selinux_initialize]

static void selinux_initialize(void)

{

/*判斷selinux功能是否啟用。方法是:

1) /sys/fs/selinux 是否存在。或者

2) ro.boot.selinux 屬性不為disabled

*/

if (selinux_is_disabled()) return;

//載入sepolicy檔案

if (selinux_android_load_policy() < 0) {......}

selinux_init_all_handles();

/*selinux有兩種工作模式

“permissive”:所有操作都被允許(即沒有MAC),但是如果有違反許可權的話,會記錄日誌

“enforcing”:所有操作都會進行許可權檢查

*/

bool is_enforcing = selinux_is_enforcing();

//設定SELinux的模式

security_setenforce(is_enforcing);

}

來看上述程式碼中的兩個函式:

selinux_android_load_policy:載入sepolicy檔案。selinux_init_all_handles:初始化file_context,seapp_context及property_context相關內容。
(1) selinux_android_load_policy

來看selinux_android_load_policy,其程式碼如下所示:

[-->external/libselinux/src/android.c:: selinux_android_load_policy]

int selinux_android_load_policy(void)

{

char *mnt = SELINUXMNT;// 值為/sys/fs/selinux

int rc;//掛載/sys/fs/selinux,SELINUXFS值為"selinuxfs"

rc = mount(SELINUXFS, mnt, SELINUXFS, 0, NULL);

......

// /sys/fs/selinux為userpace和kernel中的SELinux模組互動的通道

set_selinuxmnt(mnt);//此函式定義在selinux.h中,屬於libselinux API.

return selinux_android_reload_policy(); //載入SEAndroid中的policy檔案

}

圖11展示了Nexus 7上/sys/fs/selinux的內容:

\

圖11 /sys/fs/selinux的內容

使用者空間程序可同讀寫/sys/fs/selinux的各個檔案或其中的子目錄來通知Kernel中的SELinux完成相關的操作。

我們此處此處舉一個例子,如圖11下方紅框中的booleans資料夾:

我們可以SELinux的安全配置檔案中寫一些類似if/else的語句。if中的是布林判斷條件。比如booleans資料夾下有一個in_qemu檔案,這個就是sepolicy安全配置檔案中的一個布林變數。in_qemu定義在domain.te檔案中,關鍵詞是boolcat booleans/in_qemu:列印in_qemu布林變數的取值。讀者會發現它的值為“0 0”。為什麼有兩個零呢,這第一個0是它的當前值,第二個零代表pending取值。即還沒有賦值給當前值的一箇中間變數。如果我們通過 echo "1" > booleans/in_qemu的話,第二個零將變成1。為什麼需要有中間變數呢?讀者注意圖11的右上方有一個commit_pending_bools檔案。原來,通過在布林變數中設定一個pending變數,我們可以實現批處理操作。即先修改1個或多個布林變數的pending變數,然後往commit_pending_bools寫1,這樣這些一個或多個的布林變數將使用pending變數取代當前值。

接下來看看selinux_android_reload_policy函式:

[-->external/libselinux/src/android.c:: selinux_android_reload_policy]

int selinux_android_reload_policy(void)

{

int fd = -1, rc; struct stat sb; void *map = NULL;

int i = 0;

// sepolicy_file指明sepolicy檔案的路徑。Android中有兩處,第一個是

// /data/security/current/sepolicy。第二個是root目錄下的sepolicy檔案。

//下面這段邏輯可知,SEAndroid只使用其中的一個,如果/data/目錄下有sepolicy檔案,則

//優先使用它

while (fd < 0 && sepolicy_file[i]) {

fd = open(sepolicy_file[i], O_RDONLY | O_NOFOLLOW);

i++;

}

......

map = mmap(NULL, sb.st_size, PROT_READ, MAP_PRIVATE, fd, 0);

......

//假設使用根目錄下的sepolicy檔案。下面這個函式由selinux.h定義,它將此檔案載入到

//核心中

rc = security_load_policy(map, sb.st_size);

......

munmap(map, sb.st_size);

close(fd);

return 0;

}

init通過mmap的方式,將sepolicy檔案傳遞給了kernel。init使用了libselinux提供的API函式來完成相關操作。而libselinux則是通過操作/sys/fs/selinux下的檔案來完成和Kernel中SELinux模組的互動。libselinux庫的API不是我們研究的重點,感興趣的兄弟請自己研究原始碼。

總之,selinux_android_load_policy幹得最重要的一件事情就是將sepolicy檔案傳遞給Kernel,這樣Kernel就有了安全策略配置檔案,後續的MAC才能開展起來。

在此,請讀者注意sepolicy檔案的位置:

首先檢視/data/security/current/sepolicy:筆者自定義的策略檔案一般放在這裡,init優先使用它。/sepolicy:root根目錄下的sepolicy,如果data目錄下沒有sepolicy,則使用它。系統預設的sepolicy在此。
(2) selinux_init_all_handles

前面講過,init要給一些死貨和property打標籤,為了完成這個工作,根據libselinux的API,init需要先建立兩個handler,程式碼在selinux_init_all_handles中:

[-->init.c:: selinux_init_all_handles]

void selinux_init_all_handles(void)

{

sehandle = selinux_android_file_context_handle();

sehandle_prop = selinux_android_prop_context_handle();

}

建立兩個handler,主要為後續做labeling控制。我們來看看prop的context:

[-->init.c::selinux_android_prop_context_handle]

struct selabel_handle* selinux_android_prop_context_handle(void)

{

int i = 0;

struct selabel_handle* sehandle = NULL;

//setopts_prop也有兩個值:

//第一個是/data/security/property_contexts。第二個是/property_contexts

while ((sehandle == NULL) && seopts_prop[i].value) {

sehandle = selabel_open(SELABEL_CTX_ANDROID_PROP, &seopts_prop[i], 1);

i++;

}

//假設採用的是根目錄下的property_context檔案

......

return sehandle;

}

handler其實就是為了開啟xxx_context檔案。由於它們和restorecon有關,下面直接來看restorecon函式,看看這些handler是怎麼個用法。

2.2 restorecon分析

[-->init.c::restorecon]

int restorecon(const char *pathname)

{

char *secontext = NULL;

struct stat sb;

int i;

if (is_selinux_enabled() <= 0 || !sehandle)

return 0;

if (lstat(pathname, &sb) < 0) return -errno;

//查詢file_context檔案中是否包含有pathname路徑的控制選項

if (selabel_lookup(sehandle, &secontext, pathname, sb.st_mode) < 0)

return -errno;

//設定patchname目錄的security_context,lsetfilecon的實現非常簡單,就是呼叫

//

if (lsetfilecon(pathname, secontext) < 0) {

freecon(secontext);

return -errno;

}

freecon(secontext);

return 0;

}

想知道selinux是如何labeling一個檔案或目錄的嗎?答案在lsetfilecon中:

[-->external/libselinux/src/lsetfilecon.c:: lsetfilecon]

int lsetfilecon(const char *path, const security_context_t context)

{

//設定檔案系統的屬性

return lsetxattr(path, XATTR_NAME_SELINUX, context, strlen(context) + 1,0);

}

2.3 property許可權檢查

一般而言,SELinux許可權檢查都是由kernel來完成的,不過對於Android平臺中的Property而言,這卻完全是一個使用者空間的內容。所以,我們看看init是如何使用libselinux來完成使用者空間的許可權檢查的。

每當其他程序通過setprop函式設定屬性時,property_service中有一個叫check_

[system/core/init/property_service.c:: check_mac_perms]

static int check_mac_perms(const char *name, char *sctx)

{

if (is_selinux_enabled() <= 0) return 1;

char *tctx = NULL;

const char *class = "property_service";

const char *perm = "set";

int result = 0;

......

//檢查property_context中是否定義了目標SContext,即tctx。

if (selabel_lookup(sehandle_prop, &tctx, name, 1) != 0) goto err;

//將源SContext和目標SContext進行比較,判斷是否有相關許可權。name是屬性的名字

//源SContext是呼叫setprop程序的SContext。目標SContext是property_context

//檔案中定義的SContext。

if (selinux_check_access(sctx, tctx, class, perm, name) == 0)

result = 1;

freecon(tctx);

err:

return result;

}

怎麼樣?理解起來並不困難吧?使用者空間的許可權檢查主要就是通過selinux_check_access完成,其輸入引數包括:

源的SContext:它就是呼叫setprop的程序的SContext目標的SContext:不同的屬性有不同的SContext,這是在property_context中定義的。要檢查的Object class(系統所支援的類在external/sepolicy/security_classes檔案中定義)。操作名稱(perm,由access vector定義。對Property這種Object class而言,其唯一需要做許可權檢查的操作就是set。讀者可參考external/sepolicy/access_vectors這個檔案)。

具體的哪一個屬性(name引數指定,就是具體指明哪一檔案)。

提示:關於這些API的說明,讀者請參考http://selinuxproject.org/page/User_Resources中的Manual pages文件。

下面我們來看Android中應用程式是如何使用SELinux的。

3. 應用程式中的SELinux

對應用程式而言,最重要的工作就是管理它們的DT和TT:

所有Android的Application對應的程序都是從zygote程序中fork出來的。從前文介紹DT的知識可知,在做DT時,可以根據所執行的不同Type的檔案來轉換到不同的DT。但這個對Android而言不可行。因為zygote在fork子程序後,並沒有執行execv。apk在安裝後,都會在/data/data/目錄下建立自己對於的資料夾,這個工作是由installd來完成的。同樣,installd應該給這些不同的資料夾打上對應的label。

我們先來看應用程式的DT。

3.1 Java應用程式的DT

Android中應用程序(就是APK所在的程序)的DT轉換其實很簡單,它及其具有Android特色:

普通的DT是根據所execv檔案的Type來設定DT轉換條件。而Android中則根據該APK簽名信息來講最終的程序轉換到幾種預設值的Domain中。
(1) mac_permissions.xml的用途

我們先來看PackageManagerService:

[-->PackageManagerService.java::PackageManageService]

......

/*下面這個函式將嘗試解析

1)/data/security/mac_permissions.xml 或

2)/system/etc/security/mac_permissions.xml 中的內容。

*/

mFoundPolicyFile = SELinuxMMAC.readInstallPolicy();

注意,mac_permissions.xml位於external/sepolicy中,圖12所示為該檔案的原始:

\

圖12 mac_permissions.xml的內容

在系統過程中,圖12中的@RELEASE,@PLATFORM等內容會被RELEASE、PLATFORM簽名信息替換。圖13所示為Nexus 7中該檔案的內容。

\

圖13 Nexus7中mac_permissions.xml內容

mac_permissions.xml儲存了不同簽名所對應的seinfo:如seinfo為platform時的簽名是什麼,seinfo為media的時候簽名又是什麼。那麼,這些資訊有啥用呢?來看下文。

(2) 掃描APK

當APK安裝時,也就是APK被PKMGS掃描的時候,有如下的程式碼:

[-->PackageManagerService.java::ScanPackageLI]

if (mFoundPolicyFile) {

//下面這個函式將根據簽名信息賦值seinfo值給對應的apk

SELinuxMMAC.assignSeinfoValue(pkg);

}

[-->SELinuxMMAC.java::assignSeinfoValue]

public static void assignSeinfoValue(PackageParser.Package pkg) {

//對於系統app(預裝的,位於system目錄下的)

if (((pkg.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) ||

((pkg.applicationInfo.flags &

ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0)) {

for (Signature s : pkg.mSignatures) {

if (s == null) continue;

//sSigSeinfo儲存了mac_permissions.xml中seinfo標籤的內容

if (sSigSeinfo.containsKey(s)) {

String seinfo = pkg.applicationInfo.seinfo = sSigSeinfo.get(s);

return;

}

}

//sPackageSeinfo儲存了xml中package標籤下seinfo子標籤的內容

if (sPackageSeinfo.containsKey(pkg.packageName)) {

String seinfo = pkg.applicationInfo.seinfo =

sPackageSeinfo.get(pkg.packageName);

return;

}

}

//default標籤中seinfo的值

String seinfo = pkg.applicationInfo.seinfo = sSigSeinfo.get(null);

......

}

assignSeinfoValue的功能如上程式碼所示,它根據apk的簽名信息來賦值不同的seinfo,也就是諸如"platform",”media“之類的值。

提示:大家能想出為什麼要設定seinfo嗎?恩,它就是Android為App定義的SContext中的Domain的值。

(3) App的DT轉換

ActivityManagerService負責啟動目標應用程序,相關程式碼如下所示:

[-->ActivityManagerService.java:: startProcessLocked]

Process.ProcessStartResult startResult =

Process.start("android.app.ActivityThread",

app.processName, uid, uid, gids, debugFlags, mountExternal,

app.info.targetSdkVersion, app.info.seinfo, null);

根據《深入理解Android卷I》第4章對zygote的介紹,zygote程序將fork一個子程序,相關函式在:

[-->ZygoteConnection.java::runOnce]

pid = Zygote.forkAndSpecialize(parsedArgs.uid, parsedArgs.gid, parsedArgs.gids,

parsedArgs.debugFlags, rlimits, parsedArgs.mountExternal,

parsedArgs.seInfo,parsedArgs.niceName);

該函式由JNI實現,程式碼在dalvik/vm/native/ dalvik_system_Zygote.cpp中,其中最重要的是內部所呼叫的forkAndSpecializeCommon:

[-->dalvik_system_Zygote.cpp:: forkAndSpecializeCommon]

pid = fork();

if (pid == 0) {

......

err = setSELinuxContext(uid, isSystemServer, seInfo, niceName);

.....}

[-->external/libselinux/android.c::selinux_android_setcontext]

int selinux_android_setcontext(uid_t uid,int isSystemServer,

const char *seinfo,const char *pkgname)

{

char *orig_ctx_str = NULL, *ctx_str; context_t ctx = NULL;

int rc = -1;

if (is_selinux_enabled() <= 0) return 0;

//重要函式:seapp_context_init,內部將呼叫selinux_android_seapp_context_reload

//以載入seapp_contexts檔案。

// 1) /data/security/current/seapp_contexts 或者

// 2) /seapp_contexts 本例而言,就是根目錄下的這個seapp_context檔案

__selinux_once(once, seapp_context_init);

rc = getcon(&ctx_str);

ctx = context_new(ctx_str);

orig_ctx_str = ctx_str;

//從zygote程序fork出來後,最初的SContext取值為u:r:zygote:s0

//下面這個函式將根據uid,pkgname等設定最終的SC。例如u:r:system_app:s0等

rc = seapp_context_lookup(SEAPP_DOMAIN, uid, isSystemServer, seinfo, pkgname,

ctx);

ctx_str = context_str(ctx);

rc = security_check_context(ctx_str);

if (strcmp(ctx_str, orig_ctx_str)) {

rc = setcon(ctx_str);

}

rc = 0;

......

return rc;

}

圖14所示為seapp_context的內容,非常簡單:

\

圖14 seapp_context內容

上面程式碼中的seapp_context_lookup將根據圖14的內容,通過不同的apk所對應的seinfo,找到他們的目標domain,然後再設定為它們新的SContext。例如圖15的Nexus 7中ps -Z的結果圖。

\

圖15 ps -Z檢視apk程序的SContext

seapp_context_lookup是完成從seapp_context檔案內容對映到具體對應為哪個Domain的關鍵函式,該函式第一次看起來嚇死人,其實蠻簡單。這裡就不再多說。

anyway,SEAndroid中,不同應用程式將根據它們的簽名信息得到對應的SContext(主要是Domain,MLS其實沒用上,但以後可以用上,這是通過圖14中的levelFrom語句來控制的,具體可參考seapp_context_lookup的實現)。

DT完成後,我們看系統如何為它們的對應資料夾打標籤

3.2 App data目錄的TT

還是在PackageManagerService的scanPackageLI函式中,

[-->PackageManagerService.java:: scanPackageLI]

int ret = createDataDirsLI(pkgName, pkg.applicationInfo.uid,

pkg.applicationInfo.seinfo);

createDataDirsLI最終會呼叫installd實現的函式:

[-->installd/commands.c::install]

//內部呼叫selinux_android_setfilecon2,它和上文的selinux_android_setcontext

//幾乎一樣。最終它將設定pkgdir的SContext。注意,它主要根據seapp_context檔案中的

//type欄位來確定最終的Type值。

if (selinux_android_setfilecon2(pkgdir, pkgname, seinfo, uid) < 0) {

......

}

圖16展示了ls -Z /data/data目錄下的結果。

\

圖16 /data/data目錄下ls -Z的結果

是不是和圖14中seapp_context檔案的type欄位描述一樣一樣的?

4 小試牛刀

下面,筆者將通過修改shell的許可權,使其無法設定屬性。

先來看shell的te,如下所示:

[external/sepolicy/shell.te]

# Domain for shell processes spawned by ADB

type shell, domain;

type shell_exec, file_type;

#shell屬於unconfined_domain,unconfined即是不受限制的意思

unconfined_domain(shell)

# Run app_process.

# XXX Split into its own domain?

app_domain(shell)

unconfied_domain是一個巨集,它將shell和如下兩個attribute相關聯:

[external/sepolicy/te_macros]

#####################################

# unconfined_domain(domain)

# Allow the specified domain to do anything.

#

define(`unconfined_domain', `

typeattribute $1 mlstrustedsubject; #這個和MLS有關

typeattribute $1 unconfineddomain;

')

unconfineddomain許可權很多,它的allow語句定義在unconfined.te中:

[external/sepolicy/unconfined.te]

......

allow unconfineddomain property_type:property_service set;

從上面可以看出,shell所關聯的unconfineddomain有許可權設定屬性。所以,我們把它改成:

allow {unconfineddomain -shell} property_type:property_service set;

通過一個“-”號,將shell的許可權排除。

然後:

我們mmm external/sepolicy,得到sepolicy檔案。將其push到/data/security/current/sepolicy目錄下接著呼叫setprop selinux.reload_policy 1,使得init重新載入sepolicy,由於/data目錄下有了sepolicy,所以它將使用這個新的。

圖17所示為整個測試的例子:

\

圖17 測試結果

根據圖17:

重新載入sepolicy之前,筆者可通過"setprop wlan.driver.status test_ok"設定該屬性的值為test_ok。注意,這個值筆者瞎設的。當通過setprop selinux.reload_policy 1的命令後,init重新載入了sepolicy。從此,筆者再setprop wlan.driver.status 都不能修改該屬性的值。

圖18所示為dmesg輸出,可以看出,當selinux使用了data目錄下這個新的sepolicy後,shell的setprop許可權就被否了!

\

圖18 dmesg輸出

提示:前面曾提到過audit,日誌一類的事情。恩,這個日誌由kernel輸出,可藉助諸如audit2allow等host上的工具檢視哪些地方有違反許可權的地方。系統會將源,目標SContext等資訊都打印出來。

三 全文總結

本文對SELinux的核心知識進行了介紹。從入門角度來說,有了這些內容,SELinux大概80%左右的知識都已經介紹,剩下來的工作就是不斷去修改和嘗試不同的安全配置檔案。

然後我們對SEAndroid進行了相關介紹,這部分基本上反映了Android是如何利用這些安全配置檔案來構造自己的安全環境的。

從目前AOSP SEAndroid安全配置原始檔來看,很多te檔案中都使用瞭如下這樣的語句:

\

圖19 permissive定義

其中,permissive關鍵詞表示不用對上述這些type/domain進行MAC監管。permissive一般用於測試某個策略,看是否對整個系統有影響。一旦測驗通過,就可以把permissve語句移掉,以真正提升安全。

基於SEAndroid,廣大搞機人可以:

針對行業,開發更加安全的安全策略。定製MLS,針對企業級或軍用級的多層許可權管理。不知道google有沒有意向實現Modular Policy,這樣的話,SELinux靈活性更高。

另外,要提醒讀者的是,安全配置需要考慮的東西非常多,稍有不甚,就會影響系統其他模組的執行。比如筆者在研究SELinux時,不小心把Ubuntu的影象介面系統啟動不了,後來只能移除SELinux後才解決。這也是為什麼SELinux出來這麼多年,但是大家好像碰到它的機會很少的原因,因為它的配置實在是太麻煩,很容易出錯!

最後,反覆提醒讀者,一旦修改了策略檔案,務必進行全方位,多層面測試。