成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

SEAndroid安全機(jī)制框架分析

移動(dòng)開發(fā) Android
SEAndroid安全機(jī)制所要保護(hù)的對(duì)象是系統(tǒng)中的資源,這些資源分布在各個(gè)子系統(tǒng)中,例如我們經(jīng)常接觸的文件就是分布文件子系統(tǒng)中的。實(shí)際上,系統(tǒng)中需要保護(hù)的資源非常多,除了前面說(shuō)的文件之外,還有進(jìn)程、socket和ipc等等。

SEAndroid安全機(jī)制所要保護(hù)的對(duì)象是系統(tǒng)中的資源,這些資源分布在各個(gè)子系統(tǒng)中,例如我們經(jīng)常接觸的文件就是分布文件子系統(tǒng)中的。實(shí)際上,系統(tǒng)中需要保護(hù)的資源非常多,除了前面說(shuō)的文件之外,還有進(jìn)程、socket和ipc等等。對(duì)于Android系統(tǒng)來(lái)說(shuō),由于使用了與傳統(tǒng)Linux系統(tǒng)不一樣的用戶空間運(yùn)行時(shí),即應(yīng)用程序運(yùn)行時(shí)框架,因此它在用戶空間有一些特有的資源是需要特別保護(hù)的,例如系統(tǒng)屬性的設(shè)置。

接下來(lái),我們就通過圖1來(lái)觀察SEAndroid安全機(jī)制的整體框架,如下所示:

圖1 SEAndroid安全機(jī)制框架

從圖1可以看到,以SELinux文件系統(tǒng)接口為邊界,SEAndroid安全機(jī)制包含有內(nèi)核空間和用戶空間兩部分支持。在內(nèi)核空間,主要涉及到一個(gè)稱為SELinux LSM的模塊。而在用戶空間中,涉到Security Context、Security Server和SEAndroid Policy等模塊。這些內(nèi)核空間模塊和用戶空間模塊的作用以及交互如下所示:

1. 內(nèi)核空間的SELinux LSM模塊負(fù)責(zé)內(nèi)核資源的安全訪問控制。

2. 用戶空間的SEAndroid Policy描述的是資源安全訪問策略。系統(tǒng)在啟動(dòng)的時(shí)候,用戶空間的Security Server需要將這些安全訪問策略加載內(nèi)核空間的SELinux LSM模塊中去。這是通過SELinux文件系統(tǒng)接口實(shí)現(xiàn)的。

3. 用戶空間的Security Context描述的是資源安全上下文。SEAndroid的安全訪問策略就是在資源的安全上下文基礎(chǔ)上實(shí)現(xiàn)的。

4. 用戶空間的Security Server一方面需要到用戶空間的Security Context去檢索對(duì)象的安全上下文,另一方面也需要到內(nèi)核空間去操作對(duì)象的安全上下文。

5. 用戶空間的selinux庫(kù)封裝了對(duì)SELinux文件系統(tǒng)接口的讀寫操作。用戶空間的Security Server訪問內(nèi)核空間的SELinux LSM模塊時(shí),都是間接地通過selinux進(jìn)行的。這樣可以將對(duì)SELinux文件系統(tǒng)接口的讀寫操作封裝成更有意義的函數(shù)調(diào)用。

6. 用戶空間的Security Server到用戶空間的Security Context去檢索對(duì)象的安全上下文時(shí),同樣也是通過selinux庫(kù)來(lái)進(jìn)行的。

接下來(lái),我們就從內(nèi)核空間和用戶空間兩個(gè)角度來(lái)分析SEAndroid安全機(jī)制框架。

一. 內(nèi)核空間

在內(nèi)核空間中,存在一個(gè)SELinux LSM模塊,這個(gè)模塊包含有一個(gè)訪問向量緩沖(Access Vector Cache)和一個(gè)安全服務(wù)(Security Server)。Security Server負(fù)責(zé)安全訪問控制邏輯,即由它來(lái)決定一個(gè)主體訪問一個(gè)客體是否是合法的。這里說(shuō)的主體一般就是指進(jìn)程,而客體就是主體要訪問的資源,例如文件。

與SELinux Security Server相關(guān)的一個(gè)內(nèi)核子模塊是LSM,全稱是Linux Security Model。LSM可以說(shuō)是為了SELinux而設(shè)計(jì)的,但是它是一個(gè)通用的安全模塊,SELinux可以使用,其它的模塊也同樣可以使用。這體現(xiàn)了Linux內(nèi)核模塊的一個(gè)重要設(shè)計(jì)思想,只提供機(jī)制實(shí)現(xiàn)而不提供策略實(shí)現(xiàn)。在我們這個(gè)例子中,LSM實(shí)現(xiàn)的就是機(jī)制,而SELinux就是在這套機(jī)制下的一個(gè)策略實(shí)現(xiàn)。也就是說(shuō),你也可以通過LSM來(lái)實(shí)現(xiàn)自己的一套MAC安全機(jī)制。

SELinux、LSM和內(nèi)核中的子系統(tǒng)是如何交互的呢?首先,SELinux會(huì)在LSM中注冊(cè)相應(yīng)的回調(diào)函數(shù)。其次,LSM會(huì)在相應(yīng)的內(nèi)核對(duì)象子系統(tǒng)中會(huì)加入一些Hook代碼。例如,我們調(diào)用系統(tǒng)接口read函數(shù)來(lái)讀取一個(gè)文件的時(shí)候,就會(huì)進(jìn)入到內(nèi)核的文件子系統(tǒng)中。在文件子系統(tǒng)中負(fù)責(zé)讀取文件函數(shù)vfs_read就會(huì)調(diào)用LSM加入的Hook代碼。這些Hook代碼就會(huì)調(diào)用之前SELinux注冊(cè)進(jìn)來(lái)的回調(diào)函數(shù),以便后者可以進(jìn)行安全檢查。

SELinux在進(jìn)行安全檢查的時(shí)候,首先是看一下自己的Access Vector Cache是否已經(jīng)有結(jié)果。如果有的話,就直接將結(jié)果返回給相應(yīng)的內(nèi)核子系統(tǒng)就可以了。如果沒有的話,就需要到Security Server中去進(jìn)行檢查。檢查出來(lái)的結(jié)果在返回給相應(yīng)的內(nèi)核子系統(tǒng)的同時(shí),也會(huì)保存在自己的Access Vector Cache中,以便下次可以快速地得到檢查結(jié)果。

上面描述的安全訪問控制流程可以通過圖2來(lái)總結(jié),如下所示:

 

圖2 SELinux安全訪問控制流程

