Objectif du chapitre
Ce chapitre explique comment Linux associe une identité de sécurité à chaque processus.
Dans le chapitre précédent, nous avons étudié les bases utilisateurs et groupes, notamment avec :
Code: Select all
getpwent()
getpwuid()
getgrent()
getgrgid()
Code: Select all
/etc/passwd
/etc/group
Le chapitre actuel répond à une autre question, beaucoup plus importante pour la sécurité système :Quels utilisateurs et quels groupes existent sur le système ?
C’est donc un chapitre central pour comprendre :Avec quels droits ce processus tourne-t-il actuellement ?
- les droits des processus ;
- l’identité réelle d’un processus ;
- l’identité effective utilisée pour les permissions ;
- le fonctionnement des programmes set-user-ID et set-group-ID ;
- le rôle de root ;
- les groupes supplémentaires ;
- le fait qu’un programme puisse temporairement obtenir, perdre ou récupérer des privilèges.
1. Notion générale de credentials
Sous Linux, chaque processus possède un ensemble d’identifiants numériques associés à la sécurité.
Ces identifiants sont appelés :
On peut traduire cela par :process credentials
ou plus simplement :informations d’identification du processus
Ces credentials indiquent au noyau avec quelle identité le processus s’exécute et quels droits il possède.identité de sécurité du processus
Les identifiants principaux sont :
- le real user ID ;
- le real group ID ;
- l’effective user ID ;
- l’effective group ID ;
- le saved set-user-ID ;
- le saved set-group-ID ;
- le file-system user ID ;
- le file-system group ID ;
- les supplementary group IDs.
Par exemple, sous Linux :
Code: Select all
UID 0 = root
UID 1000 = utilisateur classique
2. Real User ID et Real Group ID
Le real user ID correspond à l’utilisateur réel qui a lancé le processus.
On le récupère avec :
Code: Select all
#include <unistd.h>
uid_t getuid(void);
gid_t getgid(void);
Code: Select all
getuid()
La fonction :
Code: Select all
getgid()
Exemple :
Code: Select all
#include <stdio.h>
#include <unistd.h>
int main(void)
{
printf("Real UID : %d\n", (int)getuid());
printf("Real GID : %d\n", (int)getgid());
return 0;
}
Code: Select all
Real UID : 1000
Real GID : 1000
Il ne répond pas toujours à la question :Qui a lancé le programme ?
Cette deuxième question dépend surtout de l’effective UID.Avec quels droits le programme accède-t-il aux fichiers ?
3. Effective User ID et Effective Group ID
L’effective user ID est l’un des concepts les plus importants du chapitre.
On le récupère avec :
Code: Select all
#include <unistd.h>
uid_t geteuid(void);
gid_t getegid(void);
Code: Select all
geteuid()
La fonction :
Code: Select all
getegid()
Le point fondamental est le suivant :
Donc, pour savoir si un processus peut ouvrir, lire ou écrire un fichier, le noyau regarde principalement :Sous Linux, les permissions d’accès sont généralement vérifiées à partir de l’effective user ID et de l’effective group ID.
- l’effective UID ;
- l’effective GID ;
- les groupes supplémentaires ;
- les permissions du fichier.
Code: Select all
#include <stdio.h>
#include <unistd.h>
int main(void)
{
printf("Real UID : %d\n", (int)getuid());
printf("Effective UID : %d\n", (int)geteuid());
printf("Real GID : %d\n", (int)getgid());
printf("Effective GID : %d\n", (int)getegid());
return 0;
}
Code: Select all
Real UID : 1000
Effective UID : 1000
Code: Select all
Real UID : 1000
Effective UID : 0
- l’utilisateur réel est jean ;
- mais le programme s’exécute avec les privilèges effectifs de root.
4. Différence entre real UID et effective UID
La différence est essentielle.
Le real UID indique :
L’effective UID indique :l’utilisateur réel qui a lancé le programme.
Exemple mental :l’identité utilisée par le noyau pour vérifier les permissions.
Code: Select all
Real UID = jean
Effective UID = root
- jean a lancé le programme ;
- mais le programme possède les droits de root pendant son exécution.
L’exemple classique est :
Code: Select all
/usr/bin/passwd
Mais le fichier qui contient les mots de passe chiffrés est :
Code: Select all
/etc/shadow
Un utilisateur normal ne peut pas modifier directement :
Code: Select all
/etc/shadow
Code: Select all
passwd
5. Le bit set-user-ID
Le bit set-user-ID, souvent abrégé setuid, est un bit spécial de permission sur un fichier exécutable.
Il permet de dire :
Exemple :Quand ce programme est exécuté, son effective UID devient l’UID du propriétaire du fichier.
Code: Select all
ls -l /usr/bin/passwd
Code: Select all
-rwsr-xr-x 1 root root 68208 /usr/bin/passwd
Code: Select all
s
Code: Select all
-rwsr-xr-x
Le fichier appartient à :
Code: Select all
root root
Code: Select all
passwd
Code: Select all
Real UID = UID de l'utilisateur
Effective UID = 0
C’est un mécanisme très puissant, mais aussi très dangereux si le programme contient une faille.le programme est lancé par l’utilisateur, mais il s’exécute avec les privilèges de root.
6. Créer un programme setuid root
Exemple pédagogique simple :
Code: Select all
#include <stdio.h>
#include <unistd.h>
int main(void)
{
printf("Real UID : %d\n", (int)getuid());
printf("Effective UID : %d\n", (int)geteuid());
return 0;
}
Code: Select all
gcc main.c -o prog
Code: Select all
sudo chown root:root prog
Code: Select all
sudo chmod u+s prog
Code: Select all
ls -l prog
Code: Select all
-rwsr-xr-x 1 root root ... prog
Code: Select all
./prog
Code: Select all
Real UID : 1000
Effective UID : 0
7. Root : UID 0
Sous Linux, root n’est pas magique parce qu’il s’appelle root.
Root est spécial parce que son UID vaut :
Code: Select all
0
Donc, en pratique :
Code: Select all
if (geteuid() == 0)
{
printf("Le processus est privilégié.\n");
}
Ce qu’il faut retenir :
Et surtout :UID 0 = superutilisateur.
Ce sont les nombres qui comptent, pas les noms.
8. Set-group-ID
Le mécanisme set-group-ID est similaire au set-user-ID, mais pour le groupe.
Le bit setgid permet de dire :
Exemple de permissions :Quand ce programme est exécuté, son effective GID devient le GID du groupe propriétaire du fichier.
Code: Select all
-rwxr-sr-x
Le setgid est utile quand un programme doit accéder à des ressources appartenant à un groupe spécifique.
Par exemple, un programme peut temporairement obtenir les droits d’un groupe particulier sans que l’utilisateur soit directement membre principal de ce groupe.
9. Programmes privilégiés et sécurité
Un programme setuid root est extrêmement sensible.
Pourquoi ?
Parce qu’un utilisateur normal peut l’exécuter, mais le programme tourne avec :
Code: Select all
Effective UID = 0
Exemples de risques :
- buffer overflow ;
- injection de commandes ;
- mauvaise validation d’un chemin de fichier ;
- utilisation dangereuse de variables d’environnement ;
- mauvaise gestion des fichiers temporaires ;
- race condition ;
- appel à system() avec des données contrôlées par l’utilisateur.
Règle importante :
Bonne pratique :Un programme setuid doit faire le moins possible avec les privilèges élevés.
- obtenir les privilèges uniquement quand c’est nécessaire ;
- les abandonner dès que possible ;
- vérifier toutes les entrées utilisateur ;
- éviter les appels dangereux ;
- ne jamais faire confiance à l’environnement ;
- utiliser des chemins absolus ;
- limiter les permissions au strict nécessaire.
10. Saved set-user-ID et saved set-group-ID
Le saved set-user-ID permet à un programme de sauvegarder une identité privilégiée afin de pouvoir la récupérer plus tard.
C’est très important pour les programmes qui doivent :
- obtenir temporairement des privilèges ;
- les abandonner temporairement ;
- puis les récupérer plus tard si nécessaire.
Code: Select all
Real UID = 1000
Effective UID = 0
Saved UID = 0
Code: Select all
seteuid(getuid());
Code: Select all
Real UID = 1000
Effective UID = 1000
Saved UID = 0
Mais comme son saved UID vaut encore 0, il peut récupérer root avec :
Code: Select all
seteuid(0);
C’est très important en sécurité.
11. Drop privileges : perdre temporairement ou définitivement root
Un programme privilégié peut vouloir abandonner ses privilèges.
Il y a deux grandes idées :
- abandon temporaire ;
- abandon définitif.
Exemple :
Code: Select all
seteuid(getuid());
S’il avait un saved UID privilégié, il peut parfois récupérer les privilèges plus tard avec :
Code: Select all
seteuid(0);
Exemple :
Code: Select all
setuid(getuid());
Cela peut empêcher le processus de récupérer root ensuite.
Idée générale :
seteuid() est souvent utilisé pour abandonner temporairement les privilèges.
Il faut cependant faire attention, car les règles précises dépendent des appels utilisés et du fait que le processus soit privilégié ou non.setuid() peut être utilisé pour abandonner définitivement les privilèges.
12. Les appels getresuid() et getresgid()
Linux fournit des appels permettant de récupérer directement les trois identifiants :
- real UID ;
- effective UID ;
- saved set-user-ID.
Code: Select all
#define _GNU_SOURCE
#include <unistd.h>
int getresuid(uid_t *ruid, uid_t *euid, uid_t *suid);
int getresgid(gid_t *rgid, gid_t *egid, gid_t *sgid);
Code: Select all
#define _GNU_SOURCE
#include <stdio.h>
#include <unistd.h>
int main(void)
{
uid_t ruid;
uid_t euid;
uid_t suid;
if (getresuid(&ruid, &euid, &suid) == -1)
{
perror("getresuid");
return 1;
}
printf("Real UID : %d\n", (int)ruid);
printf("Effective UID : %d\n", (int)euid);
printf("Saved UID : %d\n", (int)suid);
return 0;
}
13. setuid()
Prototype :
Code: Select all
#include <unistd.h>
int setuid(uid_t uid);
Code: Select all
setuid()
Son comportement dépend fortement du contexte.
Pour un processus non privilégié, setuid() est très limité.
Un processus normal ne peut pas décider de devenir root par magie.
Par exemple :
Code: Select all
setuid(0);
Pour un processus privilégié, setuid() peut modifier l’identité de manière plus profonde.
Point important :
Il faut que le processus soit déjà privilégié, ou qu’il ait été lancé via un mécanisme comme setuid root.Un programme normal ne peut pas devenir root simplement en appelant setuid(0).
14. seteuid()
Prototype :
Code: Select all
#include <unistd.h>
int seteuid(uid_t euid);
Code: Select all
seteuid()
Elle est souvent utilisée par les programmes setuid pour alterner entre :
- mode privilégié ;
- mode non privilégié.
Code: Select all
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
int main(void)
{
uid_t user = getuid();
printf("Début : real=%d effective=%d\n", (int)getuid(), (int)geteuid());
if (seteuid(user) == -1)
{
perror("seteuid drop");
return 1;
}
printf("Après drop : real=%d effective=%d\n", (int)getuid(), (int)geteuid());
if (seteuid(0) == -1)
{
perror("seteuid regain");
return 1;
}
printf("Après regain : real=%d effective=%d\n", (int)getuid(), (int)geteuid());
return 0;
}
Dans un programme normal, ça ne marchera pas.
15. setreuid() et setregid()
Prototypes :
Code: Select all
#include <unistd.h>
int setreuid(uid_t ruid, uid_t euid);
int setregid(gid_t rgid, gid_t egid);
Exemple :
Code: Select all
setreuid(-1, getuid());
- -1 signifie : ne pas changer cette valeur ;
- getuid() est utilisé pour mettre l’effective UID à la valeur du real UID.
Ces fonctions existent pour donner plus de contrôle au programme, mais elles doivent être utilisées avec prudence.
16. setresuid() et setresgid()
Prototypes :
Code: Select all
#define _GNU_SOURCE
#include <unistd.h>
int setresuid(uid_t ruid, uid_t euid, uid_t suid);
int setresgid(gid_t rgid, gid_t egid, gid_t sgid);
- le real UID ;
- l’effective UID ;
- le saved UID.
Exemple :
Code: Select all
setresuid(getuid(), getuid(), getuid());
Cela peut être utilisé pour abandonner définitivement les privilèges.
Ces appels sont très utiles quand on veut éviter les ambiguïtés des anciens appels comme setuid() et seteuid().
17. File-system User ID et File-system Group ID
Linux possède aussi des identifiants spécifiques aux vérifications du système de fichiers :
- file-system user ID ;
- file-system group ID.
Code: Select all
#include <sys/fsuid.h>
int setfsuid(uid_t fsuid);
int setfsgid(gid_t fsgid);
Historiquement, ils servaient notamment pour des cas comme les serveurs de fichiers.
À ton niveau, il faut surtout retenir :
Dans la majorité des programmes classiques, on ne l’utilise pas directement.Le fsuid est une identité Linux spécifique utilisée pour les permissions du système de fichiers.
Mais il est important de savoir qu’il existe, car le livre l’explique dans le modèle complet des credentials Linux.
18. Supplementary Group IDs
Un processus n’a pas seulement un GID principal.
Il peut aussi avoir plusieurs groupes supplémentaires.
Ces groupes supplémentaires sont importants pour les permissions.
Exemple :
Code: Select all
groups
Code: Select all
jean adm cdrom sudo dip plugdev users docker
Un processus lancé par cet utilisateur peut hériter de ces groupes supplémentaires.
Ces groupes sont utilisés dans les vérifications d’accès.
Exemple :
Un fichier appartient au groupe :
Code: Select all
docker
Pour récupérer les groupes supplémentaires :
Code: Select all
#include <unistd.h>
#include <sys/types.h>
int getgroups(int size, gid_t list[]);
Code: Select all
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <stdlib.h>
int main(void)
{
int n;
gid_t *groups;
n = getgroups(0, NULL);
if (n == -1)
{
perror("getgroups");
return 1;
}
groups = malloc(sizeof(gid_t) * n);
if (groups == NULL)
{
perror("malloc");
return 1;
}
if (getgroups(n, groups) == -1)
{
perror("getgroups");
free(groups);
return 1;
}
for (int i = 0; i < n; i++)
{
printf("Group ID : %d\n", (int)groups[i]);
}
free(groups);
return 0;
}
Code: Select all
getgroups(0, NULL)
Ensuite, on alloue un tableau assez grand.
Puis on rappelle :
Code: Select all
getgroups(n, groups)
19. setgroups() et initgroups()
Pour modifier les groupes supplémentaires, on trouve notamment :
Code: Select all
#include <grp.h>
int setgroups(size_t size, const gid_t *list);
int initgroups(const char *user, gid_t group);
Cette fonction remplace la liste des groupes supplémentaires du processus.
Elle est réservée aux processus privilégiés.
Un utilisateur normal ne peut pas changer librement ses groupes.
initgroups()
Cette fonction initialise les groupes supplémentaires d’un utilisateur à partir de la base des groupes.
Elle est typiquement utilisée par des programmes comme login, su ou des services qui changent d’utilisateur.
Exemple mental :
Dans ce cas, initgroups() peut être utilisée pour initialiser correctement les groupes supplémentaires.Je vais exécuter ce service en tant que l’utilisateur www-data, donc je dois charger les groupes de www-data.
20. Relation entre ce chapitre et le chapitre précédent
Le chapitre précédent parlait des informations stockées sur les utilisateurs et les groupes.
Exemples :
Code: Select all
getpwuid()
getpwent()
getgrgid()
getgrent()
- le nom d’un utilisateur ;
- son UID ;
- son GID principal ;
- son home directory ;
- son shell ;
- les groupes définis dans le système.
Exemples :
Code: Select all
getuid()
geteuid()
setuid()
seteuid()
getgroups()
- quel utilisateur réel a lancé le processus ;
- quel utilisateur effectif est utilisé pour les permissions ;
- si le processus tourne avec des droits root ;
- quels groupes sont actifs pour ce processus ;
- si le processus peut perdre ou récupérer des privilèges.
[table]
[tr][td]Chapitre précédent[/td][td]Chapitre actuel[/td][/tr]
[tr][td]Base utilisateurs/groupes[/td][td]Droits du processus[/td][/tr]
[tr][td]/etc/passwd[/td][td]UID réel/effectif/sauvegardé[/td][/tr]
[tr][td]/etc/group[/td][td]GID réel/effectif/sauvegardé[/td][/tr]
[tr][td]getpwent(), getpwuid()[/td][td]getuid(), geteuid()[/td][/tr]
[tr][td]getgrent(), getgrgid()[/td][td]getgid(), getegid(), getgroups()[/td][/tr]
[tr][td]Informations statiques[/td][td]État runtime du processus[/td][/tr]
[/table]
Formule très simple :
Le chapitre précédent dit quels comptes existent.
Ce chapitre dit avec quels droits un processus s’exécute.
21. Exemple complet : afficher les credentials d’un processus
Voici un exemple plus complet qui affiche les UID, GID et groupes supplémentaires du processus.
Code: Select all
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
int main(void)
{
uid_t ruid;
uid_t euid;
uid_t suid;
gid_t rgid;
gid_t egid;
gid_t sgid;
int nb_groups;
gid_t *groups;
if (getresuid(&ruid, &euid, &suid) == -1)
{
perror("getresuid");
return 1;
}
if (getresgid(&rgid, &egid, &sgid) == -1)
{
perror("getresgid");
return 1;
}
printf("UIDs\n");
printf(" real : %d\n", (int)ruid);
printf(" effective : %d\n", (int)euid);
printf(" saved : %d\n", (int)suid);
printf("\nGIDs\n");
printf(" real : %d\n", (int)rgid);
printf(" effective : %d\n", (int)egid);
printf(" saved : %d\n", (int)sgid);
nb_groups = getgroups(0, NULL);
if (nb_groups == -1)
{
perror("getgroups");
return 1;
}
groups = malloc(sizeof(gid_t) * nb_groups);
if (groups == NULL)
{
perror("malloc");
return 1;
}
if (getgroups(nb_groups, groups) == -1)
{
perror("getgroups");
free(groups);
return 1;
}
printf("\nSupplementary groups\n");
for (int i = 0; i < nb_groups; i++)
{
printf(" group[%d] = %d\n", i, (int)groups[i]);
}
free(groups);
return 0;
}
Code: Select all
gcc main.c -o creds
Code: Select all
./creds
22. Exemple : vérifier si le processus est root
Un programme peut tester s’il est lancé avec des privilèges root :
Code: Select all
#include <stdio.h>
#include <unistd.h>
int main(void)
{
if (geteuid() == 0)
{
printf("Le processus tourne avec les privilèges root.\n");
}
else
{
printf("Le processus ne tourne pas avec les privilèges root.\n");
}
return 0;
}
Code: Select all
geteuid()
Code: Select all
getuid()
Parce que ce qui compte pour les permissions, c’est l’UID effectif.
Un programme setuid root peut avoir :
Code: Select all
getuid() = 1000
geteuid() = 0
Code: Select all
geteuid() == 0
23. Exemple : afficher le nom utilisateur à partir de l’UID
Ici, on combine le chapitre précédent et le chapitre actuel.
On récupère l’UID du processus avec :
Code: Select all
getuid()
Code: Select all
getpwuid()
Code: Select all
#include <stdio.h>
#include <unistd.h>
#include <pwd.h>
#include <sys/types.h>
int main(void)
{
uid_t uid;
struct passwd *pwd;
uid = getuid();
pwd = getpwuid(uid);
if (pwd == NULL)
{
perror("getpwuid");
return 1;
}
printf("UID réel : %d\n", (int)uid);
printf("Nom utilisateur : %s\n", pwd->pw_name);
printf("Home directory : %s\n", pwd->pw_dir);
printf("Shell : %s\n", pwd->pw_shell);
return 0;
}
- getuid() vient du chapitre credentials ;
- getpwuid() vient du chapitre utilisateurs/groupes.
24. Analogie avec Windows
Pour faire le lien avec Windows, on peut comparer les concepts comme ceci :
[table]
[tr][td]Linux[/td][td]Windows[/td][/tr]
[tr][td]UID[/td][td]SID utilisateur[/td][/tr]
[tr][td]GID[/td][td]SID de groupe[/td][/tr]
[tr][td]Supplementary groups[/td][td]Groupes dans le token[/td][/tr]
[tr][td]Effective UID[/td][td]Token utilisé pour les accès[/td][/tr]
[tr][td]setuid root[/td][td]Exécution avec privilèges élevés[/td][/tr]
[tr][td]seteuid()[/td][td]Changement temporaire d’identité / impersonation simplifiée[/td][/tr]
[tr][td]UID 0[/td][td]Compte administrateur / SYSTEM selon contexte[/td][/tr]
[/table]
La différence est que le modèle UNIX historique est plus simple :
- UID ;
- GID ;
- permissions rwx ;
- root UID 0.
- access token ;
- SID ;
- ACL ;
- privileges ;
- integrity levels ;
- impersonation tokens ;
- security descriptors.
Un processus possède une identité de sécurité utilisée pour décider ce qu’il peut faire.
25. Ce qu’un processus normal ne peut pas faire
Un point fondamental :
Ce code :Un processus normal ne peut pas devenir root tout seul.
Code: Select all
setuid(0);
Si un utilisateur normal lance un programme normal, alors l’appel échouera généralement avec :
Code: Select all
Operation not permitted
- qu’il soit lancé par root ;
- ou qu’il soit un programme setuid root ;
- ou qu’il dispose d’une capacité Linux appropriée ;
- ou qu’il soit exécuté dans un contexte déjà privilégié.
26. Capabilities Linux
Le livre mentionne aussi que Linux moderne peut diviser certains privilèges root en capabilities.
Historiquement, root possédait tous les privilèges.
Linux a ensuite introduit les capabilities pour découper les privilèges.
Exemples de capabilities :
Code: Select all
CAP_NET_ADMIN
CAP_SYS_ADMIN
CAP_SETUID
CAP_SETGID
Par exemple :
- un programme peut avoir le droit de configurer le réseau ;
- sans avoir absolument tous les privilèges de root.
Mais le modèle historique UID/GID/setuid reste la base à comprendre.Les capabilities sont une version plus fine des privilèges root.
27. Résumé des fonctions importantes
Lire les identités du processus
Code: Select all
getuid()
geteuid()
getgid()
getegid()
getgroups()
getresuid()
getresgid()
Code: Select all
setuid()
seteuid()
setgid()
setegid()
setreuid()
setregid()
setresuid()
setresgid()
Code: Select all
setgroups()
initgroups()
Code: Select all
setfsuid()
setfsgid()
28. Ce qu’il faut retenir absolument
À retenir par cœur :
- Chaque processus possède des credentials.
- Le real UID indique qui a lancé le programme.
- L’effective UID indique avec quels droits le programme agit.
- Les permissions sont généralement vérifiées avec l’effective UID et l’effective GID.
- root correspond à l’UID 0.
- Un programme setuid root peut tourner avec Effective UID = 0 même s’il est lancé par un utilisateur normal.
- Le saved UID permet de perdre temporairement puis de récupérer certains privilèges.
- Les groupes supplémentaires participent aux vérifications de permissions.
- Un processus normal ne peut pas devenir root simplement en appelant setuid(0).
- Les programmes setuid sont puissants mais dangereux.
29. Résumé final du chapitre
Ce chapitre explique comment Linux gère les droits d’un processus.
Le noyau ne se contente pas de savoir quel utilisateur existe dans /etc/passwd. Il associe à chaque processus une identité de sécurité complète.
Cette identité contient plusieurs UID et GID :
- real UID/GID ;
- effective UID/GID ;
- saved UID/GID ;
- file-system UID/GID ;
- groupes supplémentaires.
Le mécanisme setuid permet à un programme d’obtenir les privilèges du propriétaire du fichier exécutable. Si le propriétaire est root et que le bit setuid est activé, le programme peut s’exécuter avec les droits de root.
C’est ce qui permet à des programmes comme passwd d’effectuer des opérations privilégiées tout en étant lancés par des utilisateurs normaux.
Cependant, ce mécanisme est très sensible en sécurité. Un programme setuid mal écrit peut devenir une faille critique.
Ce chapitre est donc une base fondamentale pour comprendre :
- la sécurité UNIX ;
- les permissions Linux ;
- les programmes privilégiés ;
- la différence entre utilisateur réel et utilisateur effectif ;
- le fonctionnement de root ;
- les futures notions comme capabilities, démons, sandboxing et sécurité système.
Le chapitre précédent décrit les comptes du système ; ce chapitre décrit les droits réels d’un processus en cours d’exécution.
Sous Linux, ce n’est pas seulement qui lance le programme qui compte, mais surtout avec quelle identité effective le processus s’exécute.
