Smack核心機制淺析

在以上幾篇博客中,我已經闡述瞭有關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等。

 

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *