英語

Landlock LSM:核心文件

作者:

Mickaël Salaün

日期:

2025 年 3 月

Landlock 的目標是建立作用域訪問控制(即沙盒)。為了加強整個系統,此功能應該對任何程序可用,包括非特權程序。因為這樣的程序可能被入侵或植入後門(即不可信),所以從核心和其他程序的角度來看,Landlock 的功能必須是安全的。因此,Landlock 的介面必須暴露最小的攻擊面。

Landlock 的設計目的是供非特權程序使用,同時遵循由其他訪問控制機制(例如 DAC、LSM)強制執行的系統安全策略。 Landlock 規則不得干擾系統上強制執行的其他訪問控制,而只能新增更多限制。

任何使用者都可以在他們的程序上強制執行 Landlock 規則集。它們被合併並針對繼承的規則集進行評估,以確保只能新增更多約束。

使用者空間文件可以在這裡找到:Landlock:非特權訪問控制

安全訪問控制的指導原則

  • Landlock 規則應側重於對核心物件的訪問控制,而不是系統呼叫過濾(即系統呼叫引數),這是 seccomp-bpf 的目的。

  • 為了避免多種側通道攻擊(例如安全策略洩露、基於 CPU 的攻擊),Landlock 規則應無法以程式設計方式與使用者空間通訊。

  • 核心訪問檢查不應減慢來自非沙盒程序的訪問請求。

  • 與 Landlock 操作相關的計算(例如,強制執行規則集)應僅影響請求它們的程序。

  • 沙盒程序直接從核心獲得的資源(例如,檔案描述符)應保留其作用域訪問許可權(在資源獲取時),無論哪個程序使用它們。參見 檔案描述符訪問許可權

  • 訪問拒絕應根據系統和 Landlock 域配置進行記錄。日誌條目必須包含有關拒絕原因和相關安全策略所有者的資訊。這種日誌生成應該對允許的請求產生可忽略不計的效能和記憶體影響。

設計選擇

Inode 訪問許可權

所有訪問許可權都與 inode 以及可以透過它訪問的內容相關聯。讀取目錄的內容並不意味著允許讀取列出的 inode 的內容。實際上,檔名是其父目錄本地的,並且 inode 可以透過(硬)連結被多個檔名引用。能夠取消連結檔案只會直接影響目錄,而不會影響未連結的 inode。這就是為什麼 LANDLOCK_ACCESS_FS_REMOVE_FILELANDLOCK_ACCESS_FS_REFER 不允許繫結到檔案,而只允許繫結到目錄的原因。

檔案描述符訪問許可權

訪問許可權在開啟時被檢查並繫結到檔案描述符。基本原則是,當等效的操作序列在相同的 Landlock 域下執行時,應導致相同的結果。

LANDLOCK_ACCESS_FS_TRUNCATE 許可權為例,如果相關檔案層次結構未授予該訪問許可權,則可能允許開啟一個檔案進行寫入,而不允許 ftruncate 結果檔案描述符。以下操作序列具有相同的語義,因此應具有相同的結果

  • truncate(path);

  • int fd = open(path, O_WRONLY); ftruncate(fd); close(fd);

與檔案訪問模式(例如 O_RDWR)類似,附加到檔案描述符的 Landlock 訪問許可權即使在程序之間傳遞(例如透過 Unix 域套接字)也會保留。然後,即使接收程序未被 Landlock 沙盒化,也會強制執行此類訪問許可權。實際上,這是保持整個系統訪問控制一致所必需的,並且這避免了透過檔案描述符傳遞(即混亂的副手攻擊)的無人值守繞過。

測試

有關向後相容性、ptrace 限制和檔案系統支援的使用者空間測試可以在這裡找到:tools/testing/selftests/landlock/

核心結構

物件

struct landlock_object_underops

對底層物件的操作

定義:

struct landlock_object_underops {
    void (*release)(struct landlock_object *const object) __releases(object->lock);
};

成員

release

釋放底層物件(例如,inode 的 iput())。

struct landlock_object

繫結到核心物件的安全 blob

定義:

struct landlock_object {
    refcount_t usage;
    spinlock_t lock;
    void *underobj;
    union {
        struct rcu_head rcu_free;
        const struct landlock_object_underops *underops;
    };
};

成員

usage

