天天看點

鳥人的Android揭秘(11)——Init程序源代碼分析(二)

      前面一節我們已經講解了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初始化失敗。

鳥人的Android揭秘(11)——Init程式源代碼分析(二)

      在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模式表示即使違反了安全政策,也隻是會發出警告,而不會真的拒絕執行。