簡體   English   中英

防止進程在 Linux 上打開新的文件描述符,但允許通過套接字接收文件描述符

[英]Prevent process from opening new file descriptor on Linux but allow receiving file descriptors via sockets

我目前正在開發一個項目,在該項目中,我有一個父進程設置一個 socketpair,fork 然后使用這個 socketpair 進行通信。 孩子,如果它想打開一個文件(或任何其他基於文件描述符的資源)應該總是去父,請求資源並通過 socketpair 發送fd 此外,我想防止孩子自己打開任何文件描述符。

我偶然發現setrlimit成功地阻止了孩子打開新的文件描述符,但它似乎也使通過初始套接字連接發送的任何文件描述符無效。 Linux 上是否有任何方法允許單個進程打開任何文件,將其文件描述符發送給其他進程並讓它們使用它們,而不允許這些其他進程自己打開任何文件描述符?

對於我的用例,可以是任何內核配置、系統調用等,只要它可以在 fork 之后應用並且只要它適用於所有文件描述符(不僅是文件,還包括套接字、套接字對等)。

您在這里擁有的正是seccomp的用例。

使用 seccomp,您可以以不同的方式過濾系統調用。 在這種情況下,您想要做的是,在fork() ,安裝一個seccomp過濾器,該過濾器不允許使用open(2)openat(2)socket(2) (以及更多)。 為此,您可以執行以下操作:

  1. 首先,使用seccomp_init(3)SCMP_ACT_ALLOW的默認行為創建一個 seccomp 上下文。
  2. 然后使用seccomp_rule_add(3)為要拒絕的每個系統調用向上下文添加規則。 如果嘗試進行系統調用,您可以使用SCMP_ACT_KILLSCMP_ACT_KILL進程,使用SCMP_ACT_ERRNO(val)使系統調用失敗並返回指定的errno值,或使用手冊頁中定義的任何其他action值。
  3. 使用seccomp_load(3)加載上下文以使其生效。

在繼續之前,請注意,像這樣的黑名單方法通常比白名單方法弱。 它允許任何未明確禁止的系統調用,並可能導致繞過過濾器 如果您認為您要執行的子進程可能惡意地試圖避開過濾器,或者如果您已經知道子進程需要哪些系統調用,那么白名單方法更好,您應該執行與上述相反的操作:使用SCMP_ACT_KILL的默認操作創建過濾器,並使用SCMP_ACT_ALLOW允許所需的系統調用。 在代碼方面差異很小(白名單可能更長,但步驟相同)。

這是上述示例(為了簡單起見,我正在執行exit(-1)以防出錯):

#include <stdlib.h>
#include <seccomp.h>

static void secure(void) {
    int err;
    scmp_filter_ctx ctx;

    int blacklist[] = {
        SCMP_SYS(open),
        SCMP_SYS(openat),
        SCMP_SYS(creat),
        SCMP_SYS(socket),
        SCMP_SYS(open_by_handle_at),
        // ... possibly more ...
    };

    // Create a new seccomp context, allowing every syscall by default.
    ctx = seccomp_init(SCMP_ACT_ALLOW);
    if (ctx == NULL)
        exit(-1);

    /* Now add a filter for each syscall that you want to disallow.
       In this case, we'll use SCMP_ACT_KILL to kill the process if it
       attempts to execute the specified syscall. */

    for (unsigned i = 0; i < sizeof(blacklist) / sizeof(blacklist[0]); i++) {
        err = seccomp_rule_add(ctx, SCMP_ACT_KILL, blacklist[i], 0);
        if (err)
            exit(-1);
    }

    // Load the context making it effective.
    err = seccomp_load(ctx);
    if (err)
        exit(-1);
}

現在,在您的程序中,您可以調用上述函數在fork()之后立即應用 seccomp 過濾器,如下所示:

child_pid = fork();
if (child_pid == -1)
    exit(-1);

if (child_pid == 0) {
    secure();

    // Child code here...

    exit(0);
} else {
    // Parent code here...
}

關於 seccomp 的一些重要說明:

  • seccomp 過濾器一旦應用,就不能被該過程刪除或更改。
  • 如果過濾器允許fork(2)clone(2) ,則任何子進程都將受到同一個過濾器的約束。
  • 如果允許execve(2) ,則在調用execve(2)將保留現有過濾器。
  • 如果允許prctl(2)系統調用,則該進程能夠應用更多過濾器。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM