我目前在一个项目中,我有一个父进程来设置一个套接字对,派生然后使用此套接字对进行通信。子级如果要打开文件(或其他任何基于文件描述符的资源),则应始终转到父级,请求该资源并获取通过套接字对发送的fd
。此外,我想防止 child 自己打开任何文件描述符。
我偶然发现了setrlimit
,它成功阻止了子进程打开新的文件描述符,但是它似乎也使通过初始套接字连接发送的任何文件描述符无效。 Linux上是否有任何方法允许单个进程打开任何文件,将其文件描述符发送给其他进程并允许他们使用它们,而又不允许这些其他进程自己打开任何文件描述符?
对于我的用例,可以是任何内核配置,系统调用等,只要可以在fork之后应用,并且可以应用于所有文件描述符(不仅是文件,还可以是套接字,套接字对等)。
最佳答案
您在这里拥有的正是seccomp的用例。
使用seccomp,可以以不同方式过滤系统调用。在这种情况下,您想要做的就是在fork()
之后安装一个seccomp
过滤器,该过滤器禁止使用open(2)
,openat(2)
和socket(2)
(以及更多)。
为此,您可以执行以下操作:
seccomp_init(3)
的默认行为SCMP_ACT_ALLOW
创建一个seccomp上下文。 seccomp_rule_add(3)
为要拒绝的每个系统调用向上下文添加规则。如果尝试进行系统调用,则可以使用SCMP_ACT_KILL
终止进程,使用SCMP_ACT_ERRNO(val)
来使系统调用无法返回指定的errno
值,或者使用手册页中定义的任何其他action
值。 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的一些重要说明:
fork(2)
或clone(2)
,则任何子进程都将受同一过滤器的约束。 execve(2)
,则在调用execve(2)
时将保留现有过滤器。 prctl(2)
syscall,则该进程可以应用其他过滤器。 关于c - 防止进程在Linux上打开新的文件描述符,但允许通过套接字接收文件描述符,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59730828/