Здесь у вас есть именно тот случай использования seccomp .
Используя seccomp, вы можете фильтровать системные вызовы по-разному. То , что вы хотите сделать в этой ситуации, сразу же после того fork()
, чтобы установить seccomp
фильтр , который запрещает использование open(2)
, openat(2)
,socket(2)
(и больше). Для этого вы можете сделать следующее:
- Сначала создайте контекст seccomp
seccomp_init(3)
с использованием поведения по умолчанию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);
}
Теперь в вашей программе вы можете вызвать вышеуказанную функцию, чтобы применить фильтр 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...
}
Несколько важных замечаний по seccomp:
- После применения фильтра seccomp процесс не может быть удален или изменен.
- Если
fork(2)
илиclone(2)
фильтр разрешен разрешен, любые дочерние процессы будут ограничены тем же фильтром.
- Если
execve(2)
это разрешено, существующий фильтр будет сохранен при вызовеexecve(2)
.
- Если
prctl(2)
системный вызов разрешен, процесс может применить дополнительные фильтры.