Здесь у вас есть именно тот случай использования seccomp .
Используя seccomp, вы можете фильтровать системные вызовы различными способами. В этой ситуации вы хотите сразу после fork()
установить фильтр seccomp
, который запрещает использование open(2)
, openat(2)
, socket(2)
(и более). Чтобы выполнить sh этого, вы можете сделать следующее:
- Сначала создайте контекст 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)
разрешено, процесс может применять другие фильтры.