android init.rc文件語法詳解(續)

    在“上一篇android  init.rc文件語法詳解”,但是到了android5.0之後,按照上面的方法做,可能我們要啓動的服務就起不來了。這是因爲採用了新的安全機制了——SEAndroid/SElinux的安全機制。

    下面就介紹下,在SEAndroid/SElinux如何配置才能啓動init.rc裏面定義的服務。

    下面我們在rc文件裏面定義一個服務

    service test1 /system/bin/test

user root

group root 

disabled

oneshot

 

    然後再on boot得時候啓動這個

    on boot

     start test1 

 

    如果在android5.0之前的系統,這樣就可以在on boot啓動這個服務了。但是到了android5.0之後,這個服務可能啓動失敗了。

    查看串口信息(注意不是logcat的信息,是串口信息),我們會看到“init: Warning!  Service test1 needs a SELinux domain defined; please fix!”這樣警告。這是因爲我們沒有爲service test1定義SELinux的權限規則。

    對於沒有定義SELinux的權限規則的service,系統只是給出一條警告,還是會繼續啓動這個進程。如果我們的服務沒有觸及到未允許的權限操作,那麼這個服務一樣會正常啓動的,我們可以直接無視這個警告。但是如果觸及到未允許的權限操作,那麼這個服務可能就不能正常啓動。這就需要我們定義一個SELinux domain,添加需要的權限。下面就介紹如何操作。

    第一步,在devices/platform/platform-sub/sepolicy/目錄下添加一個test1.te的文件(注意不同的平臺可能路徑不完全一樣,反正找到sepolicy目錄就好了,如果在上面介紹的路徑下沒有sepolicy目錄,我們也可以直接添加到external/sepolicy/下)。

    然後在sepolicy目錄下的Android.mk文件中添加下面的內容

    BOARD_SEPOLICY_UNION := \

    ... \

    test1.te

    

    第二步,在我們新建的test1.te文件中添加如下內容

    type test1, domain;

    type test1_exec, exec_type, file_type;

    init_daemon_domain(test1)

 

    然後在file_contexts(如果在sepolicy目錄下沒有,也可以添加到external/sepolicy/file_contexts)文件中添加如下內容

    /system/bin/test u:object_r:test1_exec:s0

    然後編譯,燒錄到目標板,重新啓動。這時再看串口信息,“init: Warning!  Service test1 needs a SELinux domain defined; please fix!”這個警告沒有了。但是我們的服務還是沒有正常啓動起來。這是因爲我們還沒有添加權限。

    第三步,我們查看logcat的調試信息。我們可以通過by Log Message="avc"來過濾。

    在logcat裏面,我們會看到類似下面的警告

    avc: denied { execute_no_trans } for path="/system/bin/toolbox" dev="mmcblk0p7" ino=306 scontext=u:r:test1:s0 tcontext=u:object_r:system_file:s0 tclass=file permissive=0

    說明test1沒有execute_no_trans的權限。這就需要我們添加權限,我們可以在test1.te文件中按下面的格式添加權限

    allow scontext tcontext:tclass {denied ...};

    例如對於上面的警告,我們可以加上如下的語句

    allow test1 system_file:file {execute_no_trans};

    也可以是這樣 allow test1 system_file:file execute_no_trans;

    如果最後面的denied 是多項 就必須用{}括起來。如果是一項是可以不要括號的。

    如果我們想賦予這個class下的所有權限,可以用通配符*號來代替。例如

    allow test1 system_file:file *;

 

    下面舉幾個例子

    avc: denied { execute } for name="pppd" dev="mmcblk0p7" ino=254 scontext=u:r:pppd_gprs:s0 tcontext=u:object_r:ppp_exec:s0 tclass=file permissive=0

    對於上面的警告,則需在pppd_gprs.te(如果沒有這個文件需要參考前面的方法添加這個文件)添加下面的語句

    allow pppd_gprs ppp_exec:file execute ;

 

    denied { write } for name="/" dev="mmcblk0p2" ino=1 scontext=u:r:init:s0 tcontext=u:object_r:vfat:s0 tclass=dir permissive=0

    在init.te添加下面的語句

    allow init vfat:dir { write   };

 

    另外添加了權限,有可能編譯出錯。

    例如我們添加了

    allow test1 system_file:file entrypoint;

    可能會出現下面的編譯錯誤

    libsepol.check_assertion_helper: neverallow on line 239 of external/sepolicy/domain.te (or line 5194 of policy.conf) violated by allow test1 system_file:file { entrypoint };

    我們看看domain.te的239行,有下面的語句

    neverallow domain { file_type -exec_type }:file entrypoint;

    neverallow和我們定義的allow衝突了,我們需要設置test1爲例外

    所以要修改上面那一句爲

    neverallow { domain -test1 } { file_type -exec_type }:file entrypoint;

    再編譯,通過了。把image下載到目標板上再運行,服務正常跑起來了。

    如果還是不能正常運行,再看logcat的調試信息,看看還有沒有avc的警告,如果有,繼續重複前面的步驟,直到沒有警告爲止,服務就可以正常運行了。

 

    通過上面的方法,在init.rc裏面啓動服務已經沒有問題了,但是我們有很多服務不是在init.rc裏面啓動的,而是在其他的服務或者進程中根據某些條件,通過property_set("ctl.start", "xxx")、property_set("ctl.stop", "xxx")來啓動或者結束服務。例如我們常用的3G、4G的驅動,往往就是在rild中初始化模塊OK後啓動pppd_gprs這個服務的。

    但是在5.0下,我們往往會發現rild正常工作了,但是pppd_gprs沒有啓動。logcat看也沒有avc相關的警告。下面就以這個爲例子,看看怎麼解決這個問題。

    我們接上調試串口,可以看到

