在以上幾篇部落格中,我已經闡述了有關Smack技術的一些知識和要點,但是沒有把Smack最精華的部分展示出來,由於本人學疏才淺,在闡述的過程中不免有這樣那樣的問題和詬病,但本人還是儘力把問題說清楚,在這篇文章中,我將盡我所能把Smack的核心機制進行闡述,如果涉及的內容有錯誤,還請梁老師和各位批判指正。
一 Smack是如何為主體和客體打上標籤,又是如何擷取它們的標籤?
Smack的主體是任務,Smack稱之為task,客體是檔案,通訊端,訊息,IPC等,Smack為主體和客體打上和擷取標籤可以分為Smack核心空間和Smack使用者空間兩部分。
1. Smack核心空間:
為task打上標籤: 我們知道Smack利用了LSM機制,LSM是要改造核心對象,為其添加安全域,對於任務而言,它的結構體task_struct裡有一個成員是cred型,而cred結構體中有一項是void *security,這正是task的安全域,它指向了task_smack這個結構體,這個結構體裡的成員就是父進程和子進程的安全性標籤,smack_lsm.c中的代碼static struct task_smack *new_task_smack(char *task, char *forked, gfp_t gfp)是用來設定task_smack的父子進程smack標籤,參數task指向父進程smack標籤,forked指向子進程的smack標籤,task_struct是以Linux核心鏈表形式組織起來,當設定完進程的安全性標籤後,該函數會初始化task_smack鏈表。
擷取task的安全性標籤:在smack_lsm.c中,有一個很重要的宏--task_security(task),它的作用是擷取進程的安全域,即void *security所指向的task_smack結構體,而在smack.h中有一個靜態函數smk_of_current(),它用來擷取當前進程的smack標籤。
為inode打上標籤:inode結構體中安全域是指向了inode_smack結構體,它不僅包含了檔案對象的smack標籤,而且包含了建立該inode的進程的smack標籤,在smack_lsm.c中,鉤子函數smack_inode_post_setxattr為inode打上標籤,但是要注意只有以超級使用者運行程式,才能在使用者空間將檔案打上Smack標籤,因為像setxattr(設定安全性標籤)或者xattr這樣的函數或命令都被安插了Smack的鉤子smack_inode_setxattr,而此鉤子會檢查當前啟動並執行程式有沒有超級使用者的許可權。
擷取inode標籤:smk_fetch, smk_of_inode, smack_inode_getsecurity用來擷取inode的smack標籤。注意,Smack標籤形式是鍵--值,鍵是XATTR_NAME_SMACK,XATTR_NAME_SMACKEXEC(進程標籤),XATTR_NAME_SMACKIPIN(接收包的標籤),XATTR_NAME_SMACKIPOUT(外發包的標籤)它們在Linux的源碼xattr.h中作為宏定義,而這裡的值是Smack標籤的值,即Smack的字串,鉤子函數一般是根據鍵(name)來擷取值(value)。
2. Smack使用者空間:
我們可以在/ect/accesses中寫下一行訪問規則--主體標籤 客體標籤 訪問方式,運行smackload工具,這樣這行規則便會被寫入Smack的虛擬檔案/smack/load中,這樣smackfs.c中代碼smk_write_load函數會設定相應的主體,客體標籤以及存取控制鏈表smack_rule。
通過工具getsmack和setsmack可以分別設定和擷取當前進程的Smack標籤,必須注意兩點:第一,設定進程Smack標籤的程式必須具備超級使用者的許可權,因為Smack的鉤子setprocattr會對系統調用進行檢查,第二,我們只能對當前啟動並執行進程設定和擷取標籤,也就是說當前啟動並執行進程只能擷取或者改變自己的標籤,這是由setprocattr和getprocattr兩個鉤子檢查的。在Linux的/proc目錄下有個self的子目錄,它指向當前正在運行進程,而self/attr/current裡面儲存著當前進程的Smack標籤,預設是"_",getsmack和setsmack就是對此進行操作。
所以,對於使用者來說,Smack給我們提供了很方便的介面,設定主客體安全性標籤由Smack在核心空間幫我們完成。
二 Smack是基於LSM機制,那麼Smack是如何?LSM?
像Smack,SELinux這樣的安全模組都是基於LSM做的,這也是Linux的鼻祖Torvalds所要求的,Smack要想實現存取控制,必須做到:
第一,重寫Smack的鉤子函數,實現對進程,檔案,通訊端,管道,訊息,IPC等控制;
第二,執行個體化結構體security_operations,讓LSM的鉤子指標指向Smack的鉤子函數;
第三,向LSM註冊Smack的安全模組,讓安全模組發揮效能;
第四,初始化安全調用。
三 Smack的存取控制的實現
在前面的例子中,我們以普通使用者運行setxattr函數對檔案設定Smack標籤,結果是操作無法進行,這其實是因為setxattr函數是一個系統調用,而此系統調用的原始碼又被安插了LSM鉤子,而LSM鉤子指標指向了Smack的鉤子函數,那麼Smack的鉤子函數就可以對上層的調用進行存取控制。Smack的存取控制可以分為幾類:
第一類,關於進程跟蹤ptrace
Smack鉤子函數要求當上層應用執行ptrace等進程跟蹤函數或命令時,當前進程對被跟蹤進程有讀和寫的許可權,如smack_ptrace_access_check和smack_ptrace_me;
第二類,關於超塊superblock
當上層應用執行擷取檔案狀態statfs函數時,Smack鉤子函數smack_sb_statfs要求當前進程對檔案有讀許可權,當上層應用執行mount或unmount進行檔案系統掛載或卸載操作時,Smack鉤子函數smack_sb_mount和smack_sb_unmount要求當前進程對檔案有寫入權限;
第三類,關於可執行程式linux_bprm
此類鉤子函數主要是為linux_bprm設定和提交憑證cred,如smack_bprm_set_creds和smack_bprm_committing_creds;
第四類, 關於索引結點inode
當上層應用要執行添加目錄,刪除目錄,變更檔名,設定和移除檔案擴充屬性等操作時,Smack鉤子函數要求當前進程對被操作的inode有寫入權限,具體鉤子函數是smack_inode_link,smack_inode_unlink,smack_inode_rmdir,smack_inode_rename,smack_inode_secattr,smack_inode_setxattr等,當上層應用執行擷取檔案擴充屬性的操作時,Smack的鉤子函數要求當前進程對該inode有讀許可權,具體鉤子函數是smack_inode_getxattr;
第五類,關於檔案file
Linux的檔案結構體file中也有安全域void *f_security,Smack的鉤子函數smack_file_alloc_security使得此安全域指向當前進程的Smack的標籤,也就是說如果該進程建立了檔案,那麼檔案的安全域就是該進程的Smack標籤,除此之外還有擷取檔案狀態,為檔案加鎖等鉤子函數。
第六類, 關於進程task
當上層應用執行設定進程組識別碼setpgid,設定進程nice值setnice,設定進程調度值setscheduler等操作時,Smack鉤子函數要求當前進程對被設定的進程或者說事目標進程有寫入權限,而當上層應用執行getpgid,getsid等操作時,Smack鉤子函數要求當前進程對目標進程有讀許可權;
第七類,關於訊息
Linux訊息結構體msg_msg中有安全域void *security,它指向了當前進程的安全性標籤,這通過鉤子函數smack_msg_msg_alloc_security來設定;
第八類,關於IPC
Linux結構體shmid_kernel有一個成員是kern_ipc_perm類型,而此類型的安全域void *security由Smack的鉤子函數smack_shm_alloc_security設定為空白,當進程利用共用記憶體進行通訊時,Smack鉤子函數smk_curacc_shm來進行許可權檢查;
第九類,關於訊號量數組sem_array
Linux結構體sem_arrayl有一個成員是kern_ipc_perm類型,Smack鉤子函數smk_curacc_sem來進行許可權檢查;
第十類,關於通訊端socket
一 從Android核心編譯說起
在將Smack編譯到Android核心中,我曾經發現Smack的Kconfig檔案裡明確要求了NETLABEL,如果核心不進行NETLABEL配置,那麼Android核心無法編譯Smack,這就是說NETLABEL在Smack技術中佔有舉足輕重的作用,那麼什麼是NETLABEL?
NETLABEL翻譯成網路標籤,它是一項類似於LSM的工程,可以使得Linux核心具備向IP包打上安全性標籤CIPSO的能力,而這裡的安全性標籤CIPSO(Commerical IP Security Option)是附加在IP包的包頭的一個資訊體,由網域名稱解釋DOI(domain interpretation),安全層級Level,安全分類Category組成,其中DOI負責對IP包的安全性標籤進行解釋,能夠讓在網路上收發雙方決定對方有沒有許可權發送包,發送方要想將IP包發送給網路的接收方,那麼發送方必須對接收方有寫入權限。
那麼Linux核心通過什麼方式或者說藉助什麼能夠在IP包上打上CIPSO標籤? 核心是通過LSM提供的鉤子函數可以設定或者擷取IP包的CIPSO標籤,在Smack技術中,Smack鉤子函數smack_sk_alloc_security可以使得通訊端sock的安全域指向sockect_smack結構體,而sockect_smack結構體兩項smk_in和smk_out分別代表了從此通訊端接收包的標籤和從此通訊端發送出去包的標籤。
二 LSM和NETLABEL互動的結構體netlbl_lsm_secattr
LSM對通訊端設定CIPSO標籤是藉助了netlbl_lsm_secattr這個結構體,這個結構體儲存了通訊端的安全屬性,域,類型等資訊。Smack的鉤子函數static void smack_to_secattr(char *smack, struct netlbl_lsm_secattr *nlsp)可以將smack標籤填充到nlsp中,而Smack鉤子函數static int smack_netlabel(struct sock *sk, int labeled)就是藉助netlbl_lsm_secattr來設定通訊端sock的CIPSO標籤。
三 兩個重要的結構體smack_known和smk_netlbladdr
struct smack_known {
struct list_head list;
char smk_known[SMK_LABELLEN];
u32 smk_secid;
struct smack_cipso *smk_cipso;
spinlock_t smk_cipsolock; /* for changing cipso map */
struct list_head smk_rules; /* access rules */
struct mutex smk_rules_lock; /* lock for the rules */
};
這個結構體以Linux核心鏈表形式組織起來,其中smk_known表示smack的安全性標籤,每當上層應用執行設定檔案的smack標籤操作時,這個鏈表會被Smack函數smack_import和smack_import_entry檢查,如果設定的smack標籤在smack_known鏈表中查不到,則新的smack標籤會被匯入到此鏈表,在鏈表中的結點不會被刪除,新結點只會被加入。而這裡的smack_cipso正是CIPSO標籤,通過此結構體一個smk_known可以映射到一個smack_cipso。
在Smack的使用者空間我們可以通過工具smackcipso向Smack虛擬檔案/smack/cipso匯入規則: smack標籤 安全層級(整數) 安全分類(整數),匯入到cipso檔案中的形式是 smack標籤 安全層級/安全分類1,安全分類2,...。
在smack_lsm.c檔案中,有很多函數涉及到smack_known的操作,其中大部分涉及通訊端鉤子,如:
static char *smack_from_secattr(struct netlbl_lsm_secattr *sap, struct socket_smack *ssp),這個鉤子函數就將netlbl_lsm_secattr的相關資訊設定到通訊端的安全域ssp中。
struct smk_netlbladdr {
struct list_head list;
struct sockaddr_in smk_host; /* network address */
struct in_addr smk_mask; /* network mask */
char *smk_label; /* label */
};
此結構體也是以Linux核心鏈表組織起來的,它是關於主機的網路地址和主機的Smack標籤,它在smack_lsm.c中也多次用到,如Smack的鉤子函數:static char *smack_host_label(struct sockaddr_in *sip)就是根據主機地址sip來從smk_netlbladdr鏈表中擷取主機的Smack標籤。
四 Smack對網路的存取控制
Smack對網路控制很像IPC機制,Smack的LSM機制將從通訊端發送或者接收的IP包都打上了CIPSO標籤,如果在網路中傳輸的包沒有被打上CIPSO標籤,那麼網路環境的CIPSO標籤將被賦予該IP包。網路的發送方要想把IP包發送給接收方,必須保證發送方對接收方有Smack的寫入權限。如果我們想要容許一方One和另一方Other進行網路通訊,我們可以在Smack規則寫下:One Other wW Other One wW。
Smack的鉤子函數static int smack_unix_may_send(struct socket *sock, struct socket *other),檢查了通訊端sock對通訊端other有沒有發送包的許可權,這其實就是在存取控制鏈表smack_rule中檢查sock的安全域socket_smack的成員smk_out對other的安全域socket_smack的成員smk_in有沒有寫入權限。類似的鉤子函數還有smack_unix_stream_connect,smack_netlabel_send,smack_socket_sendmsg等。