從圖2可以看到,內(nèi)核中的資源在訪問的過程中,一般需要獲得三次檢查通過:

1. 一般性錯(cuò)誤檢查,例如訪問的對(duì)象是否存在、訪問參數(shù)是否正確等。

2. DAC檢查,即基于Linux UID/GID的安全檢查。

3. SELinux檢查,即基于安全上下文和安全策略的安全檢查。

二. 用戶空間

在用戶空間中,SEAndroid包含有三個(gè)主要的模塊,分別是安全上下文(Security Context)、安全策略(SEAndroid Policy)和安全服務(wù)(Security Server)。接下來(lái)我們就分別對(duì)它們進(jìn)行描述。

1. 安全上下文

SEAndroid是一種基于安全策略的MAC安全機(jī)制。這種安全策略又是建立在對(duì)象的安全上下文的基礎(chǔ)上的。這里所說(shuō)的對(duì)象分為兩種類型,一種稱主體(Subject),一種稱為客體(Object)。主體通常就是指進(jìn)程,而客觀就是指進(jìn)程所要訪問的資源,例如文件、系統(tǒng)屬性等。

安全上下文實(shí)際上就是一個(gè)附加在對(duì)象上的標(biāo)簽(Tag)。這個(gè)標(biāo)簽實(shí)際上就是一個(gè)字符串,它由四部分內(nèi)容組成,分別是SELinux用戶、SELinux角色、類型、安全級(jí)別,每一個(gè)部分都通過一個(gè)冒號(hào)來(lái)分隔,格式為“user:role:type:sensitivity”。

例如,在開啟了SEAndroid安全機(jī)制的設(shè)備上執(zhí)行帶-Z選項(xiàng)的ls命令,就可以看到一個(gè)文件的安全上下文:

  1. $ ls -Z /init.rc   
  2. -rwxr-x--- root     root     u:object_r:rootfs:s0 init.rc   

  上面的命令列出文件/init.rc的安全上下文為“u:object_r:rootfs:s0”,這表明文件/init.rc的SELinux用戶、SELinux角色、類型和安全級(jí)別分別為u、object_r、rootfs和s0。

       又如,在開啟了SEAndroid安全機(jī)制的設(shè)備上執(zhí)行帶-Z選項(xiàng)的ps命令,就可以看到一個(gè)進(jìn)程的安全上下文:

  1. $ ps -Z   
  2. LABEL                          USER     PID   PPID  NAME   
  3. u:r:init:s0                    root      1     0     /init   
  4. ......   

上面的命令列出進(jìn)程init的安全上下文為“u:r:init:s0”,這表明進(jìn)程init的SELinux用戶、SELinux角色、類型和安全級(jí)別分別為u、r、init和s0。

       在安全上下文中,只有類型(Type)才是最重要的,SELinux用戶、SELinux角色和安全級(jí)別都幾乎可以忽略不計(jì)的。正因?yàn)槿?此,SEAndroid安全機(jī)制又稱為是基于TE(Tyoe Enforcement)策略的安全機(jī)制。不過為了方便理解安全上下文,接下來(lái)我們還是簡(jiǎn)單地對(duì)SELinux用戶、SELinux角色和安全級(jí)別的作用 進(jìn)行介紹。

       對(duì)于進(jìn)程來(lái),SELinux用戶和SELinux角色只是用來(lái)限制進(jìn)程可以標(biāo)注的類型。而對(duì)于文件來(lái)說(shuō),SELinux用戶和SELinux角色就可以完 全忽略不計(jì)。為了完整地描述一個(gè)文件的安全上下文,通常將它的SELinux角色固定為object_r,而將它的SELinux用戶設(shè)置為創(chuàng)建它的進(jìn)程 的SELinux用戶。

       在SEAndroid中,只定義了一個(gè)SELinux用戶u,因此我們通過ps -Z和ls -Z命令看到的所有的進(jìn)程和文件的安全上下文中的SELinux用戶都為u。同時(shí),SEAndroid也只定義了一個(gè)SELinux角色r,因此,我們通 過ps -Z命令看到的所有進(jìn)程的安全上下文中的SELinux角色都為r。

       通過external/sepolicy/users和external/sepolicy/roles文件的內(nèi)容,我們就可以看到SEAndroid所定義的SELinux用戶和SELinux角色。

       文件external/sepolicy/users的內(nèi)容如下所示:

  1. user u roles { r } level s0 range s0 - mls_systemhigh;  
  2.  

述語(yǔ)句聲明了一個(gè)SELinux用戶u,它可用的SELinux角色為r,它的默認(rèn)安全級(jí)別為s0,可用的安全級(jí)別范圍為s0~mls_systemhigh,其中,mls_systemhigh為系統(tǒng)定義的最高安全級(jí)別。

       文件external/sepolicy/roles的內(nèi)容如下所示:

  1. role r;   
  2. role r types domain;   

第一個(gè)語(yǔ)句聲明了一個(gè)SELinux角色r;第二個(gè)語(yǔ)句允許SELinux角色r與類型domain關(guān)聯(lián)。

       上面提到,對(duì)于進(jìn)程來(lái)說(shuō),SELinux用戶和SELinux角色只是用來(lái)限制進(jìn)程可以標(biāo)注的類型,這是如何體現(xiàn)的呢?以前面列出的 external/sepolicy/users和external/sepolicy/roles文件內(nèi)容來(lái)例,如果沒有出現(xiàn)其它的user或者 role聲明,那么就意味著只有u、r和domain可以組合在一起形成一個(gè)合法的安全上下文,而其它形式的安全上下文定義均是非法的。

       讀者可能注意到,前面我們通過ps -Z命令看到進(jìn)程init的安全上下文為“u:r:init:s0”,按照上面的分析,這是不是一個(gè)非法的安全上下文呢?答案是否定的,因?yàn)樵诹硗庖粋€(gè)文 件external/sepolicy/init.te中,通過type語(yǔ)句聲明了類型init,并且將domain設(shè)置為類型init的屬性,如下所 示:

  1. type init, domain;   

由于init具有屬性domain,因此它就可以像domain一樣,可以和SELinux用戶u和SELinux角色組合在一起形成合法的安全上下文。

關(guān)于SELinux用戶和SELinux角色,我們就介紹到這里,接下來(lái)我們?cè)俳榻B安全級(jí)別。安全級(jí)別實(shí)際上也是一個(gè)MAC安全機(jī)制,它是建立在TE的基礎(chǔ)之上的。在SELinux中,安全級(jí)別是可選的,也就是說(shuō),可以選擇啟用或者不啟用。

安全級(jí)別最開始的目的是用來(lái)對(duì)政府分類文件進(jìn)行訪問控制的。在基于安全級(jí)別的MAC安全機(jī)制中,主體(subject)和客體(object)都關(guān)聯(lián)有一個(gè)安全級(jí)別。其中,安全級(jí)別較高的主體可以讀取安全級(jí)別較低的客體,而安全級(jí)別較低的主體可以寫入安全級(jí)別較高的客體。前者稱為“read down”,而后者稱為“write up”。通過這種規(guī)則,可以允許數(shù)據(jù)從安全級(jí)別較低的主體流向安全級(jí)別較高的主體,而限制數(shù)據(jù)從安全級(jí)別較高的主體流向安全級(jí)別較低的主體,從而有效地保護(hù)了數(shù)據(jù)。注意,如果主體和客體的安全級(jí)別是相同的,那么主體是可以對(duì)客體進(jìn)行讀和寫的。

通過圖3可以看到基于安全級(jí)別的MAC安全機(jī)制的數(shù)據(jù)流向控制,如下所示:

圖3 基于安全級(jí)別的MAC安全機(jī)制數(shù)據(jù)流

在圖3中,我們定義了兩個(gè)安全級(jí)別:PUBLIC和SECRET,其中,SECRET的安全級(jí)別高于PUBLIC。

在實(shí)際使用中,安全級(jí)別是由敏感性(Sensitivity)和類別(Category)兩部分內(nèi)容組成的,格式為“sensitivity[:category_set]”,其中,category_set是可選的。例如,假設(shè)我們定義有s0、s1兩個(gè)Sensitivity,以c0、c1、c2三個(gè)Category,那么“s0:c0,c1”表示的就是Sensitivity為s0、Category為c0和c1的一個(gè)安全級(jí)別。

介紹完成SELinux用戶、SELinux角色和安全級(jí)別之后,最后我們就介紹類型。在SEAndroid中,我們通常將用來(lái)標(biāo)注文件的安全上下文中的類型稱為file_type,而用來(lái)標(biāo)注進(jìn)程的安全上下文的類型稱為domain,并且每一個(gè)用來(lái)描述文件安全上下文的類型都將file_type設(shè)置為其屬性,每一個(gè)用來(lái)進(jìn)程安全上下文的類型都將domain設(shè)置為其屬性。

將一個(gè)類型設(shè)置為另一個(gè)類型的屬性可以通過type語(yǔ)句實(shí)現(xiàn)。例如,我們前面提到的用來(lái)描述進(jìn)程init的安全策略的文件external/sepolicy/init.te,就使用以下的type語(yǔ)句來(lái)將類型 domain設(shè)置類型init的屬性:

  1. type init domain;   

這樣就可以表明init描述的類型是用來(lái)描述進(jìn)程的安全上下文的。

        同樣,如果我們查看另外一個(gè)文件external/sepolicy/file.te,可以看到App數(shù)據(jù)文件的類型聲明:

  1. type app_data_file, file_type, data_file_type; 

上述語(yǔ)句表明類型app_data_file具有屬笥file_type,即它是用來(lái)描述文件的安全上下文的。

        了解了SEAndroid安全機(jī)制的安全上下文之后,我們就可以繼續(xù)Android系統(tǒng)中的對(duì)象的安全上下文是如何定義的了。這里我們只討論四種類型的對(duì) 象的安全上下文,分別是App進(jìn)程、App數(shù)據(jù)文件、系統(tǒng)文件和系統(tǒng)屬性。這四種類型對(duì)象的安全上下文通過四個(gè)文件來(lái)描 述:mac_permissions.xml、seapp_contexts、file_contexts和property_contexts,它們均 位于external/sepolicy目錄中。

        文件external/sepolicy/mac_permissions.xml的內(nèi)容如下所示:

  1. ?xml version="1.0" encoding="utf-8"?>   
  2. <policy>   
  3.    
  4.     <!-- Platform dev key in AOSP -->   
  5.     <signer signature="@PLATFORM" >   
  6.       <seinfo value="platform" />   
  7.     </signer>   
  8.    
  9.     <!-- Media dev key in AOSP -->   
  10.     <signer signature="@MEDIA" >   
  11.       <seinfo value="media" />   
  12.     </signer>   
  13.    
  14.     <!-- shared dev key in AOSP -->   
  15.     <signer signature="@SHARED" >   
  16.       <seinfo value="shared" />   
  17.     </signer>   
  18.    
  19.     <!-- release dev key in AOSP -->   
  20.     <signer signature="@RELEASE" >   
  21.       <seinfo value="release" />   
  22.     </signer>   
  23.    
  24.     <!-- All other keys -->   
  25.     <default>   
  26.       <seinfo value="default" />   
  27.     </default>   
  28.    
  29. </policy>   

文件mac_permissions.xml給不同簽名的App分配不同的seinfo字符串,例如,在AOSP源碼環(huán)境下編譯并且使用平臺(tái)簽名的App獲得的seinfo為“platform”,使用第三方簽名安裝的App獲得的seinfo簽名為"default"。

        這個(gè)seinfo描述的是其實(shí)并不是安全上下文中的Type,它是用來(lái)在另外一個(gè)文件external/sepolicy/seapp_contexts 中查找對(duì)應(yīng)的Type的。文件external/sepolicy/seapp_contexts的內(nèi)容如下所示:

  1. # Input selectors:    
  2. #   isSystemServer (boolean)   
  3. #   user (string)   
  4. #   seinfo (string)   
  5. #   name (string)   
  6. #   sebool (string)   
  7. # isSystemServer=true can only be used once.   
  8. # An unspecified isSystemServer defaults to false.   
  9. # An unspecified string selector will match any value.   
  10. # A user string selector that ends in * will perform a prefix match.   
  11. # user=_app will match any regular app UID.   
  12. # user=_isolated will match any isolated service UID.   
  13. # All specified input selectors in an entry must match (i.e. logical AND).   
  14. # Matching is case-insensitive.   
  15. # Precedence rules:   
  16. #     (1) isSystemServer=true before isSystemServer=false.   
  17. #     (2) Specified user= string before unspecified user= string.   
  18. #     (3) Fixed user= string before user= prefix (i.e. ending in *).   
  19. #     (4) Longer user= prefix before shorter user= prefix.    
  20. #     (5) Specified seinfo= string before unspecified seinfo= string.   
  21. #     (6) Specified name= string before unspecified name= string.   
  22. #     (7) Specified sebool= string before unspecified sebool= string.   
  23. #   
  24. # Outputs:   
  25. #   domain (string)   
  26. #   type (string)   
  27. #   levelFrom (string; one of none, all, app, or user)   
  28. #   level (string)   
  29. # Only entries that specify domain= will be used for app process labeling.   
  30. # Only entries that specify type= will be used for app directory labeling.   
  31. # levelFrom=user is only supported for _app or _isolated UIDs.   
  32. # levelFrom=app or levelFrom=all is only supported for _app UIDs.   
  33. # level may be used to specify a fixed level for any UID.    
  34. #   
  35. isSystemServer=true domain=system   
  36. user=system domain=system_app type=system_data_file   
  37. user=bluetooth domain=bluetooth type=bluetooth_data_file   
  38. user=nfc domain=nfc type=nfc_data_file   
  39. user=radio domain=radio type=radio_data_file   
  40. user=_app domain=untrusted_app type=app_data_file levelFrom=none   
  41. user=_app seinfo=platform domain=platform_app type=platform_app_data_file   
  42. user=_app seinfo=shared domain=shared_app type=platform_app_data_file   
  43. user=_app seinfo=media domain=media_app type=platform_app_data_file   
  44. user=_app seinfo=release domain=release_app type=platform_app_data_file   
  45. user=_isolated domain=isolated_app   

