У вас тут є саме такий варіант використання seccomp .
Використовуючи seccomp, ви можете фільтрувати системні виклики різними способами. Те , що ви хочете зробити в цій ситуації, відразу ж після того fork()
, щоб встановити seccomp
фільтр , який забороняє використання open(2)
, openat(2)
, socket(2)
(і більше). Для цього можна зробити наступне:
- Спочатку створіть контекст seccomp, використовуючи
seccomp_init(3)
поведінку за замовчуванням SCMP_ACT_ALLOW
.
- Потім додайте правило до контексту, використовуючи
seccomp_rule_add(3)
для кожного syscall, який ви хочете відхилити. Ви можете використовувати SCMP_ACT_KILL
для вбивства процесу при спробі системного виклику, SCMP_ACT_ERRNO(val)
щоб змусити syscall повернути вказане errno
значення або будь-яке інше action
значення, визначене на сторінці керівництва.
- Завантажте контекст,
seccomp_load(3)
щоб зробити його ефективним.
Перш ніж продовжувати, зверніть увагу, що такий підхід у чорному списку, як цей, загалом слабший, ніж підхід до білого списку. Це дозволяє будь-який syscall, який явно не заборонений, і може призвести до обходу фільтра . Якщо ви вважаєте, що дочірній процес, який ви хочете виконати, може зловмисно намагатися уникати фільтра, або якщо ви вже знаєте, які систематичні дзвінки знадобляться дітям, підхід із білого списку краще, і вам слід зробити протилежне вищесказаному: створити фільтр із дією за замовчуванням SCMP_ACT_KILL
та дозволити необхідні syscalls з 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:
- Одноразовий фільтр, що застосовується, не може бути видалений або змінений процесом.
- Якщо фільтр дозволений
fork(2)
або clone(2)
дозволений, будь-які дочірні процеси будуть обмежені тим самим фільтром.
- Якщо
execve(2)
це дозволено, існуючий фільтр зберігатиметься під час виклику до execve(2)
.
- Якщо
prctl(2)
дозволено систематичне виклик, процес може застосувати додаткові фільтри.