seccomp(2) System Calls Manual seccomp(2) NOM seccomp - Agir sur l'etat de calcul securise (Secure Computing State) du processus BIBLIOTHEQUE Bibliotheque C standard (libc, -lc) SYNOPSIS #include /* Definition des constantes SECCOMP_* */ #include /* Definition de struct sock_fprog */ #include /* Definition des constantes AUDIT_* */ #include /* Definition des constantes SIG* */ #include /* Definition des constantes PTRACE_* */ #include /* Definition des constantes SYS_* */ #include int syscall(SYS_seccomp, unsigned int operation, unsigned int flags, void *args); Remarque : la glibc ne fournit pas d'enveloppe pour seccomp(), imposant l'utilisation de syscall(2). DESCRIPTION L'appel systeme seccomp() agit sur l'etat de calcul securise (seccomp) du processus appelant. Actuellement, Linux gere les valeurs d'operation suivantes : SECCOMP_SET_MODE_STRICT Les seuls appels systeme que le thread appelant est autorise a faire sont read(2), write(2), _exit(2) (mais pas exit_group(2)) et sigreturn(2). Les autres appels systeme aboutissent a la fin du thread appelant ou a la fin du processus complet avec le signal SIGKILL quand il n'y a qu'un seul thread. Le mode de calcul securise strict est utile pour les applications de traitement de nombres qui peuvent avoir besoin d'executer un code a octets non fiable, obtenu peut-etre en lisant un tube ou un socket. Remarquez que si le thread appelant ne peut plus appeler sigprocmask(2), il peut utiliser sigreturn(2) pour bloquer tous les signaux, sauf ceux provenant de SIGKILL et de SIGSTOP. Cela veut dire que alarm(2) (par exemple) n'est pas suffisant pour restreindre la duree d'execution d'un processus. Pour terminer de maniere fiable un processus, SIGKILL doit etre utilise. On peut le faire en utilisant timer_create(2) avec SIGEV_SIGNAL et sigev_signo positionne a SIGKILL ou en utilisant setrlimit(2) pour positionner la limite ferme de RLIMIT_CPU. Cette fonctionnalite n'est disponible que si le noyau a ete construit avec l'option CONFIG_SECCOMP activee. La valeur de flags doit etre de 0 et args doit etre NULL. Cette operation est fonctionnellement identique a l'appel : prctl(PR_SET_SECCOMP, SECCOMP_MODE_STRICT); SECCOMP_SET_MODE_FILTER Les appels systeme autorises sont definis par un pointeur vers un filtre Berkeley Packet (BPF) fourni a l'aide de args. Ce parametre est un pointeur vers une struct sock_fprog ; il peut etre concu pour filtrer des appels systeme de votre choix ainsi que des parametres d'appel systeme. Si le filtre n'est pas valable, seccomp() echoue en renvoyant EINVAL dans errno. Si fork(2) ou clone(2) est autorise par le filtre, les processus enfant seront contraints par les memes filtres d'appel systeme que leur parent. Si execve(2) est autorise, les filtres existants seront preserves lors d'un appel a execve(2). Pour utiliser l'operation SECCOMP_SET_MODE_FILTER, soit le thread appelant doit avoir la capacite CAP_SYS_ADMIN dans son espace de noms utilisateur, soit il doit avoir deja le bit no_new_privs defini. Si ce bit n'a pas deja ete positionne par un ascendant du thread, le thread doit effectuer l'appel suivant : prctl(PR_SET_NO_NEW_PRIVS, 1); Sinon, l'operation SECCOMP_SET_MODE_FILTER echoue et renvoie EACCES dans errno. Cette exigence garantit qu'un processus non privilegie ne peut pas appliquer un filtre malveillant et appeler un programme set-user-ID ou avec d'autres privileges en utilisant execve(2), compromettant ainsi le programme (un tel filtre malveillant pourrait, par exemple, conduire setuid(2) a essayer de definir les identifiants utilisateur de l'appelant a des valeurs non nulles pour renvoyer plutot 0 sans faire d'appel systeme. Ainsi, le programme pourrait etre bidouille pour garder les privileges du super-utilisateur a des moments ou il est possible de l'influencer pour faire des choses dangereuses vu qu'il n'a pas abandonne ses privileges). Si prctl(2) ou seccomp() est autorise par le filtre rattache, d'autres filtres peuvent etre ajoutes. Cela augmentera le temps d'evaluation mais permet d'autres reductions de la surface d'attaque lors de l'execution d'un thread. L'operation SECCOMP_SET_MODE_FILTER n'est disponible que si le noyau a ete configure avec CONFIG_SECCOMP_FILTER. Quand flags vaut 0, cette operation est fonctionnellement identique a l'appel : prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, args); Les parametres reconnus de flags sont : SECCOMP_FILTER_FLAG_LOG (depuis Linux 4.14) Toutes les actions de renvoi des filtres, sauf SECCOMP_RET_ALLOW, doivent etre journalisees. Un administrateur peut outrepasser cet attribut de filtre en empechant des actions specifiques d'etre journalisees a l'aide du fichier /proc/sys/kernel/seccomp/actions_logged. SECCOMP_FILTER_FLAG_NEW_LISTENER (depuis Linux 5.0) Apres une installation reussie du programme de filtrage, renvoyer un nouveau descripteur de fichier de notification pour l'espace utilisateur. (L'attribut close-on-exec est defini pour le descripteur de fichier.) Quand le filtre renvoie SECCOMP_RET_USER_NOTIF, une notification sera envoyee a ce descripteur de fichier. Pour un thread, au maximum un seul filtre de seccomp utilisant l'attribut SECCOMP_FILTER_FLAG_NEW_LISTENER peut etre installe. Consultez seccomp_unotify(2) pour plus de details. SECCOMP_FILTER_FLAG_SPEC_ALLOW (depuis Linux 4.17) Desactiver la mitigation Speculative Store Bypass. SECCOMP_FILTER_FLAG_TSYNC Lors de l'ajout d'un filtre, synchroniser tous les autres threads du processus appelant avec la meme arborescence de filtres seccomp. Une << arborescence de filtres >> est une liste ordonnee de filtres rattachee a un thread (le rattachement de filtres identiques dans des appels seccomp() distincts genere differents filtres depuis cette perspective). Si aucun thread ne peut pas se synchroniser avec l'arborescence de filtres, l'appel ne rattachera pas le nouveau filtre seccomp et echouera en renvoyant le premier identifiant de thread qui n'a pas pu se synchroniser. La synchronisation echouera si un autre thread du meme processus est en SECCOMP_MODE_STRICT ou si des nouveaux filtres seccomp lui sont rattaches en propre, en decalage par rapport a l'arborescence de filtres du thread appelant. SECCOMP_GET_ACTION_AVAIL (depuis Linux 4.14) Tester pour savoir si une action est prise en charge par le noyau. Cette operation peut aider a confirmer que le noyau connait l'action de renvoi d'un filtre recemment ajoute puisque le noyau traite toutes les actions inconnues comme des SECCOMP_RET_KILL_PROCESS. La valeur de flags doit etre de 0 et args doit etre un pointeur vers une action de renvoi de filtre 32 bits non signe. SECCOMP_GET_NOTIF_SIZES (depuis Linux 5.O) Obtenir la taille des structures de notification de l'espace utilisateur de seccomp. Comme ces structures peuvent evoluer et croitre avec le temps, cette commande peut etre utilisee pour determiner quelle quantite de memoire allouer pour envoyer et recevoir des notifications. La valeur de flags doit etre de 0 et args doit etre un pointeur vers un struct seccomp_notif_sizes de la forme suivante : struct seccomp_notif_sizes __u16 seccomp_notif; /* Taille de la structure de notification */ __u16 seccomp_notif_resp; /* Taille de la structure de reponse */ __u16 seccomp_data; /* Taille de 'struct seccomp_data' */ }; Consultez seccomp_unotify(2) pour plus de details. Filtres Lors de l'ajout d'un filtre a l'aide de SECCOMP_SET_MODE_FILTER, args pointe vers un programme de filtrage : struct sock_fprog { unsigned short len; /* Nombre d'instructions BPF */ struct sock_filter *filter; /* Pointeur vers le tableau d'instructions BPF */ }; Chaque programme doit contenir une ou plusieurs instructions BPF : struct sock_filter { /* Filter block */ __u16 code; /* Code du filtre reel */ __u8 jt; /* Jump true (sauter le vrai) */ __u8 jf; /* Jump false (sauter le faux) */ __u32 k; /* Champ generique multiusages */ }; Lors de l'execution des instructions, le programme BPF agit sur les informations de l'appel systeme qui sont rendues disponibles (c'est-a-dire qu'il utilise le mode d'adressage BPF_ABS) en tant que tampon (en lecture seule) ayant la forme suivante : struct seccomp_data { int nr; /* Numero de l'appel systeme */ __u32 arch; /* Valeur AUDIT_ARCH_* (voir ) */ __u64 instruction_pointer; /* pointeur vers l'instruction du processeur */ __u64 args[6]; /* Jusqu'a 6 parametres de l'appel systeme */ }; Comme la numerotation des appels systeme varie entre les architectures et comme certaines (comme x86-64) autorisent du code de l'espace utilisateur a utiliser les conventions de l'appelant d'autres architectures (et comme cette convention peut varier pendant la vie d'un processus qui utilise execve(2) pour executer des binaires qui utilisent differentes conventions), il est generalement necessaire de verifier la valeur du champ arch. Il est fortement recommande d'utiliser une approche par liste d'autorisations autant que possible, parce qu'une telle approche est plus robuste et plus simple. Une liste d'interdictions devra etre mise a jour a chaque fois qu'un appel systeme dangereux sera ajoute (ou un attribut ou une option si elles font partie de la liste des interdictions) et il est souvent possible de modifier la representation d'une valeur sans changer sa signification, conduisant a contourner la liste d'interdictions. Voir aussi Mises en garde ci-dessous. Le champ arch n'est pas unique pour toutes les conventions d'appelant. Les ABI x86-64 et x32 utilisent AUDIT_ARCH_X86_64 en tant que arch et elles fonctionnent sur les memes processeurs. Au contraire, le masque __X32_SYSCALL_BIT est utilise sur le numero d'appel systeme pour parler independamment aux deux ABI. Cela veut dire qu'une politique peut soit interdire tous les appels systeme avec __X32_SYSCALL_BIT, soit elle doit les reconnaitre avec le positionnement ou pas de __X32_SYSCALL_BIT. Une liste des appels systeme a interdire qui s'appuie sur nr et qui ne contient pas de valeurs nr ou __X32_SYSCALL_BIT est positionne peut etre contournee par un programme malveillant qui positionne __X32_SYSCALL_BIT. En outre, les noyaux precedant Linux 5.4 autorisaient a tort nr dans les intervalles 512-547 ainsi que les appels systeme non x32 correspondants relies (operation OU) avec __X32_SYSCALL_BIT. Par exemple, nr == 521 et nr == (101 | __X32_SYSCALL_BIT) creeraient des appels ptrace(2) avec une semantique potentiellement confondue entre x32 et x86_64 dans le noyau. Les politiques prevues pour fonctionner sur des noyaux anterieurs a Linux 5.4 doivent garantir qu'elles interdisent ou qu'elles gerent correctement ces appels systeme. Sur Linux 5.4 et plus recents, de tels appels systeme echoueront avec une erreur ENOSYS sans rien faire. Le champ instruction_pointer fournit l'adresse de l'instruction en langage machine qui a effectue l'appel systeme. Cela pourrait etre utile avec /proc/pid/maps pour effectuer des verifications a partir de la region (projection) du programme qui a fait l'appel systeme (il est probablement raisonnable de verrouiller les appels systeme mmap(2) et mprotect(2) pour empecher le programme de contourner de telles verifications). Lors de la verification des valeurs de args, gardez en tete que les parametres sont souvent tronques silencieusement avant d'etre traites mais apres la verification seccomp. Cela arrive par exemple si l'ABI i386 est utilisee sur un noyau x86-64 : bien que le noyau n'ira normalement pas regarder au-dela des 32 bits les plus faibles des parametres, les valeurs des registres 64 bits complets seront presentes dans les donnees de seccomp. Un exemple moins surprenant est que si l'ABI x86-64 est utilisee pour effectuer un appel systeme prenant un parametre de type int, la moitie du registre du parametre la plus significative est ignoree par l'appel systeme mais visible dans les donnees de seccomp. Un filtre seccomp renvoie une valeur 32 bits en deux parties : la plus significative, de 16 bits (correspondant au masque defini par la constante SECCOMP_RET_ACTION_FULL), contient une des valeurs << action >> listee ci-dessous ; la moins significative, de 16 bits (definie par la constante SECCOMP_RET_DATA), contient des << data >> a associer a ce code de retour. Si plusieurs filtres existent, ils sont tous executes dans l'ordre inverse de leur apparition dans l'arbre des filtres - si bien que le filtre le plus recemment installe est execute en premier) (remarquez que tous les filtres seront appeles meme si l'un des premiers appeles renvoie SECCOMP_RET_KILL, cela pour simplifier le code du noyau et pour fournir une petite acceleration d'execution d'ensembles de filtres en evitant la verification de ce cas rare). La valeur renvoyee de l'evaluation d'un appel systeme donne est la premiere valeur vue de l'action de plus haute priorite (ainsi que ses donnees associees) renvoyee par l'execution de tous les filtres. Dans l'ordre decroissant de priorite, les valeurs d'action qui peuvent etre renvoyees par un filtre seccomp sont : SECCOMP_RET_KILL_PROCESS (depuis Linux 4.14) Cette valeur aboutit a la fin immediate du processus, avec un vidage memoire. L'appel systeme n'est pas execute. Contrairement a SECCOMP_RET_KILL_THREAD ci-dessous, tous les threads du groupe de threads sont termines (pour un point sur les groupes de thread, voir la description de l'attribut CLONE_THREAD de clone(2)). Le processus se termine parce que il a ete tue par un signal SIGSYS. Meme si un gestionnaire de signal a ete enregistre pour SIGSYS, le gestionnaire sera ignore dans ce cas et le processus se termine toujours. Le processus parent qui attend ce processus (en utilisant waitpid(2) ou equivalent) recoit wstatus qui indique que son enfant s'est termine suite a un signal SIGSYS. SECCOMP_RET_KILL_THREAD (ou SECCOMP_RET_KILL) Cette valeur provoque la fin immediate du thread qui a effectue l'appel systeme. L'appel systeme n'est pas execute. Les autres threads du meme groupe de threads continueront a s'executer. Le thread s'est termine comme tue par un signal SIGSYS. Voir SECCOMP_RET_KILL_PROCESS ci-dessus. Avant Linux 4.11, tout processus qui se terminait de cette maniere ne generait pas de vidage memoire (bien que SIGSYS soit documente dans signal(7) pour avoir comme action par defaut celle de terminer avec un vidage memoire). Depuis Linux 4.11, un processus d'un seul thread creera un vidage memoire s'il se termine dans ce cadre. Avec l'apparition de SECCOMP_RET_KILL_PROCESS dans Linux 4.14, SECCOMP_RET_KILL_THREAD a ete ajoute comme synonyme de SECCOMP_RET_KILL, afin de distinguer plus clairement les deux actions. Remarque : l'utilisation de SECCOMP_RET_KILL_THREAD pour tuer un thread unique d'un processus de plusieurs threads va probablement mettre le processus dans un etat incoherent et corrompre pour toujours son etat. SECCOMP_RET_TRAP Cette valeur fait envoyer par le noyau un signal SIGSYS adresse au thread declencheur (l'appel systeme n'est pas execute). Divers champs seront positionnes dans la structure siginfo_t (voir sigaction(2)) associee au signal : - si_signo contiendra SIGSYS. - si_call_addr affichera l'adresse de l'instruction de l'appel systeme. - si_syscall et si_arch indiqueront l'appel systeme qui a ete tente. - si_code contiendra SYS_SECCOMP. - si_errno contiendra la partie SECCOMP_RET_DATA du code de retour du filtre. Le compteur du programme sera arrete comme si l'appel systeme a ete fait (c'est-a-dire que le compteur du programme ne pointera pas vers l'instruction de l'appel systeme). Le registre du code de retour contiendra une valeur dependante de l'architecture ; en cas de relance de l'execution, positionnez-la sur quelque chose adapte a l'appel systeme (la dependance de l'architecture provient du fait que son remplacement par ENOSYS ecraserait des informations utiles). SECCOMP_RET_ERRNO Cette valeur fait passer la partie SECCOMP_RET_DATA du code de retour du filtre a l'espace utilisateur en tant que valeur errno sans executer l'appel systeme. SECCOMP_RET_USER_NOTIF (depuis Linux 5.0) Faire suivre l'appel systeme a un processus de superviseur attache de l'espace utilisateur attache pour permettre a ce processus de decider quoi faire de l'appel systeme. Si il n'y a pas de superviseur attache (soit parce que le filtre n'a pas ete installe avec l'attribut SECCOMP_FILTER_FLAG_NEW_LISTENER ou parce que le descripteur de fichier etait ferme), le filtre renvoie ENOSYS (c'est similaire a ce qui se produit quand un filtre renvoie SECCOMP_RET_TRACE et qu'il n'y a pas d'observateur). Consultez seccomp_unotify(2) pour plus de details. Remarquez que le processus de superviseur ne sera pas notifie si un autre filtre renvoie une valeur d'action ayant une priorite superieure a SECCOMP_RET_USER_NOTIF. SECCOMP_RET_TRACE Quand cette valeur est renvoyee, le noyau essaiera de notifier a un observateur base sur ptrace(2) avant d'executer l'appel systeme. Si aucun observateur n'est present, l'appel systeme n'est pas execute et renvoie un echec en positionnant errno sur ENOSYS. Un observateur sera notifie s'il demande PTRACE_O_TRACESECCOMP en utilisant ptrace(PTRACE_SETOPTIONS). Il sera notifie d'un PTRACE_EVENT_SECCOMP et la partie SECCOMP_RET_DATA du code de retour du filtre sera mise a la disposition de l'observateur a l'aide de PTRACE_GETEVENTMSG. L'observateur peut ignorer l'appel systeme en modifiant le numero de l'appel systeme a -1. Autrement, l'observateur peut modifier l'appel systeme demande en le passant a un numero d'appel systeme valable. Si l'observateur demande a ignorer l'appel systeme, ce dernier renverra la valeur que l'observateur place dans le registre du code de retour. Avant Linux 4.8, la verification seccomp ne sera pas refaite apres que l'observateur ait recu une notification (cela signifie que sur les anciens noyaux, les conteneurs bases sur seccomp ne doivent pas autoriser l'utilisation de ptrace(2) - meme sur d'autres processus encapsules - sans une prudence extreme ; les ptracers peuvent utiliser ce mecanisme pour sortir d'un conteneur seccomp). Remarquez que le processus d'un observateur ne sera pas notifie si un autre filtre renvoie une valeur d'action ayant une priorite superieure a SECCOMP_RET_TRACE. SECCOMP_RET_LOG (depuis Linux 4.14) Cette valeur fait executer l'appel systeme apres l'enregistrement de l'action de retour du filtre. Un administrateur peut supplanter la journalisation de cette action a l'aide du fichier /proc/sys/kernel/seccomp/actions_logged. SECCOMP_RET_ALLOW Cette valeur provoque l'execution de l'appel systeme. Si on indique un code d'action different de ceux ci-dessus, l'action de filtre est traitee soit comme un SECCOMP_RET_KILL_PROCESS (depuis Linux 4.14), soit comme un SECCOMP_RET_KILL_THREAD (dans Linux 4.13 et anterieurs). /proc interfaces Les fichiers du repertoire /proc/sys/kernel/seccomp offrent des informations et des configurations seccomp supplementaires : actions_avail (depuis Linux 4.14) Une liste ordonnee en lecture seule d'actions de renvoi de filtre seccomp sous la forme d'une chaine. L'ordre, de gauche a droite, est decroissant pour la priorite. La liste represente l'ensemble des actions de renvoi de filtre seccomp gerees par le noyau. actions_logged (depuis Linux 4.14) Une liste ordonnee en lecture-ecriture d'actions de renvoi de filtre seccomp autorisees pour la journalisation. Les ecritures dans le fichier n'ont pas besoin d'etre ordonnees, mais les lectures se feront dans le meme ordre que pour actions_avail. Il est important de remarquer que la valeur de actions_logged n'empeche pas certaines actions de renvoi de filtre d'etre enregistrees quand le sous-systeme d'audit est configure pour auditer une tache. Si l'action n'est pas retrouvee dans le fichier actions_logged, la decision finale d'auditer l'action de cette tache revient au sous-systeme d'audit pour toutes les actions de renvoi de filtre autres que SECCOMP_RET_ALLOW. La chaine << allow >> n'est pas acceptee dans le fichier actions_logged car il n'est pas possible d'enregistrer les actions SECCOMP_RET_ALLOW. Essayer d'ecrire << allow >> dans le fichier echouera avec l'erreur EINVAL. Enregistrement d'audit des actions seccomp Depuis Linux 4.14, le noyau offre la possibilite d'enregistrer les actions renvoyees par des filtres seccomp dans le compte-rendu d'audit. Le noyau prend la decision d'enregistrer une action a partir du type d'action, de sa presence dans le fichier actions_logged et de l'activation de l'audit du noyau (par exemple avec l'option d'amorcage du noyau audit=1). Les regles sont les suivantes : - Si l'action est SECCOMP_RET_ALLOW, l'action n'est pas enregistree. - Sinon, si l'action est SECCOMP_RET_KILL_PROCESS ou SECCOMP_RET_KILL_THREAD et si elle apparait dans le fichier actions_logged, l'action est enregistree. - Sinon, si le filtre a demande l'enregistrement (l'attribut SECCOMP_FILTER_FLAG_LOG) et si elle apparait dans le fichier actions_logged, l'action est enregistree. - Sinon, si l'audit du noyau est active et si le processus doit etre audite (autrace(8)), l'action est enregistree. - Sinon, l'action n'est pas enregistree. VALEUR RENVOYEE En cas de succes, seccomp() renvoie 0. En cas d'erreur, si SECCOMP_FILTER_FLAG_TSYNC a ete utilise, le code de retour est l'identifiant du thread a l'origine de l'echec de la synchronisation (cet identifiant est un identifiant de thread du noyau du type renvoye par clone(2) et gettid(2)). Si une autre erreur arrive, -1 est renvoye et errno est positionne pour indiquer l'erreur. ERREURS seccomp() peut echouer pour les raisons suivantes : EACCES L'appelant n'avait pas la capacite CAP_SYS_ADMIN dans son espace de noms utilisateur ou n'avait pas positionne no_new_privs avant d'utiliser SECCOMP_SET_MODE_FILTER. EBUSY Pendant l'installation d'un nouveau filtre, l'attribut SECCOMP_FILTER_FLAG_NEW_LISTENER a ete indique, mais un filtre precedent a deja ete installe avec cet attribut. EFAULT args n'etait pas une adresse valable. EINVAL L'operation est inconnue ou n'est pas prise en charge par cette version ou cette configuration du noyau. EINVAL Les flags specifies ne sont pas valables pour l'operation donnee. EINVAL L'operation comprenait BPF_ABS, mais la position indiquee n'etait pas alignee sur une limite 32 bits ou elle depassait sizeof(struct seccomp_data). EINVAL Un mode de calcul securise a deja ete defini et l'operation differe du parametrage existant. EINVAL operation indiquait SECCOMP_SET_MODE_FILTER mais le programme de filtre vers lequel pointait args n'etait pas valable ou sa longueur etait de zero ou depassait BPF_MAXINSNS instructions (4096). ENOMEM Plus assez de memoire. ENOMEM La taille totale de tous les programmes de filtre rattaches au thread appelant depasserait MAX_INSNS_PER_PATH instructions (32768). Remarquez qu'afin de calculer cette limite, chaque programme de filtre deja existant integre une penalite de depassement de 4 instructions. EOPNOTSUPP operation indiquait SECCOMP_GET_ACTION_AVAIL mais le noyau ne gere pas l'action de renvoi de filtre indiquee par args. ESRCH Un autre thread a provoque un echec pendant la synchronisation, mais son identifiant n'a pas pu etre determine. STANDARDS Linux. HISTORIQUE Linux 3.17. NOTES Au lieu de coder a la main des filtres seccomp comme demontre dans l'exemple ci-dessous, vous pourriez preferer utiliser la bibliotheque libseccomp qui fournit une interface de generation de filtres seccomp. Le champ Seccomp du fichier /proc/pid/status offre une methode de visualisation du mode seccomp du processus ; voir proc(5). seccomp() fournit un sur-ensemble de fonctionnalites de l'operation PR_SET_SECCOMP de prctl(2) (qui ne prend pas en charge les flags). Depuis Linux 4.4, l'operation PTRACE_SECCOMP_GET_FILTER de ptrace(2) peut etre utilisee pour obtenir les filtres seccomp d'un processus. Gestion d'architecture pour le BPF seccomp La gestion d'architecture pour le filtrage de BPF seccomp est disponible sur les architectures suivantes : - x86-64, i386, x32 (depuis Linux 3.5) - ARM (depuis Linux 3.8) - s390 (depuis Linux 3.8) - MIPS (depuis Linux 3.16) - ARM-64 (depuis Linux 3.19) - PowerPC (depuis Linux 4.3) - Tile (depuis Linux 4.3) - PA-RISC (depuis Linux 4.6) Mises en garde Il y a beaucoup de subtilites a prendre en compte lorsqu'on applique des filtres seccomp a un programme, notamment : - Certains appels systeme traditionnels ont des implementations dans l'espace utilisateur dans le vdso(7) de nombreuses architectures. Parmi les exemples remarquables, se trouvent clock_gettime(2), gettimeofday(2) et time(2). Sur de telles architectures, le filtrage seccomp de ces appels systeme sera sans effet (il y a cependant des cas ou les implementations vdso(7) se rabattent sur le veritable appel systeme, alors les filtres seccomp verraient l'appel systeme). - Le filtrage seccomp s'appuie sur les numeros d'appel systeme. Cependant, les applications n'appellent generalement pas directement les appels systeme, mais plutot les fonctions enveloppe de la bibliotheque C qui appellent a leur tour les appels systeme. Par consequent, vous devez garder en tete ce qui suit : - Les enveloppes de la glibc pour certains appels systeme traditionnels peuvent utiliser des appels systeme ayant des noms differents dans le noyau. Par exemple, la fonction enveloppe exit(2) utilise en fait l'appel systeme exit_group(2) et la fonction fork(2) utilise en realite les appels clone(2). - Le comportement des fonctions enveloppe peut changer en fonction des architectures, selon la plage d'appels systeme fournie sur ces architectures. Autrement dit, la meme fonction enveloppe peut appeler differents appels systeme selon les architectures. - Enfin, le comportement des fonctions enveloppe peut changer selon les versions de la glibc. Par exemple, dans d'anciennes versions, la fonction enveloppe de la glibc de open(2) appelait l'appel systeme du meme nom, mais a partir de la 2.26, l'implementation est passee a l'appel openat(2) sur toutes les architectures. La consequence des points ci-dessus est qu'il pourrait etre necessaire de filtrer un appel systeme autre que celui prevu. Plusieurs pages de manuel de la section 2 donnent des details utiles sur les differences entre les fonctions enveloppe et les appels systeme sous-jacents dans les sous-sections intitulees Differences entre le noyau et la bibliotheque C. En outre, remarquez que l'application de filtres seccomp risque meme de provoquer des bogues dans une application, quand les filtres provoquent des echecs inattendus d'operations legitimes que l'application a besoin d'effectuer. De tels bogues pourraient ne pas etre facilement identifies lors d'un test des filtres seccomp s'ils se produisent a des endroits du code rarement utilises. Details BPF specifiques a seccomp Remarquez que les details BPF suivants sont specifiques aux filtres seccomp : - Les modificateurs de taille BPF_H et BPF_B ne sont pas pris en charge : toutes les operations doivent charger et stocker des mots (4 octets) (BPF_W). - Pour acceder au contenu du tampon seccomp_data, utilisez le modificateur du mode d'adressage BPF_ABS. - Le modificateur du mode d'adressage BPF_LEN produit un operande de mode immediatement dont la valeur est la taille du tampon seccomp_data. EXEMPLES Le programme ci-dessous accepte quatre parametres ou plus. Les trois premiers sont un numero d'appel systeme, un identifiant numerique d'architecture et un numero d'erreur. Le programme utilise ces valeurs pour construire un filtre BPF utilise lors de l'execution pour effectuer les verifications suivantes : - Si le programme ne tourne pas sur l'architecture indiquee, le filtre BPF fait echouer les appels systeme avec l'erreur ENOSYS. - Si le programme essaie d'executer l'appel systeme ayant le numero indique, le filtre BPF fait echouer l'appel systeme en positionnant errno sur le numero d'erreur indique. Les autres parametres de la ligne de commande indiquent le chemin et les parametres supplementaires d'un programme que notre exemple doit essayer d'executer en utilisant execv(3) (une fonction de bibliotheque qui utilise l'appel systeme execve(2)). Certains exemples d'execution du programme sont presentes ci-dessous. Tout d'abord, on affiche l'architecture sur laquelle on est (x86-64) puis on construit une fonction d'interpreteur qui cherche les numeros d'appels systeme sur cette architecture : $ uname -m x86_64 $ syscall_nr() { cat /usr/src/linux/arch/x86/syscalls/syscall_64.tbl | \ awk '$2 != "x32" && $3 == "'$1'" { print $1 }' } Quand le filtre BPF rejette un appel systeme (cas n 2 ci-dessus), il fait echouer l'appel systeme avec le numero d'erreur indique sur la ligne de commande. Dans les exemples presentes ici, nous utiliserons le numero d'erreur 99 : $ errno 99 EADDRNOTAVAIL 99 Ne peut pas affecter l'adresse demandee Dans l'exemple suivant, on essaie d'executer la commande whoami(1), mais le filtre BPF rejette l'appel systeme execve(2), donc la commande n'est meme pas executee : $ syscall_nr execve 59 $ ./a.out Utilisation : ./a.out [] Astuce pour : AUDIT_ARCH_I386: 0x40000003 AUDIT_ARCH_X86_64 : 0xC000003E $ ./a.out 59 0xC000003E 99 /bin/whoami execv : Ne peut pas affecter l'adresse demandee Dans le prochain exemple, le filtre BPF rejette l'appel systeme write(2) pour que, meme si elle a pu demarrer, la commande whoami(1) ne puisse pas ecrire de sortie : $ syscall_nr write 1 $ ./a.out 1 0xC000003E 99 /bin/whoami Dans le dernier exemple, le filtre BPF rejette un appel systeme qui n'est pas utilise par la commande whoami(1), elle peut donc s'executer et produire une sortie : $ syscall_nr preadv 295 $ ./a.out 295 0xC000003E 99 /bin/whoami cecilia Source du programme #include #include #include #include #include #include #include #include #include #define X32_SYSCALL_BIT 0x40000000 #define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0])) static int install_filter(int syscall_nr, unsigned int t_arch, int f_errno) { unsigned int upper_nr_limit = 0xffffffff; /* On suppose que AUDIT_ARCH_X86_64 renvoie a l'ABI x86-64 normale (dans l'ABI x32, tous les appels systeme ont le bit 30 positionne dans le champ 'nr', donc les numeros sont >= X32_SYSCALL_BIT). */ if (t_arch == AUDIT_ARCH_X86_64) upper_nr_limit = X32_SYSCALL_BIT - 1; struct sock_filter filter[] = { /* [0] Charger l'architecture depuis le tampon 'seccomp_data' dans l'accumulateur. */ BPF_STMT(BPF_LD | BPF_W | BPF_ABS, (offsetof(struct seccomp_data, arch))), /* [1] Avancer de 5 instructions si l'architecture ne correspond pas a 't_arch'. */ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, t_arch, 0, 5), /* [2] Charger le numero d'appel systeme depuis le tampon 'seccomp_data' dans l'accumulateur. */ BPF_STMT(BPF_LD | BPF_W | BPF_ABS, (offsetof(struct seccomp_data, nr))), /* [3] Verifier l'ABI - necessaire seulement pour x86-64 si on utilise une liste d'interdictions. Utiliser BPF_JGT au lieu de verifier par rapport au masque de bits pour ne pas devoir recharger le numero d'appel systeme. */ BPF_JUMP(BPF_JMP | BPF_JGT | BPF_K, upper_nr_limit, 3, 0), /* [4] Avancer d'une instruction si le numero d'appel systeme ne correspond pas a 'syscall_nr'. */ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, syscall_nr, 0, 1), /* [5] Architecture et appel systeme correspondants : ne pas executer l'appel systeme et renvoyer 'f_errno' dans 'errno'. */ BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ERRNO | (f_errno & SECCOMP_RET_DATA)), /* [6] Cible du numero d'appel systeme inadequate : autoriser d'autres appels systeme. */ BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW), /* [7] Cible de l'architecture inadequate : tuer le processus. */ BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_KILL_PROCESS), }; struct sock_fprog prog = { .len = ARRAY_SIZE(filter), .filter = filter, }; if (syscall(SYS_seccomp, SECCOMP_SET_MODE_FILTER, 0, &prog)) { perror("seccomp"); return 1; } return 0; } int main(int argc, char *argv[]) { if (argc < 5) { fprintf(stderr, "Utilisation : " "%s []\n" "Astuce pour : AUDIT_ARCH_I386: 0x%X\n" " AUDIT_ARCH_X86_64: 0x%X\n" "\n", argv[0], AUDIT_ARCH_I386, AUDIT_ARCH_X86_64); exit(EXIT_FAILURE); } if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) { perror("prctl"); exit(EXIT_FAILURE); } if (install_filter(strtol(argv[1], NULL, 0), strtoul(argv[2], NULL, 0), strtol(argv[3], NULL, 0))) exit(EXIT_FAILURE); execv(argv[4], &argv[4]); perror("execv"); exit(EXIT_FAILURE); } VOIR AUSSI bpfc(1), strace(1), bpf(2), prctl(2), ptrace(2), seccomp_unotify(2), sigaction(2), proc(5), signal(7), socket(7) Plusieurs pages de la bibliotheque libseccomp, dont : scmp_sys_resolver(1), seccomp_export_bpf(3), seccomp_init(3), seccomp_load(3) et seccomp_rule_add(3). Les fichiers Documentation/networking/filter.txt et Documentation/userspace-api/seccomp_filter.rst des sources du noyau (ou Documentation/prctl/seccomp_filter.txt avant Linux 4.13). McCanne, S. et Jacobson, V. (1992) The BSD Packet Filter : une nouvelle architecture de captation de paquets au niveau utilisateur, colloque de la conference USENIX a l'hiver 1993 TRADUCTION La traduction francaise de cette page de manuel a ete creee par Christophe Blaess , Stephan Rafin , Thierry Vignaud , Francois Micaux, Alain Portal , Jean-Philippe Guerard , Jean-Luc Coulon (f5ibh) , Julien Cristau , Thomas Huriaux , Nicolas Francois , Florentin Duneau , Simon Paillard , Denis Barbier , David Prevot , Jean-Philippe MENGUAL et Jean-Pierre Giraud Cette traduction est une documentation libre ; veuillez vous reporter a la GNU General Public License version 3 concernant les conditions de copie et de distribution. Il n'y a aucune RESPONSABILITE LEGALE. Si vous decouvrez un bogue dans la traduction de cette page de manuel, veuillez envoyer un message a . Pages du manuel de Linux 6.06 31 octobre 2023 seccomp(2)