Empêcher le processus d'ouvrir un nouveau descripteur de fichier sous Linux mais autoriser la réception de descripteurs de fichier via des sockets


9

Je travaille actuellement sur un projet où j'ai un processus parent qui configure un socketpair, des fourches et utilise ensuite ce socketpair pour communiquer. L'enfant, s'il veut ouvrir un fichier (ou toute autre ressource basée sur un descripteur de fichier) doit toujours aller vers le parent, demander la ressource et obtenir l' fdenvoi via le socketpair. De plus, je veux empêcher l'enfant d'ouvrir un descripteur de fichier par lui-même.

Je suis tombé sur setrlimitce qui empêche avec succès l'enfant d'ouvrir de nouveaux descripteurs de fichiers, mais cela semble également invalider tous les descripteurs de fichiers envoyés via la connexion socket initiale. Existe-t-il une méthode sous Linux qui permet à un seul processus d'ouvrir un fichier, d'envoyer son descripteur de fichier à d'autres processus et de les laisser les utiliser sans autoriser ces autres processus à ouvrir eux-mêmes un descripteur de fichier?

Pour mon cas d'utilisation, cela peut être n'importe quelle configuration de noyau, appel système, etc. tant qu'il peut être appliqué après fork et tant qu'il s'applique à tous les descripteurs de fichiers (pas seulement les fichiers mais aussi les sockets, les paires de sockets, etc.).


1
Vous pourriez être intéressé par seccomp.
user253751

Réponses:


6

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 seccompfiltre qui interdit l'utilisation de open(2),openat(2) , socket(2)(et plus). Pour ce faire, vous pouvez effectuer les opérations suivantes:

  1. Tout d'abord, créez un contexte seccomp en utilisant seccomp_init(3) le comportement par défaut de SCMP_ACT_ALLOW.
  2. 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_KILLpour 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 actionvaleur définie dans la page de manuel.
  3. 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_KILLet 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.

2
Liste noire pour un bac à sable? Généralement, c'est une mauvaise idée, vous voulez plutôt la liste blanche.
Déduplicateur

@Deduplicator Je sais, mais une approche de liste blanche ne s'applique pas très bien à la situation d'OP car ils veulent simplement interdire l'ouverture de nouveaux descripteurs de fichiers. J'ajouterai une note à la fin.
Marco Bonelli

Merci pour la réponse, c'est ce dont j'ai besoin. Une liste blanche est en effet meilleure pour l'application que je voulais à l'origine. Je n'avais tout simplement pas à l'esprit qu'il y avait plus de choses à restreindre que d'ouvrir des descripteurs de fichiers.
jklmnn

@jklmnn ouais, exactement. J'ai en effet juste oublié que cela socket(2)peut aussi créer un fd, donc ça devrait être bloqué aussi. Si vous connaissez le processus enfant, une approche de liste blanche est meilleure.
Marco Bonelli

@MarcoBonelli Une liste blanche est définitivement meilleure. Spontanément, creat(), dup()et dup2()sont tous les appels système Linux que les descripteurs de fichier de retour. Il y a beaucoup de façons de contourner une liste noire ...
Andrew Henle
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.