此計數器用於將物件繫結到匹配它的規則或在新增新規則時保持其活動狀態。如果此計數器達到零,則不得修改此結構,但仍可以在 RCU 讀取端臨界區內讀取此計數器。當向 usage 計數器為零的物件新增新規則時,我們必須等到指向此物件的指標設定為 NULL(或回收)。

lock

防止併發修改。必須從 usage 降至零時到清理 underobj 對此物件的任何弱引用時持有此鎖。

鎖順序:inode->i_lock 巢狀在其中。

underobj

用於清理物件並將物件標記為繫結到其底層核心結構。此指標受 lock 保護。參見 landlock_release_inodes() 和 release_inode()。

{unnamed_union}

anonymous

rcu_free

允許從 RCU 讀取端臨界區內無鎖使用 usagelockunderobjrcu_freeunderops 僅由 landlock_put_object() 使用。

underops

允許 landlock_put_object() 釋放底層物件(例如,inode)。

描述

此結構的目標是以安全的方式將一組短暫的訪問許可權(屬於不同域)繫結到核心物件(例如,inode)。這意味著處理併發使用和修改。

struct landlock_object 的生命週期取決於引用它的規則。

檔案系統

struct landlock_inode_security

Inode 安全 blob

定義:

struct landlock_inode_security {
    struct landlock_object __rcu *object;
};

成員

object

指向已分配物件的弱指標。新物件的所有分配都受底層 inode->i_lock 保護。但是,從 inode 中原子地分離 object 僅受 object->lock 保護,從 object 的 usage 引用計數降至零到此指標被置空的時間(參見 release_inode() 和 hook_sb_delete())。實際上,由於 get_inode_object() 執行的仔細 rcu_access_pointer() 檢查,這種分離不需要 inode->i_lock。

描述

允許引用繫結到 inode 的 struct landlock_object(即底層物件)。

struct landlock_file_security

檔案安全 blob

定義:

struct landlock_file_security {
    access_mask_t allowed_access;
#ifdef CONFIG_AUDIT;
    deny_masks_t deny_masks;
    u8 fown_layer;
#endif ;
    struct landlock_cred_security fown_subject;
};

成員

allowed_access

開啟檔案時可用的訪問許可權。這不一定是當時可用的全套訪問許可權,但它是授權以後對已開啟檔案進行操作所必需的子集。

deny_masks

域層級別,拒絕可選訪問(參見 _LANDLOCK_ACCESS_FS_OPTIONAL)。

fown_layer

具有 LANDLOCK_SCOPE_SIGNAL 的 fown_subject->domain 的層級別。

fown_subject

任務的 Landlock 憑據,該任務設定可能接收訊號的 PID,例如,將 MSG_OOB 寫入相關套接字時的 SIGURG。此指標受相關 file->f_owner->lock 保護,就像 fown_struct 的成員:pid、uid 和 euid 一樣。

描述

此資訊在 hook_file_open 中開啟檔案時填充,並跟蹤開啟檔案時可用的相關 Landlock 訪問許可權。其他 LSM 鉤子使用這些許可權來授權對已開啟檔案的操作。

struct landlock_superblock_security

超級塊安全 blob

定義:

struct landlock_superblock_security {
    atomic_long_t inode_refs;
};

成員

inode_refs

正在由 release_inode() 釋放的(來自此超級塊)掛起的 inode 的數量。參見 struct super_block->s_fsnotify_inode_refs 。

描述

允許 hook_sb_delete() 等待對 release_inode() 的併發呼叫。

規則集和域

域是一個繫結到一組主體(即任務的憑據)的只讀規則集。每次在任務上強制執行規則集時,當前域將被複制,並且規則集作為新域中的新規則層匯入。實際上,一旦進入域,每個規則都繫結到一個層級別。要授予對物件的訪問許可權,每個層中至少一個規則必須允許對物件執行請求的操作。然後,任務只能轉換為一個新域,該域是當前域的約束與任務提供的規則集的約束的交集。

對於正在沙盒化的任務,主體的定義是隱式的,這使得推理更容易,並有助於避免陷阱。

struct landlock_layer

給定層的訪問許可權

定義:

struct landlock_layer {
    u16 level;
    access_mask_t access;
};

成員

level

此層在層堆疊中的位置。

access