文件中的注釋解釋了如何在文件seapp_contexts查找對(duì)象的Type,這里不再累述,只是舉兩個(gè)例子來(lái)說(shuō)明。

       從前面的分析可知,對(duì)于使用平臺(tái)簽名的App來(lái)說(shuō),它的seinfo為“platform”。用戶空間的Security Server在為它查找對(duì)應(yīng)的Type時(shí),使用的user輸入為"_app"。這樣在seapp_contexts文件中,與它匹配的一行即為:

  1. user=_app seinfo=platform domain=platform_app type=platform_app_data_file   

這樣我們就可以知道,使用平臺(tái)簽名的App所運(yùn)行在的進(jìn)程domain為“platform_app”,并且它的數(shù)據(jù)文件的file_type為“platform_app_data_file”。

       又如,使用第三方簽名的App的seinfo為“default”。用戶空間的Security Server在為它查找對(duì)應(yīng)的Type時(shí),使用的user輸入也為"_app"。我們注意到,在seapp_contexts文件中,沒有一行對(duì)應(yīng)的 user和seinfo分別為“_app”和“default”。但是有一行是最匹配的,即:

  1. user=_app domain=untrusted_app type=app_data_file levelFrom=none   

這樣我們就可以知道,使用第三方簽名的App所運(yùn)行在的進(jìn)程domain為“unstrusted_app”,并且它的數(shù)據(jù)文件的file_type為“app_data_file”。

       接下來(lái)我們?cè)賮?lái)看系統(tǒng)文件的安全上下文是如何定義的。通過查看external/sepolicy/file_contexts文件,我們就可以看到系統(tǒng)文件的安全上下文描述,如下所示:

  1. ###########################################   
  2. # Root   
  3. /           u:object_r:rootfs:s0   
  4.    
  5. # Data files   
  6. /adb_keys       u:object_r:rootfs:s0   
  7. /default.prop       u:object_r:rootfs:s0   
  8. /fstab\..*      u:object_r:rootfs:s0   
  9. /init\..*       u:object_r:rootfs:s0   
  10. /res(/.*)?      u:object_r:rootfs:s0   
  11. /ueventd\..*        u:object_r:rootfs:s0   
  12.    
  13. # Executables   
  14. /charger        u:object_r:rootfs:s0   
  15. /init           u:object_r:rootfs:s0   
  16. /sbin(/.*)?     u:object_r:rootfs:s0   
  17.    
  18. ......   
  19.    
  20. #############################   
  21. # System files   
  22. #   
  23. /system(/.*)?       u:object_r:system_file:s0   
  24. /system/bin/ash     u:object_r:shell_exec:s0   
  25. /system/bin/mksh    u:object_r:shell_exec:s0   
  26.    
  27. ......   

文件file_contexts通過正則表達(dá)式來(lái)描述系統(tǒng)文件的安全上下文。例如,在上面列出的內(nèi)容的最后三行中,倒數(shù)第三行的正則表達(dá)式表示在 /system目錄下的所有文件的安全上下文均為“u:object_r:system_file:s0”,最后兩行的正則表達(dá)式則表示文件 /system/bin/ash和/system/bin/mksh的安全上下文應(yīng)為“u:object_r:shell_exec:s0”。雖然倒數(shù)第 三行的正則表達(dá)式描述的文件涵蓋后面兩個(gè)正則表達(dá)示描述的文件,但是后面兩個(gè)正則表達(dá)式描述的方式更加具體,因此/system/bin/ash和 /system/bin/mksh兩個(gè)文件的最終安全上下文都被設(shè)置為“u:object_r:shell_exec:s0”。

        在Android系統(tǒng)中,有一種特殊的資源——屬性,App通過讀寫它們能夠獲得相應(yīng)的信息,以及控制系統(tǒng)的行為,因此,SEAndroid也需要對(duì)它們 進(jìn)行保護(hù)。這意味著Android系統(tǒng)的屬性也需要關(guān)聯(lián)有安全上下文。這是通過文件external/sepolicy /property_contexts來(lái)描述的,它的內(nèi)容如下所示:

  1. ##########################   
  2. # property service keys   
  3. #   
  4. #   
  5. net.rmnet0              u:object_r:radio_prop:s0   
  6. net.gprs                u:object_r:radio_prop:s0   
  7. net.ppp                 u:object_r:radio_prop:s0   
  8. net.qmi                 u:object_r:radio_prop:s0   
  9. net.lte                 u:object_r:radio_prop:s0   
  10. net.cdma                u:object_r:radio_prop:s0   
  11. gsm.                    u:object_r:radio_prop:s0   
  12. persist.radio           u:object_r:radio_prop:s0   
  13. net.dns                 u:object_r:radio_prop:s0   
  14. sys.usb.config          u:object_r:radio_prop:s0   
  15. ......   

屬性的安全上下文與文件的安全上下文是類似的,它們的SELinux用戶、SELinux角色和安全級(jí)別均定義為u、object_r和s0。從上面列出 的內(nèi)容可以看出,以net.開頭的幾個(gè)屬性,以及所有以gsm.開頭的屬性、persist.radio和sys.usb.config屬性的安全上下文 均被設(shè)置為”u:object_r:radio_prop:s0“。這意味著只有有權(quán)限訪問Type為radio_prop的資源的進(jìn)程才可以訪問這些屬 性。

       #p#

2. 安全策略

       上面我們分析了SEAndroid安全機(jī)制中的對(duì)象安全上下文,接下來(lái)我們就繼續(xù)分析SEAndroid安全機(jī)制中的安全策略。SEAndroid安全機(jī) 制中的安全策略是在安全上下文的基礎(chǔ)上進(jìn)行描述的,也就是說(shuō),它通過主體和客體的安全上下文,定義主體是否有權(quán)限訪問客體。

       前面提到,SEAndroid安全機(jī)制主要是使用對(duì)象安全上下文中的類型來(lái)定義安全策略,這種安全策略就稱Type Enforcement,簡(jiǎn)稱TE。在external/sepolicy目錄中,所有以.te為后綴的文件經(jīng)過編譯之后,就會(huì)生成一個(gè)sepolicy 文件。這個(gè)sepolicy文件會(huì)打包在ROM中,并且保存在設(shè)備上的根目錄下,即它在設(shè)備上的路徑為/sepolicy。

       接下來(lái),我們就通過app.te文件的內(nèi)容來(lái)分析SEAndroid安全機(jī)制為使使用平臺(tái)簽名的App所定義的安全策略,相關(guān)的內(nèi)容如下所示:

  1. #   
  2. # Apps signed with the platform key.   
  3. #   
  4. type platform_app, domain;   
  5. permissive platform_app;   
  6. app_domain(platform_app)   
  7. platform_app_domain(platform_app)   
  8. # Access the network.   
  9. net_domain(platform_app)   
  10. # Access bluetooth.   
  11. bluetooth_domain(platform_app)   
  12. unconfined_domain(platform_app)   
  13. ......   

前面在分析seapp_contexts文件的時(shí)候,我們提到,使用平臺(tái)簽名的App所運(yùn)行在的進(jìn)程的domain指定為"platform_app"。 從上面列出的內(nèi)容可以看出,platform_app接下來(lái)會(huì)通過app_domain、platform_app_domain、 net_domain、bluetooth_domain和unconfined_domain宏分別加入到其它的domain中去,以便可以獲得相應(yīng)的 權(quán)限。接下來(lái)我們就以u(píng)nconfined_domain宏為例,分析platform_app獲得了哪些權(quán)限。 

       宏unconfined_domain定義在文件te_macros文件中,如下所示:

  1. ......   
  2.    
  3. #####################################   
  4. # unconfined_domain(domain)   
  5. # Allow the specified domain to do anything.   
  6. #   
  7. define(`unconfined_domain', `   
  8. typeattribute $1 mlstrustedsubject;   
  9. typeattribute $1 unconfineddomain;   
  10. ')   
  11.    
  12. ......   

$1引用的就是unconfined_domain的參數(shù),即platform_app。通過接下來(lái)的兩個(gè)typeattribute語(yǔ)句,為 platform_app設(shè)置了mlstrustedsubject和unconfineddomain兩個(gè)屬性。也就是 說(shuō),mlstrustedsubject和unconfineddomain這兩個(gè)Type具有權(quán)限,platform_app這個(gè)Type也具有。接下 來(lái)我們主要分析unconfineddomain這個(gè)Type具有哪些權(quán)限。

       文件unconfined.te定義了unconfineddomain這個(gè)Type所具有的權(quán)限,如下所示:

  1. allow unconfineddomain self:capability_class_set *;   
  2. allow unconfineddomain kernel:security *;   
  3. allow unconfineddomain kernel:system *;   
  4. allow unconfineddomain self:memprotect *;   
  5. allow unconfineddomain domain:process *;   
  6. allow unconfineddomain domain:fd *;   
  7. allow unconfineddomain domain:dir r_dir_perms;   
  8. allow unconfineddomain domain:lnk_file r_file_perms;   
  9. allow unconfineddomain domain:{ fifo_file file } rw_file_perms;   
  10. allow unconfineddomain domain:socket_class_set *;   
  11. allow unconfineddomain domain:ipc_class_set *;   
  12. allow unconfineddomain domain:key *;   
  13. allow unconfineddomain fs_type:filesystem *;   
  14. allow unconfineddomain {fs_type dev_type file_type}:{ dir blk_file lnk_file sock_file fifo_file } *;   
  15. allow unconfineddomain {fs_type dev_type file_type}:{ chr_file file } ~entrypoint;   
  16. allow unconfineddomain node_type:node *;   
  17. allow unconfineddomain node_type:{ tcp_socket udp_socket rawip_socket } node_bind;   
  18. allow unconfineddomain netif_type:netif *;   
  19. allow unconfineddomain port_type:socket_class_set name_bind;   
  20. allow unconfineddomain port_type:{ tcp_socket dccp_socket } name_connect;   
  21. allow unconfineddomain domain:peer recv;   
  22. allow unconfineddomain domain:binder { call transfer set_context_mgr };   
  23. allow unconfineddomain property_type:property_service set;   

一個(gè)Type所具有的權(quán)限是通過allow語(yǔ)句來(lái)描述的,以下這個(gè)allow語(yǔ)句:

  1. allow unconfineddomain domain:binder { call transfer set_context_mgr };   

  表明domain為unconfineddomain的進(jìn)程可以與其它進(jìn)程進(jìn)行binder ipc通信(call),并且能夠向這些進(jìn)程傳遞Binder對(duì)象(transfer),以及將自己設(shè)置為Binder上下文管理器(set_context_mgr)。

        注意,SEAndroid使用的是最小權(quán)限原則,也就是說(shuō),只有通過allow語(yǔ)句聲明的權(quán)限才是允許的,而其它沒有通過allow語(yǔ)句聲明的權(quán)限都是禁止,這樣就可以最大限度地保護(hù)系統(tǒng)中的資源。

        如果我們繼續(xù)分析app.te的內(nèi)容,會(huì)發(fā)現(xiàn)使用第三方簽名的App所運(yùn)行在的進(jìn)程同樣是加入到unconfineddomain這個(gè)domain的,如下所示:

  1. #   
  2. # Untrusted apps.   
  3. #   
  4. type untrusted_app, domain;   
  5. permissive untrusted_app;   
  6. app_domain(untrusted_app)   
  7. net_domain(untrusted_app)   
  8. bluetooth_domain(untrusted_app)   
  9. unconfined_domain(untrusted_app)   

這是不是意味著使用平臺(tái)簽名和第三方簽名的App所具有的權(quán)限都是一樣的呢?答案是否定的。雖然使用平臺(tái)簽名和第三方簽名的App在SEAndroid安 全框架的約束下都具有unconfineddomain這個(gè)domain所賦予的權(quán)限,但是別忘記,在進(jìn)行SEAndroid安全檢查之前,使用平臺(tái)簽名 和第三方簽名的App首先要通過DAC檢查,也就是要通過傳統(tǒng)的Linux UID/GID安全檢查。由于使用平臺(tái)簽名和第三方簽名的App在安裝的時(shí)候分配到的Linux UID/GID是不一樣的,因此就決定了它們所具有權(quán)限是不一樣的。

        同時(shí),這里使用平臺(tái)簽名和第三方簽名的App之所以會(huì)同時(shí)被賦予unconfineddomain這個(gè)domain的權(quán)限,是因?yàn)榍懊嫖覀兎治龅?app.te文件是來(lái)自于Android 4.3的。在Android 4.3中,SEAndroid安全機(jī)制是試驗(yàn)性質(zhì)的,并且啟用的是Permissive模式,也就是即使主體違反了安全策略,也只是會(huì)發(fā)出警告,而不會(huì)真 的拒絕執(zhí)行。如果我們分析的是Android 4.4的app.te文件,就會(huì)發(fā)現(xiàn),使用第三方簽名的App不再具有大部分unconfineddomain這個(gè)domain的權(quán)限,因?yàn)?Android 4.4的SEAndroid安全機(jī)制不再是試驗(yàn)性質(zhì)的,并且啟用的Enforcing模式。

       以上描述的就是基于TE的安全策略,它的核心思想就是最小權(quán)限原則,即主體對(duì)客體擁有的權(quán)限必須要通過allow語(yǔ)句定義才允許,否則的話,一切都是禁止的。

       前面我們還提到,SEAndroid安全機(jī)制的安全策略經(jīng)過編譯后會(huì)得到一個(gè)sepolicy文件,并且最終保存在設(shè)備上的根據(jù)目錄下。注意,如果我們什 么也不做,那么保存在這個(gè)sepolicy文件中的安全策略是不會(huì)自動(dòng)加載到內(nèi)核空間的SELinux LSM模塊去的。它需要我們?cè)谙到y(tǒng)啟動(dòng)的過程中進(jìn)行加載。

        系統(tǒng)中第一個(gè)啟動(dòng)的進(jìn)程是init進(jìn)程。我們知道,Init進(jìn)程在啟動(dòng)的過程中,執(zhí)行了很多的系統(tǒng)初始化工作,其中就包括加載SEAndroid安全策略的工作,如下所示:

  1. int main(int argc, char **argv)   
  2. {   
  3.     ......   
  4.    
  5.     union selinux_callback cb;   
  6.     cb.func_log = klog_write;   
  7.     selinux_set_callback(SELINUX_CB_LOG, cb);   
  8.    
  9.     cb.func_audit = audit_callback;   
  10.     selinux_set_callback(SELINUX_CB_AUDIT, cb);   
  11.    
  12.     INFO("loading selinux policy\n");   
  13.     if (selinux_enabled) {   
  14.         if (selinux_android_load_policy() < 0) {   
  15.             selinux_enabled = 0;   
  16.             INFO("SELinux: Disabled due to failed policy load\n");   
  17.         } else {   
  18.             selinux_init_all_handles();   
  19.         }   
  20.     } else {   
  21.         INFO("SELinux:  Disabled by command line option\n");   
  22.     }   
  23.    
  24.     ......   
  25. }   