init: sys_prop: Unable to start service ctl [pppd_gprs] uid:0 gid:1001 pid:148

init: sys_prop: Unable to stop service ctl [pppd_gprs] uid:0 gid:1001 pid:148

這是因爲android在SELINUX的基礎上增加了對property的權限的限制。

這個串口信息是在 /system/core/init/property_service.c的handle_property_set_fd函數中打印的。通過這個調試信息可以看出,是PID=148的進程,沒有設置ctl.start的權限,造成的。再用ps查看當前進程,可以看到PID爲148的進程是rild。那麼我們給rild賦予權限就好了。下面介紹如何添加

    property對應的上下文都定義在external/sepolicy/property_contexts文件中,打開這個文件我們可以看到裏面有“ctl.                    u:object_r:ctl_default_prop:s0”的定義,說明ctl.start和ctl.stop對應的上下文是ctl_default_prop。在這裏,我們要給rild賦予設置環境變量的權限。

    我們就需要在rild.te的文件中增加allow rild ctl_default_prop:property_service set;就可以了。如果沒有這個文件,我們可以參考前面的介紹自己添加一個文件。然後再編譯,運行,之前的提示沒有了,再看pppd_gprs也已經啓動起來了。

 

    在pppd_gprs的腳本里面,也許有設置property的地方,我們會發現設置property沒有生效,看串口信息,有init:sys_prop: permission denied uid:169  name:net.ppp0.local-ip的提示。這個也是在handle_property_set_fd中打印的,這個原因是一樣的,也是pppd_gprs服務沒有權限。同樣的參考原來的方法增加權限就好了。如果不知道是哪個服務設置的property,我們可以把source_ctx也打印出來,就知道是哪個服務了。

    最後還有一個問題,如果我們設置的property在property_contexts沒有定義怎麼辦呢?

    例如前面舉例的net.ppp0.local-ip沒有定義,那怎麼處理呢?方法如下

    1 在property_contexts中添加 net.ppp0.local-ip u:object_r:net_radio_ppp0_prop:s0(net_radio_ppp0_prop可以取任何有名字,也可以是這個文件裏面已經定義的名字)

    2 在property.te文件中增加type net_radio_ppp0_prop, property_type;(如果1中定義的是已經有的名字,2就不需要了)

    3 參考前面的方法添加權限。就可以了

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