Was Sie hier haben, ist genau der Anwendungsfall von seccomp .
Mit seccomp können Sie Systemaufrufe auf verschiedene Arten filtern. Was Sie in dieser Situation tun mögen , ist, direkt nach fork()
, eine installieren seccomp
Filter, der die Verwendung von nicht zulässt open(2)
, openat(2)
, socket(2)
(und mehr). Um dies zu erreichen, können Sie Folgendes tun:
- Erstellen Sie zunächst einen seccomp-Kontext
seccomp_init(3)
mit dem Standardverhalten von SCMP_ACT_ALLOW
.
seccomp_rule_add(3)
Fügen Sie dann dem Kontext eine Regel hinzu, indem Sie für jeden Systemaufruf, den Sie ablehnen möchten, eine Regel verwenden . Sie können SCMP_ACT_KILL
den Vorgang beenden, wenn der Syscall versucht wird SCMP_ACT_ERRNO(val)
, damit der Syscall den angegebenen errno
Wert oder einen anderen action
auf der Handbuchseite definierten Wert nicht zurückgibt.
- Laden Sie den Kontext mit
seccomp_load(3)
, um ihn effektiv zu machen.
Bevor Sie fortfahren, beachten Sie, dass ein Blacklist-Ansatz wie dieser im Allgemeinen schwächer ist als ein Whitelist-Ansatz. Es erlaubt jeden Systemaufruf, der nicht explizit verboten ist, und kann zu einer Umgehung des Filters führen . Wenn Sie der Meinung sind, dass der untergeordnete Prozess, den Sie ausführen möchten, in böswilliger Absicht versucht, den Filter zu umgehen, oder wenn Sie bereits wissen, welche Systemaufrufe von den untergeordneten Prozessen benötigt werden, ist ein Whitelist-Ansatz besser, und Sie sollten das Gegenteil der oben genannten Schritte ausführen: Erstellen Sie einen Filter mit der Standardaktion SCMP_ACT_KILL
und lassen Sie die erforderlichen Systemaufrufe mit zu SCMP_ACT_ALLOW
. In Bezug auf den Code ist der Unterschied minimal (die Whitelist ist wahrscheinlich länger, aber die Schritte sind gleich).
Hier ist ein Beispiel für das oben Genannte (ich mache es exit(-1)
im Fehlerfall nur der Einfachheit halber):
#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);
}
Jetzt können Sie in Ihrem Programm die obige Funktion aufrufen, um den Seccomp-Filter direkt nach dem folgenden anzuwenden fork()
:
child_pid = fork();
if (child_pid == -1)
exit(-1);
if (child_pid == 0) {
secure();
// Child code here...
exit(0);
} else {
// Parent code here...
}
Einige wichtige Hinweise zu seccomp:
- Ein einmal angewendeter Seccomp-Filter kann durch den Prozess nicht entfernt oder geändert werden.
- Wenn der Filter dies zulässt
fork(2)
oder clone(2)
zulässt, werden alle untergeordneten Prozesse durch denselben Filter eingeschränkt.
- Wenn dies
execve(2)
zulässig ist, bleibt der vorhandene Filter während eines Aufrufs von erhalten execve(2)
.
- Wenn der
prctl(2)
Systemaufruf zulässig ist, kann der Prozess weitere Filter anwenden.