Privilèges des processus Linux : UID, EUID, SUID et sécurité

Ce forum est dédié à apprendre le développement de programmes user mode sur Linux

Moderator: Rick

Post Reply
Hydraxx
Site Admin
Posts: 46
Joined: Mon Jan 12, 2026 4:04 pm
Location: France
Contact:

Privilèges des processus Linux : UID, EUID, SUID et sécurité

Post by Hydraxx »

Chapitre 9 — Process Credentials : identités, groupes et privilèges des processus sous Linux

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()
Ces fonctions permettent surtout de lire les informations stockées dans les bases système comme :

Code: Select all

/etc/passwd
/etc/group
Autrement dit, le chapitre précédent répondait surtout à la question :
Quels utilisateurs et quels groupes existent sur le système ?
Le chapitre actuel répond à une autre question, beaucoup plus importante pour la sécurité système :
Avec quels droits ce processus tourne-t-il actuellement ?
C’est donc un chapitre central pour comprendre :
  • 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 :
process credentials
On peut traduire cela par :
informations d’identification du processus
ou plus simplement :
identité de sécurité du processus
Ces credentials indiquent au noyau avec quelle identité le processus s’exécute et quels droits il possède.

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.
Ces identifiants sont tous numériques.

Par exemple, sous Linux :

Code: Select all

UID 0 = root
UID 1000 = utilisateur classique
Le nom de l’utilisateur n’est qu’une représentation lisible. Pour le noyau, ce qui compte réellement, ce sont les nombres.


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);
La fonction :

Code: Select all

getuid()
retourne le real user ID du processus appelant.

La fonction :

Code: Select all

getgid()
retourne le real group ID du processus appelant.

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;
}
Si l’utilisateur jean lance le programme, le processus aura probablement :

Code: Select all

Real UID : 1000
Real GID : 1000
Le real UID répond donc à la question :
Qui a lancé le programme ?
Il ne répond pas toujours à la question :
Avec quels droits le programme accède-t-il aux fichiers ?
Cette deuxième question dépend surtout de l’effective UID.


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);
La fonction :

Code: Select all

geteuid()
retourne l’UID effectif du processus.

La fonction :

Code: Select all

getegid()
retourne le GID effectif du processus.

Le point fondamental est le suivant :
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.
Donc, pour savoir si un processus peut ouvrir, lire ou écrire un fichier, le noyau regarde principalement :
  • l’effective UID ;
  • l’effective GID ;
  • les groupes supplémentaires ;
  • les permissions du fichier.
Exemple :

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;
}
Dans un programme normal, on aura souvent :

Code: Select all

Real UID      : 1000
Effective UID : 1000
Mais dans un programme setuid root, on peut avoir :

Code: Select all

Real UID      : 1000
Effective UID : 0
Cela signifie :
  • 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’utilisateur réel qui a lancé le programme.
L’effective UID indique :
l’identité utilisée par le noyau pour vérifier les permissions.
Exemple mental :

Code: Select all

Real UID      = jean
Effective UID = root
Cela signifie :
  • jean a lancé le programme ;
  • mais le programme possède les droits de root pendant son exécution.
C’est ce mécanisme qui permet à certains programmes système de faire des opérations privilégiées pour un utilisateur normal.

L’exemple classique est :

Code: Select all

/usr/bin/passwd
Un utilisateur normal doit pouvoir changer son mot de passe.

Mais le fichier qui contient les mots de passe chiffrés est :

Code: Select all

/etc/shadow
Ce fichier est protégé et appartient à root.

Un utilisateur normal ne peut pas modifier directement :

Code: Select all

/etc/shadow
Pourtant, la commande :

Code: Select all

passwd
peut le faire, car elle est généralement installée comme programme setuid root.


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 :
Quand ce programme est exécuté, son effective UID devient l’UID du propriétaire du fichier.
Exemple :

Code: Select all

ls -l /usr/bin/passwd
On peut obtenir une sortie du genre :

Code: Select all

-rwsr-xr-x 1 root root 68208 /usr/bin/passwd
Le caractère important est :

Code: Select all

s
dans :

Code: Select all

-rwsr-xr-x
Ce `s` signifie que le bit setuid est activé.

Le fichier appartient à :

Code: Select all

root root
Donc, quand un utilisateur normal exécute ce programme :

Code: Select all

passwd
le processus obtient :

Code: Select all