允許對核心物件執行的操作的位欄位。它們與物件型別相關(例如 LANDLOCK_ACTION_FS_READ)。

union landlock_key

規則集的紅黑樹的鍵

定義:

union landlock_key {
    struct landlock_object *object;
    uintptr_t data;
};

成員

object

ptr

用於標識核心物件的指標(例如,inode)。

data

用於標識任意 32 位值(例如,TCP 埠)的原始資料。

enum landlock_key_type

union landlock_key 的型別

常量

LANDLOCK_KEY_INODE

landlock_ruleset.root_inode 的節點鍵的型別。

LANDLOCK_KEY_NET_PORT

landlock_ruleset.root_net_port 的節點鍵的型別。

struct landlock_id

定義:

struct landlock_id {
    union landlock_key key;
    const enum landlock_key_type type;
};

成員

規則集的唯一規則識別符號

key

標識核心物件(例如,inode)或原始值(例如,TCP 埠)。

type

landlock_ruleset 的根樹的型別。

struct landlock_rule

定義:

struct landlock_rule {
    struct rb_node node;
    union landlock_key key;
    u32 num_layers;
    struct landlock_layer layers[] ;
};

成員

繫結到物件的訪問許可權

node

規則集的唯一規則識別符號

規則集的紅黑樹中的節點。

一個用於標識核心物件(例如,inode)或原始資料值(例如,網路套接字埠)的聯合體。這用作此規則集元素的鍵。該指標設定一次,永不修改。它始終指向一個已分配的物件,因為每個規則都會增加其物件的引用計數。

num_layers

layers 中的條目數。

layers

從最新到最新的層堆疊,實現為靈活陣列成員 (FAM)。

struct landlock_ruleset

定義:

struct landlock_ruleset {
    struct rb_root root_inode;
#if IS_ENABLED(CONFIG_INET);
    struct rb_root root_net_port;
#endif ;
    struct landlock_hierarchy *hierarchy;
    union {
        struct work_struct work_free;
        struct {
            struct mutex lock;
            refcount_t usage;
            u32 num_rules;
            u32 num_layers;
            struct access_masks access_masks[];
        };
    };
};

成員

Landlock 規則集

root_inode

包含具有 inode 物件的 struct landlock_rule 節點的紅黑樹的根。一旦規則集繫結到程序(即作為域),此樹就是不可變的,直到 usage 達到零。

root_net_port

包含具有網路埠的 struct landlock_rule 節點的紅黑樹的根。一旦規則集繫結到程序(即作為域),此樹就是不可變的,直到 usage 達到零。

hierarchy

{unnamed_union}

anonymous

即使父域消失也允許層次結構識別。這是 ptrace 保護所需要的。

work_free

允許在無鎖部分內釋放規則集。當 usage 達到零時,這僅由 landlock_put_ruleset_deferred() 使用。然後不使用欄位 lockusagenum_rulesnum_layersaccess_masks

anonymous

lock

{unnamed_struct}

usage

保護 root 免受併發修改,如果 usage 大於零。

引用此規則集的程序(即域)或檔案描述符的數量。

num_rules

一個用於標識核心物件(例如,inode)或原始資料值(例如,網路套接字埠)的聯合體。這用作此規則集元素的鍵。該指標設定一次,永不修改。它始終指向一個已分配的物件,因為每個規則都會增加其物件的引用計數。

此規則集中非重疊(即不適用於同一物件)的規則的數量。

此規則集中使用的層數。這允許檢查所有層是否允許訪問請求。值為 0 表示非合併的規則集(即非域)。

access_masks

描述

包含規則集限制的檔案系統和網路操作的子集。域將合併規則集的所有層儲存在堆疊 (FAM) 中,從第一層到最後一層。這些層用於合併規則集、使用者空間向後相容性(即面向未來)以及正確處理沒有重疊訪問許可權的合併規則集。這些層設定一次,並且在規則集的生命週期內永不更改。

此資料結構必須包含唯一條目、可更新且快速匹配物件。

struct access_masks landlock_union_access_masks(const struct landlock_ruleset *const domain)

返回域中處理的所有訪問許可權

引數

const struct landlock_ruleset *const domain

Landlock 規則集(用作域)

返回

所有域的訪問掩碼的 OR 的 access_masks 結果。