jkl*_*mnn 9 c linux system-calls
我目前正在开发一个项目,在该项目中,我有一个父进程可以设置 socketpair、fork,然后使用这个 socketpair 进行通信。孩子,如果它想打开一个文件(或任何其他基于文件描述符的资源)应该总是去父,请求资源并fd通过套接字对发送。此外,我想防止孩子自己打开任何文件描述符。
我偶然发现setrlimit它成功地阻止了孩子打开新的文件描述符,但它似乎也使通过初始套接字连接发送的任何文件描述符无效。Linux 上是否有任何方法允许单个进程打开任何文件,将其文件描述符发送给其他进程并让它们使用它们,而不允许这些其他进程自己打开任何文件描述符?
对于我的用例,可以是任何内核配置、系统调用等,只要它可以在 fork 之后应用并且只要它适用于所有文件描述符(不仅是文件,还包括套接字、套接字对等)。
您在这里拥有的正是seccomp的用例。
使用 seccomp,您可以以不同的方式过滤系统调用。在这种情况下,您想要做的是,紧接着fork()安装一个seccomp过滤器,禁止使用open(2)、openat(2)、socket(2)(以及更多)。为此,您可以执行以下操作:
seccomp_init(3)的默认行为创建一个 seccomp 上下文SCMP_ACT_ALLOW。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);
}
Run Code Online (Sandbox Code Playgroud)
现在,在您的程序中,您可以调用上述函数在 之后立即应用 seccomp 过滤器fork(),如下所示:
child_pid = fork();
if (child_pid == -1)
exit(-1);
if (child_pid == 0) {
secure();
// Child code here...
exit(0);
} else {
// Parent code here...
}
Run Code Online (Sandbox Code Playgroud)
关于 seccomp 的一些重要说明:
fork(2)或clone(2),则任何子进程都将受到同一过滤器的约束。execve(2)允许,现有过滤器将在调用execve(2).prctl(2)允许系统调用,则该进程能够应用更多过滤器。