上述代碼定義在文件system/core/init/init.c中。

        這里調(diào)用到了三個(gè)與SEAndroid相關(guān)的函數(shù):selinux_set_callback、selinux_android_load_policy 和selinux_init_all_handles,其中,selinux_set_callback和 selinux_android_load_policy來(lái)自于libselinux,而selinux_init_all_handles也是定義在文 件system/core/init/init.c中,并且它最終也是通過調(diào)用libselinux的函數(shù)來(lái)打開前面分析file_contexts和 property_contexts文件,以便可以用來(lái)查詢系統(tǒng)文件和系統(tǒng)屬性的安全上下文。

        函數(shù)selinux_set_callback用來(lái)向libselinux設(shè)置SEAndroid日志和審計(jì)回調(diào)函數(shù),而函數(shù) selinux_android_load_policy則是用來(lái)加載安全策略到內(nèi)核空間的SELinux LSM模塊中去。我們重點(diǎn)關(guān)注函數(shù)selinux_android_load_policy的實(shí)現(xiàn)。

        函數(shù)selinux_android_load_policy定義在文件external/libselinux/src/android.c,它的實(shí)現(xiàn)如下所示:

  1. nt selinux_android_load_policy(void)   
  2. {   
  3.     char *mnt = SELINUXMNT;   
  4.     int rc;   
  5.     rc = mount(SELINUXFS, mnt, SELINUXFS, 0, NULL);   
  6.     if (rc < 0) {   
  7.         if (errno == ENODEV) {   
  8.             /* SELinux not enabled in kernel */   
  9.             return -1;   
  10.         }   
  11.         if (errno == ENOENT) {   
  12.             /* Fall back to legacy mountpoint. */   
  13.             mnt = OLDSELINUXMNT;   
  14.             rc = mkdir(mnt, 0755);   
  15.             if (rc == -1 && errno != EEXIST) {   
  16.                 selinux_log(SELINUX_ERROR,"SELinux:  Could not mkdir:  %s\n",   
  17.                     strerror(errno));   
  18.                 return -1;   
  19.             }   
  20.             rc = mount(SELINUXFS, mnt, SELINUXFS, 0, NULL);   
  21.         }   
  22.     }   
  23.     if (rc < 0) {   
  24.         selinux_log(SELINUX_ERROR,"SELinux:  Could not mount selinuxfs:  %s\n",   
  25.                 strerror(errno));   
  26.         return -1;   
  27.     }   
  28.     set_selinuxmnt(mnt);   
  29.    
  30.     return selinux_android_reload_policy();   
  31. }   

