前面一節我們已經講解了init程序對目錄生成和挂載、日志初始化和設定,接下來init程序将初始化SELinux[1]并設定policy檔案,如下面代碼所示。若要詳細了解SELinux的設計原理和工作機制,需要用一整本書來講解,由于篇幅所限,在此我們不過多涉及這方面的内容。init程序運作在使用者空間,主要涉及對SELinux的挂載和配置,下面我們把重點放在這個過程上。
static void selinux_initialize(bool in_kernel_domain) {
selinux_callback cb;
cb.func_log = selinux_klog_callback;
selinux_set_callback(SELINUX_CB_LOG, cb); <---()
cb.func_audit = audit_callback;
selinux_set_callback(SELINUX_CB_AUDIT, cb);
if (in_kernel_domain) {
INFO("Loading SELinux policy...\n");
if (selinux_android_load_policy() < ) { <--- ()
ERROR("failed to load policy: %s\n", strerror(errno));
security_failure();
}
bool kernel_enforcing = (security_getenforce() == );
bool is_enforcing = selinux_is_enforcing();
if (kernel_enforcing != is_enforcing) { <--- ()
if (security_setenforce(is_enforcing)) {
ERROR("security_setenforce(%s) failed: %s\n",
is_enforcing ? "true" : "false", strerror(errno));
security_failure();
}
}
if (write_file("/sys/fs/selinux/checkreqprot", "0") == ) { <--- ()
security_failure();
}
...
}
int main(int argc, char** argv) {
...
selinux_initialize(is_first_stage);
...
}
(1) selinux_initialize()函數調用selinux_set_callback()函數設定兩個全局的回調函數指針“selinux_log”和“selinux_audit”。
(2) 調用selinux_android_load_policy()[2]函數加載并向核心設定政策,selinux_initialize()函數的主要作用就是從檔案中讀取SELinux的配置檔案,然後把它設定到核心中,這樣SELinux才能開始工作。
(3) 檢查是否啟用SELinux,有兩種方法可以配置,一是SELinux挂載目錄下的enforce檔案,其中隻有一個數字值表示SELinux的目前狀态,0表示不強制使用(permissive[3]),1則表示強制使用(enforcing);其次是/proc/cmdline檔案,在其中加入“androidboot.selinux= permissive”字段表示不強制使用(permissive),否則強制使用(enforcing),這種方法主要用于開發或調試版本。
(4) 在“/sys/fs/selinux/checkreqprot”檔案中寫入校驗結果,如果寫入成功,則SELinux設定完畢,可正常使用。
上述代碼的(2)所示selinux_android_load_policy()函數是加載SELinux的主要邏輯代碼,如下面的代碼所示,selinux_android_load_policy()函數嘗試将SELinux的虛拟檔案系統selinuxfs挂載到“/sys/fs/selinux”節點上,形成下面的圖所示的層次結構。
如果核心沒有啟用SELinux,則“/sys/fs/selinux”挂載失敗,selinux_android_load_policy()函數繼續嘗試在根目錄下建立“/selinux”節點并挂載,如果再次失敗則SELinux初始化失敗。
在SELinux挂載成功後,調用selinux_android_load_policy_helper()函數裝載policy檔案。selinux_android_load_policy_helper()函數首先調用set_policy_index()函數設定policy檔案的索引,目的是為了打開在sepolicy_file數組中定義的policy檔案,然後調用mmap()函數把它映射到記憶體中,最後調用函數security_load_policy()把policy設定到核心中。policy檔案的檔案名儲存在數組sepolicy_file中,定義如下面的代碼所示。
static const char *const sepolicy_file[] = {
"/sepolicy",
"/data/security/current/sepolicy",
NULL };
至此,SELinux的初始化就講解完了。如前一篇鳥人的Android揭秘(10)——Init程序源代碼分析(一)所說,init程序将切換到第二階段的初始化過程。下一節我們将講解init程序對屬性的初始化和屬性服務的啟動。
[1] SELinux(Security-Enhanced Linux)是由美國國家安全局(NSA)實作的一種基于“域-類型”模型(domain-type)的強制通路控制(MAC)安全系統,在這種通路控制體系的限制下,程序隻能通路它的任務中所需要檔案。Linux核心在2.6版本中引入了SELinux,并提供一個可定制的安全政策,同時也提供了很多使用者層的庫和工具。SELinux是目前為止功能最全面、測試最充分的Linux安全子產品,相應的某些安全相關的應用也被打了SELinux的更新檔。
[2] 該函數定義在external/libselinux/src/android.c中。
[3] permissive模式表示即使違反了安全政策,也隻是會發出警告,而不會真的拒絕執行。