Real UID      = UID de l'utilisateur
Effective UID = 0
Autrement dit :
le programme est lancé par l’utilisateur, mais il s’exécute avec les privilèges de root.
C’est un mécanisme très puissant, mais aussi très dangereux si le programme contient une faille.


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;
}
Compilation :

Code: Select all

gcc main.c -o prog
Si root possède le fichier :

Code: Select all

sudo chown root:root prog
Puis active le bit setuid :

Code: Select all

sudo chmod u+s prog
Alors :

Code: Select all

ls -l prog
peut afficher :

Code: Select all

-rwsr-xr-x 1 root root ... prog
Quand un utilisateur normal lance :

Code: Select all

./prog
il peut obtenir :

Code: Select all

Real UID      : 1000
Effective UID : 0
Cela montre que le processus a bien une identité réelle différente de son identité effective.


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
Le noyau donne des privilèges particuliers aux processus dont l’effective UID vaut 0.

Donc, en pratique :

Code: Select all

if (geteuid() == 0)
{
    printf("Le processus est privilégié.\n");
}
Un utilisateur pourrait théoriquement s’appeler autrement que root, mais s’il possède l’UID 0, il serait considéré comme superutilisateur.

Ce qu’il faut retenir :
UID 0 = superutilisateur.
Et surtout :
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 :
Quand ce programme est exécuté, son effective GID devient le GID du groupe propriétaire du fichier.
Exemple de permissions :

Code: Select all

-rwxr-sr-x
Le `s` dans la partie groupe indique que le bit setgid est activé.

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
Donc si le programme contient une faille, l’utilisateur pourrait réussir à obtenir plus de droits que prévu.

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.
Un programme setuid doit donc être écrit avec une discipline extrême.

Règle importante :
Un programme setuid doit faire le moins possible avec les privilèges élevés.
Bonne pratique :
  • 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.
Exemple typique :

Code: Select all

Real UID      = 1000
Effective UID = 0
Saved UID     = 0
Le programme peut temporairement faire :

Code: Select all

seteuid(getuid());
Il devient alors :

Code: Select all

Real UID      = 1000
Effective UID = 1000
Saved UID     = 0
Il a temporairement perdu les droits effectifs de root.

Mais comme son saved UID vaut encore 0, il peut récupérer root avec :

Code: Select all

seteuid(0);
Ce mécanisme permet à un programme de limiter le temps pendant lequel il tourne réellement en root.

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.
Abandon temporaire

Exemple :

Code: Select all

seteuid(getuid());
Le processus remet son effective UID à l’UID réel.

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);
Abandon définitif

Exemple :

Code: Select all

setuid(getuid());
Sur Linux, pour un processus privilégié, setuid() peut modifier plusieurs identités à la fois, notamment le real UID, l’effective UID et le saved UID.

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.
setuid() peut être utilisé pour abandonner définitivement 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.


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.
Prototype :

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);
Exemple :

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;
}
Ces appels sont très pratiques pour afficher clairement l’état complet du processus.


13. setuid()

Prototype :

Code: Select all

#include <unistd.h>

int setuid(uid_t uid);
La fonction :

Code: Select all

setuid()
modifie l’UID du processus.

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);
échouera si le processus n’a pas déjà les droits nécessaires.

Pour un processus privilégié, setuid() peut modifier l’identité de manière plus profonde.

Point important :
Un programme normal ne peut pas devenir root simplement en appelant setuid(0).
Il faut que le processus soit déjà privilégié, ou qu’il ait été lancé via un mécanisme comme setuid root.


14. seteuid()

Prototype :

Code: Select all

#include <unistd.h>

int seteuid(uid_t euid);
La fonction :

Code: Select all

seteuid()
modifie l’effective UID.

Elle est souvent utilisée par les programmes setuid pour alterner entre :
  • mode privilégié ;
  • mode non privilégié.
Exemple :

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;
}
Ce code ne peut récupérer root que si le programme possède encore une identité sauvegardée permettant de le faire.

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);
Ces fonctions permettent de modifier le real UID et l’effective UID en une seule fois.

Exemple :

Code: Select all

setreuid(-1, getuid());
Ici :
  • -1 signifie : ne pas changer cette valeur ;
  • getuid() est utilisé pour mettre l’effective UID à la valeur du real UID.
Donc cette ligne peut servir à abandonner temporairement l’effective UID privilégié.

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);
Ces fonctions permettent de modifier explicitement :
  • le real UID ;
  • l’effective UID ;
  • le saved UID.
