apparmor 原始碼分析

RayWHL發表於2022-05-04

這裡不對apparmor做介紹,記錄一下原始碼分析過程。

初始化

static int __init apparmor_init(void)
->  security_add_hooks(apparmor_hooks, ARRAY_SIZE(apparmor_hooks), "apparmor");
    ->  該函式主要通過一個結構陣列 apparmor_hooks 初始化 HOOK 函式

apparmor_hooks 結構陣列分析
具體定義在這

摘取一段分析

static struct security_hook_list apparmor_hooks[] __lsm_ro_after_init = {
    LSM_HOOK_INIT(file_permission, apparmor_file_permission),
}

這裡每一個 LSM_HOOK_INIT 都定義了一個 security_hook_list 結構。

security_hook_list 結構定義為:

struct security_hook_list {
        struct hlist_node            list;
        struct hlist_head            *head;
        union security_list_options       hook;
        char                              *lsm;} __randomize_layout;

結合 LSM_HOOK_INIT 巨集看:

#define LSM_HOOK_INIT(HEAD, HOOK) \        
    { .head =   &security_hook_heads.HEAD, .hook = { .HEAD = HOOK } }

可知,LSM_HOOK_INIT 把一個 security_hook_list 結構體中 head 指標指向 security_hook_heads 的一個成員連結串列,hook 成員初始化為 HOOK函式。
security_hook_heads 我們後面再看,我們這裡推測它是一個全域性變數。

security_add_hooks
我們回頭繼續看 security_add_hooks 函式

void __init security_add_hooks(struct security_hook_list *hooks, int count, char *lsm){
        int i;

        for (i = 0; i < count; i++) {
                hooks[i].lsm = lsm;
                hlist_add_tail_rcu(&hooks[i].list, hooks[i].head);
        }
        ...

遍歷 apparmor_hooks 結構陣列,對每一個陣列將該陣列新增到 head 指向的列表中。

以上實現即是:將每一個 security_hook_list 掛到全域性 security_hook_heads 結構體的某一個成員列表中。而 security_hook_list hook 指向具體函式

security_hook_heads

接下來看看全域性 security_hook_heads 是啥

struct security_hook_heads security_hook_heads __lsm_ro_after_init;

定義就一行,是個結構體, __lsm_ro_after_init 是指定的讀寫許可權,這裡不管。

security_hook_heads 結構定義為:

struct security_hook_heads {
        #define LSM_HOOK(RET, DEFAULT, NAME, ...) struct hlist_head NAME;
        #include "lsm_hook_defs.h"
        #undef LSM_HOOK} __randomize_layout;

簡短,但令人迷惑。include 會將對應檔案內容放到結構體裡。
lsm_hook_defs.h 內容片段如下:(剩餘內容類似)

LSM_HOOK(int, 0, inode_permission, struct inode *inode, int mask)
...
LSM_HOOK(int, 0, file_permission, struct file *file, int mask)

把定義展開,結構體就變成了:

struct security_hook_heads {
    struct hlist_head inode_permission;
    ...
    struct hlist_head file_permission;
    ...
}

展開後,就找到了前面初始化時對應的 file_permission 成員。

呼叫

apparmor如何呼叫具體的許可權檢查函式呢,以 security_file_permission 為例:

int security_file_permission(struct file *file, int mask){
       int ret;
       ret = call_int_hook(file_permission, 0, file, mask);
       ...

call_int_hook 定義在這,不貼出來了,展開後結果:

({
   int RC = 0;
   do {
       struct security_hook_list *P; 
       hlist_for_each_entry(P, &security_hook_heads.file_permission, list) {
           RC = P->hook.file_permission(file, mask);        
           if (RC != 0)
               break;
       }
   } while(0);
})

其會根據全域性變數 security_hook_heads 找到 file_permission 成員列表上所有的security_hook_list 結構,並呼叫 hook 指向的 file_permission 函式。

這個 hook 成員前面略過去了,這裡看一下,其定義為 union 型別,具體為:

union security_list_options {
       #define LSM_HOOK(RET, DEFAULT, NAME, ...) RET (*NAME)(__VA_ARGS__);
       #include "lsm_hook_defs.h"
       #undef LSM_HOOK};

定義與 security_hook_heads 有點類似,區別在於 LSM_HOOK 巨集展開方式不一樣,並且是一個union 型別,對於 file_permission 展開後變成了:

union security_list_options {
   int (*file_permission)( struct file *file, int mask);
}

所以,其就是一個函式指標。這就說得通了。

相關文章