SELINUXMNT、OLDSELINUXMNT和SELINUXFS是三個(gè)宏,它們定義在文件external/libselinux/src/policy.h文件中,如下所示:

  1. /* Preferred selinuxfs mount point directory paths. */   
  2. #define SELINUXMNT "/sys/fs/selinux"   
  3. #define OLDSELINUXMNT "/selinux"   
  4.    
  5. /* selinuxfs filesystem type string. */   
  6. #define SELINUXFS "selinuxfs"   

   回到函數(shù)selinux_android_load_policy中,我們不難發(fā)現(xiàn)它的實(shí)現(xiàn)邏輯如下所示:

        A. 以/sys/fs/selinux為安裝點(diǎn),安裝一個(gè)類型為selinuxfs的文件系統(tǒng),也就是SELinux文件系統(tǒng),用來(lái)與內(nèi)核空間的SELinux LSM模塊通信。

        B. 如果不能在/sys/fs/selinux這個(gè)安裝點(diǎn)安裝SELinux文件系統(tǒng),那么再以/selinux為安裝點(diǎn),安裝SELinux文件系統(tǒng)。

        C. 成功安裝SELinux文件系統(tǒng)之后,接下來(lái)就調(diào)用另外一個(gè)函數(shù)selinux_android_reload_policy來(lái)將SEAndroid安全策略加載到內(nèi)核空間的SELinux LSM模塊中去。

        在較舊版本的Linux系統(tǒng)中,SELinux文件系統(tǒng)是以/selinux為安裝點(diǎn)的,不過后面較新的版本都是以/sys/fs/selinux為安裝點(diǎn)的,Android系統(tǒng)使用的是后者。

        函數(shù)selinux_android_reload_policy也是定義在文件external/libselinux/src/android.c中,它的實(shí)現(xiàn)如下所示:

  1. tatic const char *const sepolicy_file[] = {   
  2.         "/data/security/current/sepolicy",   
  3.         "/sepolicy",   
  4.         0 };   
  5.    
  6. ......   
  7.    
  8. int selinux_android_reload_policy(void)   
  9. {   
  10.     int fd = -1, rc;   
  11.     struct stat sb;   
  12.     void *map = NULL;   
  13.     int i = 0;   
  14.    
  15.     while (fd < 0 && sepolicy_file[i]) {   
  16.         fd = open(sepolicy_file[i], O_RDONLY | O_NOFOLLOW);   
  17.         i++;   
  18.     }   
  19.     if (fd < 0) {   
  20.         selinux_log(SELINUX_ERROR, "SELinux:  Could not open sepolicy:  %s\n",   
  21.                 strerror(errno));   
  22.         return -1;   
  23.     }   
  24.     if (fstat(fd, &sb) < 0) {   
  25.         selinux_log(SELINUX_ERROR, "SELinux:  Could not stat %s:  %s\n",   
  26.                 sepolicy_file[i], strerror(errno));   
  27.         close(fd);   
  28.         return -1;   
  29.     }   
  30.     map = mmap(NULL, sb.st_size, PROT_READ, MAP_PRIVATE, fd, 0);   
  31.     if (map == MAP_FAILED) {   
  32.         selinux_log(SELINUX_ERROR, "SELinux:  Could not map %s:  %s\n",   
  33.             sepolicy_file[i], strerror(errno));   
  34.         close(fd);   
  35.         return -1;   
  36.     }   
  37.    
  38.     rc = security_load_policy(map, sb.st_size);   
  39.     if (rc < 0) {   
  40.         selinux_log(SELINUX_ERROR, "SELinux:  Could not load policy:  %s\n",   
  41.             strerror(errno));   
  42.         munmap(map, sb.st_size);   
  43.         close(fd);   
  44.         return -1;   
  45.     }   
  46.    
  47.     munmap(map, sb.st_size);   
  48.     close(fd);   
  49.     selinux_log(SELINUX_INFO, "SELinux: Loaded policy from %s\n", sepolicy_file[i]);   
  50.    
  51.     return 0;   
  52. }   

函數(shù)selinux_android_reload_policy的執(zhí)行過程如下所示:

         A. 依次從/data/security/current和根目錄尋找sepolicy文件,找到之后就打開,獲得一個(gè)文件描述符fd。

         B. 通過文件描述符fd將前面打開的sepolicy文件的內(nèi)容映射到內(nèi)存中來(lái),并且得到它的起始地址為map。

         C. 調(diào)用另外一個(gè)函數(shù)security_load_policy將已經(jīng)映射到內(nèi)存中的sepolicy文件內(nèi)容,即SEAndroid安全策略,加載到內(nèi)核空間的SELinux LSM模塊中去。

         D. 加載完成后,釋放sepolicy文件占用的內(nèi)存,并且關(guān)閉sepolicy文件。

         函數(shù)security_load_policy定義在文件external/libselinux/src/load_policy.c中,它的實(shí)現(xiàn)如下所

 

  1. nt security_load_policy(void *data, size_t len)   
  2. {   
  3.     char path[PATH_MAX];   
  4.     int fd, ret;   
  5.    
  6.     if (!selinux_mnt) {N   
  7.         errno = ENOENT;   
  8.         return -1;   
  9.     }   
  10.    
  11.     snprintf(path, sizeof path, "%s/load", selinux_mnt);   
  12.     fd = open(path, O_RDWR);   
  13.     if (fd < 0)   
  14.         return -1;   
  15.    
  16.     ret = write(fd, data, len);   
  17.     close(fd);   
  18.     if (ret < 0)   
  19.         return -1;   
  20.     return 0;   
  21. }   

   selinux_mnt是一個(gè)全局變量,它描述的是SELinux文件系統(tǒng)的安裝點(diǎn)。在我們這個(gè)情景中,它的值就等于/sys/fs/selinux。

         函數(shù)security_load_policy的實(shí)現(xiàn)很簡(jiǎn)單,它首先打/sys/fs/selinux/load文件,然后將參數(shù)data所描述的安全策 略寫入到這個(gè)文件中去。由于/sys/fs/selinux是由內(nèi)核空間的SELinux LSM模塊導(dǎo)出來(lái)的文件系統(tǒng)接口,因此當(dāng)我們將安全策略寫入到位于該文件系統(tǒng)中的load文件時(shí),就相當(dāng)于是將安全策略從用戶空間加載到SELinux LSM模塊中去了。以后SELinux LSM模塊中的Security Server就可以通過它來(lái)進(jìn)行安全檢查。

         #p#

3. Security Server

         用戶空間的Security Server主要是用來(lái)保護(hù)用戶空間資源的,以及用來(lái)操作內(nèi)核空間對(duì)象的安全上下文的,它由應(yīng)用程序安裝服務(wù) PackageManagerService、應(yīng)用程序安裝守護(hù)進(jìn)程installd、應(yīng)用程序進(jìn)程孵化器Zygote進(jìn)程以及init進(jìn)程組成。其 中,PackageManagerService和installd負(fù)責(zé)創(chuàng)建App數(shù)據(jù)目錄的安全上下文,Zygote進(jìn)程負(fù)責(zé)創(chuàng)建App進(jìn)程的安全上下 文,而init進(jìn)程負(fù)責(zé)控制系統(tǒng)屬性的安全訪問。

         應(yīng)用程序安裝服務(wù)PackageManagerService在啟動(dòng)的時(shí)候,會(huì)在/etc/security目錄中找到我們前面分析的 mac_permissions.xml文件,然后對(duì)它進(jìn)行解析,得到App簽名或者包名與seinfo的對(duì)應(yīng)關(guān)系。當(dāng) PackageManagerService安裝App的時(shí)候,它就會(huì)根據(jù)其簽名或者包名查找到對(duì)應(yīng)的seinfo,并且將這個(gè)seinfo傳遞給另外一 個(gè)守護(hù)進(jìn)程installed。

         守護(hù)進(jìn)程installd負(fù)責(zé)創(chuàng)建App數(shù)據(jù)目錄。在創(chuàng)建App數(shù)據(jù)目錄的時(shí)候,需要給它設(shè)置安全上下文,使得SEAndroid安全機(jī)制可以對(duì)它進(jìn)行安 全訪問控制。Installd根據(jù)PackageManagerService傳遞過來(lái)的seinfo,并且調(diào)用libselinux庫(kù)提供的 selabel_lookup函數(shù)到前面我們分析的seapp_contexts文件中查找到對(duì)應(yīng)的Type。有了這個(gè)Type之后,installd就 可以給正在安裝的App的數(shù)據(jù)目錄設(shè)置安全上下文了,這是通過調(diào)用libselinux庫(kù)提供的lsetfilecon函數(shù)來(lái)實(shí)現(xiàn)的。

        從前面Android應(yīng)用程序進(jìn)程啟動(dòng)過程的源代碼分析和Android系統(tǒng)進(jìn)程Zygote啟動(dòng)過程的源代碼分析這 兩篇文章可以知道,在Android系統(tǒng)中,Zygote進(jìn)程負(fù)責(zé)創(chuàng)建應(yīng)用程序進(jìn)程。應(yīng)用程序進(jìn)程是SEAndroid安全機(jī)制中的主體,因此它們也需要 設(shè)置安全上下文,這是由Zygote進(jìn)程來(lái)設(shè)置的。組件管理服務(wù)ActivityManagerService在請(qǐng)求Zygote進(jìn)程創(chuàng)建應(yīng)用程序進(jìn)程之 前,會(huì)到PackageManagerService中去查詢對(duì)應(yīng)的seinfo,并且將這個(gè)seinfo傳遞到Zygote進(jìn)程。于是,Zygote進(jìn) 程在fork一個(gè)應(yīng)用程序進(jìn)程之后,就會(huì)使用ActivityManagerService傳遞過來(lái)的seinfo,并且調(diào)用libselinux庫(kù)提供 的selabel_lookup函數(shù)到前面我們分析的seapp_contexts文件中查找到對(duì)應(yīng)的Domain。有了這個(gè)Domain之 后,Zygote進(jìn)程就可以給剛才創(chuàng)建的應(yīng)用程序進(jìn)程設(shè)置安全上下文了,這是通過調(diào)用libselinux庫(kù)提供的lsetcon函數(shù)來(lái)實(shí)現(xiàn)的。

        前面提到,在Android系統(tǒng)中,屬性也是一項(xiàng)需要保護(hù)的資源。Init進(jìn)程在啟動(dòng)的時(shí)候,會(huì)創(chuàng)建一塊內(nèi)存區(qū)域來(lái)維護(hù)系統(tǒng)中的屬性,接著還會(huì)創(chuàng)建一個(gè) Property服務(wù)。這個(gè)Property服務(wù)通過socket提供接口給其它進(jìn)程訪問Android系統(tǒng)中的屬性。其它進(jìn)程通過socket來(lái)和 Property服務(wù)通信時(shí),Property服務(wù)可以獲得它的安全上下文。有了這個(gè)安全上下文之后,Property服務(wù)就可以通過 libselinux庫(kù)提供的selabel_lookup函數(shù)到前面我們分析的property_contexts去查找要訪問的屬性的安全上下文了。 有了這兩個(gè)安全上下文之后,Property服務(wù)就可以決定是否允許一個(gè)進(jìn)程訪問它所指定的屬性了。

本文鏈接:http://blog.csdn.net/luoshengyang/article/details/37613135

責(zé)任編輯:chenqingxiang 來(lái)源: blog.csdn
相關(guān)推薦

2015-03-02 14:00:54

2017-02-14 13:08:45

2011-05-20 14:23:59

WLANWEPWPA

2009-05-19 15:01:12

WLANWEPWAPI

2015-05-15 10:36:13

2014-08-01 10:20:05

2020-11-25 11:00:50

物聯(lián)網(wǎng)RFID網(wǎng)絡(luò)安全

2021-11-11 17:40:08

WatchdogAndroid源碼分析

2009-07-03 18:59:02

2009-06-12 14:28:14

WCF傳輸安全

2020-04-02 15:10:57

Kubernetes集群安全

2016-10-19 14:26:48

Firefox安全附加組件

2023-10-31 16:00:51

類加載機(jī)制Java

2020-11-18 09:37:44

微服務(wù)

2011-08-24 16:59:59

LuaModule

2019-11-21 13:45:03

Web安全漏洞滲透測(cè)試

2009-06-19 09:52:46

Acegi安全框架Spring框架

2021-03-15 13:50:24

網(wǎng)絡(luò)安全Android安全機(jī)制

2025-05-22 09:15:09

2009-07-16 17:01:32

ibatis dao
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)

主站蜘蛛池模板: 欧美一区二区三区在线 | 久久久免费少妇高潮毛片 | 337p日韩 | 九色国产| 欧美aaaaaaaaaa | 欧美aⅴ片 | 久久99国产精一区二区三区 | av午夜激情 | 992tv人人草 久久精品超碰 | 妞干网视频 | 成人三级网址 | www.99热.com | 亚洲精品久久久久中文字幕欢迎你 | 天堂一区 | 精品二区视频 | 国产精品久久久久久久久久久久久久 | 欧美日韩在线观看一区二区三区 | 天天夜夜人人 | 亚洲国产精品久久久 | 欧美午夜一区二区三区免费大片 | 天天色天天色 | 一区二区久久精品 | 精品久久久久久久久久久下田 | 国产在线观 | 精品乱码一区二区三四区 | 日本午夜网站 | 一区二区三区在线免费看 | 毛片入口| 午夜视频一区二区三区 | 99免费精品 | 亚洲av毛片成人精品 | 成人一区二区三区 | 国产免费va | 国产乱码精品1区2区3区 | 亚洲一区二区日韩 | 美国一级黄色片 | 国产亚洲精品综合一区 | 中文字幕av在线一二三区 | 日本不卡视频在线播放 | 99精品欧美一区二区三区 | 8x国产精品视频一区二区 |