Elles donnent un contrôle très précis sur les credentials.

Exemple :

Code: Select all

setresuid(getuid(), getuid(), getuid());
Cette ligne tente de mettre les trois UID à l’utilisateur réel.

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.
On peut les manipuler avec :

Code: Select all

#include <sys/fsuid.h>

int setfsuid(uid_t fsuid);
int setfsgid(gid_t fsgid);
Ces identifiants sont utilisés par le noyau Linux pour certaines vérifications d’accès aux fichiers.

Historiquement, ils servaient notamment pour des cas comme les serveurs de fichiers.

À ton niveau, il faut surtout retenir :
Le fsuid est une identité Linux spécifique utilisée pour les permissions du système de fichiers.
Dans la majorité des programmes classiques, on ne l’utilise pas directement.

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
peut afficher :

Code: Select all

jean adm cdrom sudo dip plugdev users docker
Cela signifie que l’utilisateur appartient à plusieurs groupes.

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
Si ton processus possède le groupe supplémentaire docker, il peut obtenir certains droits sur ce fichier ou certains objets liés à 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[]);
Exemple simple :

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;
}
Ici, la première étape :

Code: Select all

getgroups(0, NULL)
sert à demander combien de groupes sont nécessaires.

Ensuite, on alloue un tableau assez grand.

Puis on rappelle :

Code: Select all

getgroups(n, groups)
pour remplir le tableau.


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);
setgroups()

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 :
Je vais exécuter ce service en tant que l’utilisateur www-data, donc je dois charger les groupes de www-data.
Dans ce cas, initgroups() peut être utilisée pour initialiser correctement les groupes supplémentaires.


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()
Ces fonctions permettent de lire :
  • le nom d’un utilisateur ;
  • son UID ;
  • son GID principal ;
  • son home directory ;
  • son shell ;
  • les groupes définis dans le système.
Le chapitre actuel parle de l’état de sécurité d’un processus.

Exemples :

Code: Select all

getuid()
geteuid()
setuid()
seteuid()
getgroups()
Ces fonctions permettent de savoir :
  • 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.
Résumé :

[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;
}
Compilation :

Code: Select all

gcc main.c -o creds
Exécution :

Code: Select all

./creds
Ce programme est utile pour visualiser concrètement les credentials du processus courant.


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;
}
Il faut utiliser :

Code: Select all

geteuid()
plutôt que :

Code: Select all

getuid()
Pourquoi ?

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
Donc si on veut savoir si le processus a réellement les droits de root, il faut tester :

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()
Puis on transforme cet UID en nom utilisateur avec :

Code: Select all

getpwuid()
Exemple :

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;
}
Ici :
  • getuid() vient du chapitre credentials ;
  • getpwuid() vient du chapitre utilisateurs/groupes.
Les deux chapitres se complètent donc parfaitement.


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.
Windows utilise un modèle plus riche :
  • access token ;
  • SID ;
  • ACL ;
  • privileges ;
  • integrity levels ;
  • impersonation tokens ;
  • security descriptors.
Mais l’idée générale reste comparable :
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 :
Un processus normal ne peut pas devenir root tout seul.
Ce code :

Code: Select all

setuid(0);
ne suffit pas.

Si un utilisateur normal lance un programme normal, alors l’appel échouera généralement avec :

Code: Select all

Operation not permitted
Pour qu’un processus devienne root légalement, il faut :
  • 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é.
Le mécanisme principal étudié dans ce chapitre est le setuid root.


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
Cela permet de donner à un processus une partie seulement des pouvoirs de root.

Par exemple :
  • un programme peut avoir le droit de configurer le réseau ;
  • sans avoir absolument tous les privilèges de root.
À ton niveau actuel, retiens surtout :
Les capabilities sont une version plus fine des privilèges root.
Mais le modèle historique UID/GID/setuid reste la base à comprendre.


27. Résumé des fonctions importantes

Lire les identités du processus

Code: Select all

getuid()
geteuid()
getgid()
getegid()
getgroups()
getresuid()
getresgid()
Modifier les identités

Code: Select all

setuid()
seteuid()
setgid()
setegid()
setreuid()
setregid()
setresuid()
setresgid()
Modifier les groupes supplémentaires

Code: Select all

setgroups()
initgroups()
Identifiants filesystem Linux spécifiques

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 point central est que les permissions dépendent surtout de l’identité effective.

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.
Phrase à retenir
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.

Who is online

Users browsing this forum: No registered users and 1 guest