Ce que vous avez ici est exactement le cas d'utilisation de seccomp .
En utilisant seccomp, vous pouvez filtrer les appels système de différentes manières. Ce que vous voulez faire dans cette situation, c'est, juste après fork()
, installer un seccomp
filtre qui interdit l'utilisation de open(2)
,openat(2)
, socket(2)
(et plus). Pour ce faire, vous pouvez effectuer les opérations suivantes:
- Tout d'abord, créez un contexte seccomp en utilisant
seccomp_init(3)
le comportement par défaut de SCMP_ACT_ALLOW
.
- Ajoutez ensuite une règle au contexte en utilisant
seccomp_rule_add(3)
pour chaque appel système que vous souhaitez refuser. Vous pouvez utiliser SCMP_ACT_KILL
pour arrêter le processus en cas de tentative d'appel système, SCMP_ACT_ERRNO(val)
pour empêcher l'échec de l'appel système de renvoyer le paramètre spécifié.errno
valeur ou toute autre action
valeur définie dans la page de manuel.
- Chargez le contexte en utilisant
seccomp_load(3)
pour le rendre efficace.
Avant de continuer, NOTEZ qu'une approche de liste noire comme celle-ci est en général plus faible qu'une approche de liste blanche. Il autorise tout appel système qui n'est pas explicitement interdit, et pourrait entraîner un contournement du filtre . Si vous pensez que le processus enfant que vous souhaitez exécuter pourrait essayer de manière malveillante d'éviter le filtre, ou si vous savez déjà quels appels système seront nécessaires aux enfants, une approche de liste blanche est meilleure et vous devez faire le contraire de ce qui précède: créer un filtre avec l'action par défaut de SCMP_ACT_KILL
et autoriser les appels système nécessaires avec SCMP_ACT_ALLOW
. En termes de code, la différence est minime (la liste blanche est probablement plus longue, mais les étapes sont les mêmes).
Voici un exemple de ce qui précède (je fais exit(-1)
en cas d'erreur juste pour des raisons de simplicité):
#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);
}
Maintenant, dans votre programme, vous pouvez appeler la fonction ci-dessus pour appliquer le filtre seccomp juste après fork()
, comme ceci:
child_pid = fork();
if (child_pid == -1)
exit(-1);
if (child_pid == 0) {
secure();
// Child code here...
exit(0);
} else {
// Parent code here...
}
Quelques notes importantes sur seccomp:
- Un filtre seccomp, une fois appliqué, ne peut pas être supprimé ou modifié par le processus.
- Si
fork(2)
ou clone(2)
sont autorisés par le filtre, tout processus enfant sera contraint par le même filtre.
- Si
execve(2)
est autorisé, le filtre existant sera conservé lors d'un appel à execve(2)
.
- Si l'
prctl(2)
appel système est autorisé, le processus peut appliquer d'autres filtres.