---
jupytext:
  text_representation:
    extension: .md
    format_name: myst
    format_version: 0.13
    jupytext_version: 1.16.0
kernelspec:
  name: python3
  display_name: Python 3
---

# 13. Permissions avancées

La gestion des permissions sous Linux dépasse largement le modèle `rwx` de base appris en introduction. Ce chapitre explore les mécanismes complémentaires : bits spéciaux SUID/SGID/sticky, listes de contrôle d'accès POSIX, capabilities du noyau, attributs étendus et umask. La maîtrise de ces outils est indispensable pour sécuriser un système en production.

---

## Rappel du modèle DAC

Le contrôle d'accès discrétionnaire (DAC, *Discretionary Access Control*) est le modèle de base de Linux. Chaque fichier appartient à un **propriétaire** (UID) et à un **groupe** (GID). Les permissions sont définies pour trois catégories : le propriétaire (`u`), le groupe (`g`), les autres (`o`).

### Bits de permission

Chaque catégorie dispose de trois bits : lecture (`r` = 4), écriture (`w` = 2), exécution (`x` = 1).

```
-rwxr-x--- 1 alice devs 4096 mars 10 14:32 script.sh
```

Décomposition :

- `-` : type régulier (d = répertoire, l = lien symbolique, c = char device…)
- `rwx` : propriétaire peut lire, écrire, exécuter
- `r-x` : groupe peut lire et exécuter
- `---` : les autres n'ont aucun droit

En octal, cette permission s'écrit **750** : `7 = rwx`, `5 = r-x`, `0 = ---`.

### UID/GID effectifs

Lors de l'exécution d'un processus, le noyau maintient plusieurs identités :

| Identité | Description |
|----------|-------------|
| UID réel | L'utilisateur qui a lancé le processus |
| UID effectif | L'identité utilisée pour les vérifications d'accès |
| UID sauvegardé | Copie de l'UID effectif avant un changement temporaire |
| UID de système de fichiers | Utilisé spécifiquement pour les accès aux fichiers |

Normalement, UID réel = UID effectif. Les bits spéciaux (SUID/SGID) modifient ce comportement.

### Vérification des permissions

Le noyau effectue la vérification dans l'ordre suivant :

1. Si l'UID effectif du processus est 0 (root) → accès accordé (presque toujours)
2. Si l'UID effectif correspond au propriétaire → bits du propriétaire appliqués
3. Si le GID effectif correspond au groupe → bits du groupe appliqués
4. Sinon → bits "autres" appliqués

```{admonition} Important
:class: important
La vérification s'arrête à la première correspondance. Si un fichier appartient à alice avec les permissions `---rwxrwx`, alice elle-même n'a aucun droit, même si elle fait partie du groupe.
```

---

## SUID, SGID et sticky bit

Ces trois bits occupent un quatrième octet dans la représentation octale des permissions.

### SUID (Set User ID) — bit 4000

Quand le bit SUID est positionné sur un **exécutable**, le processus s'exécute avec l'UID effectif du **propriétaire du fichier**, non de l'utilisateur qui le lance.

```bash
# Afficher les permissions de /usr/bin/passwd
ls -l /usr/bin/passwd
```

```
-rwsr-xr-x 1 root root 59976 janv. 25 2023 /usr/bin/passwd
```

Le `s` en position exécution du propriétaire indique le SUID. Quand alice exécute `passwd`, le processus tourne avec l'UID 0 (root), ce qui lui permet de modifier `/etc/shadow`.

**Positionner le SUID :**

```bash
chmod u+s /chemin/executable
# ou en octal :
chmod 4755 /chemin/executable
```

**Cas d'usage légitimes :**

- `/usr/bin/passwd` — modification du mot de passe (accès à `/etc/shadow`)
- `/usr/bin/sudo` — élévation de privilèges contrôlée
- `/usr/bin/ping` — accès aux raw sockets (sur anciens systèmes)
- `/usr/bin/su` — changement d'identité

**Dangers du SUID :**

- Un binaire SUID root mal écrit est un vecteur d'escalade de privilèges
- Les scripts shell avec SUID sont ignorés par le noyau Linux (sécurité)
- Tout fichier SUID root trouvé dans `/tmp` ou des répertoires utilisateur est suspect

```{admonition} Avertissement
:class: warning
Ne jamais positionner le bit SUID sur des interprètes (bash, python, perl) ou des éditeurs de texte. Cela offrirait immédiatement un accès root à quiconque peut exécuter le fichier.
```

### SGID (Set Group ID) — bit 2000

**Sur un exécutable :** le processus s'exécute avec le GID effectif du groupe propriétaire.

```bash
ls -l /usr/bin/write
```

```
-rwxr-sr-x 1 root tty 14328 avril 3 2022 /usr/bin/write
```

Le `s` en position exécution du groupe indique le SGID.

**Sur un répertoire :** tous les fichiers créés dans ce répertoire héritent du groupe du répertoire, quel que soit le groupe primaire de l'utilisateur créateur.

```bash
# Créer un répertoire partagé pour l'équipe devs
mkdir /srv/projet
chown root:devs /srv/projet
chmod 2775 /srv/projet

# Tout fichier créé aura le groupe devs
```

C'est le mécanisme fondamental des répertoires partagés en entreprise.

### Sticky bit — bit 1000

**Sur un répertoire :** un utilisateur ne peut supprimer ou renommer que ses propres fichiers, même s'il a le droit d'écriture sur le répertoire.

```bash
ls -ld /tmp
```

```
drwxrwxrwt 18 root root 4096 mars 24 09:15 /tmp
```

Le `t` final indique le sticky bit. Sans lui, n'importe quel utilisateur pourrait supprimer les fichiers des autres dans `/tmp`.

**Positionner le sticky bit :**

```bash
chmod +t /repertoire/partage
# ou en octal :
chmod 1777 /tmp
```

```{admonition} Note
:class: note
Sur les anciens systèmes Unix, le sticky bit sur les exécutables conservait le programme en mémoire swap. Ce comportement est obsolète sous Linux moderne.
```

---

## ACL POSIX

Les ACL (*Access Control Lists*) POSIX permettent de définir des permissions pour des utilisateurs ou groupes supplémentaires, au-delà du modèle propriétaire/groupe/autres.

### Prérequis

Le système de fichiers doit être monté avec l'option `acl` (activée par défaut sur ext4 et XFS depuis le noyau 2.6).

```bash
# Vérifier le support ACL
tune2fs -l /dev/sda1 | grep "Default mount options"
```

```
Default mount options:    user_xattr acl
```

### Consulter les ACL — getfacl

```bash
getfacl /srv/projet/rapport.txt
```

```
# file: srv/projet/rapport.txt
# owner: alice
# group: devs
user::rw-
user:bob:r--
user:charlie:rw-
group::r--
group:admins:rw-
mask::rw-
other::---
```

### Modifier les ACL — setfacl

```bash
# Donner les droits r-x à l'utilisateur bob
setfacl -m u:bob:rx /srv/projet/script.sh

# Donner les droits rw- au groupe audit
setfacl -m g:audit:rw /srv/projet/rapport.txt

# Supprimer l'ACL d'un utilisateur
setfacl -x u:bob /srv/projet/script.sh

# Supprimer toutes les ACL d'un fichier
setfacl -b /srv/projet/fichier.txt
```

### Masque ACL

Le masque (`mask`) est l'**union maximale** des droits accordés aux utilisateurs nommés, groupes nommés et au groupe propriétaire. Il agit comme un plafond.

```bash
# Réduire le masque à r-- (lecture seule pour tous les ACL)
setfacl -m mask::r-- /srv/projet/rapport.txt
```

```{admonition} Important
:class: important
Quand `chmod` est utilisé sur un fichier avec ACL, il modifie le masque ACL (pas les entrées individuelles). Les droits du propriétaire et des "autres" sont toujours modifiés directement.
```

### ACL par défaut sur les répertoires

Les ACL par défaut s'appliquent aux nouveaux fichiers et sous-répertoires créés dans un répertoire.

```bash
# Définir une ACL par défaut : bob a toujours r-x sur les nouveaux fichiers
setfacl -d -m u:bob:rx /srv/projet/

# Vérifier
getfacl /srv/projet/
```

```
# file: srv/projet/
# owner: root
# group: devs
user::rwx
group::rwx
other::---
default:user::rwx
default:user:bob:r-x
default:group::rwx
default:mask::rwx
default:other::---
```

### Interaction avec ls

La présence d'ACL est signalée par un `+` après les permissions dans `ls -l` :

```bash
ls -l /srv/projet/rapport.txt
```

```
-rw-rw----+ 1 alice devs 2048 mars 24 10:00 rapport.txt
```

---

## Capabilities Linux

Les capabilities fragmentent les privilèges root en unités granulaires. Au lieu de donner tous les droits root à un programme, on lui accorde seulement les capabilities dont il a besoin.

### Ensembles de capabilities

Chaque thread possède cinq ensembles de capabilities :

| Ensemble | Description |
|----------|-------------|
| **Permitted** | Capabilities que le thread peut activer |
| **Effective** | Capabilities actuellement actives (vérifiées par le noyau) |
| **Inheritable** | Capabilities transmissibles aux processus fils via `execve` |
| **Bounding** | Limite supérieure pour les capabilities héritables |
| **Ambient** | Capabilities héritées par les programmes non-SUID sans capabilities fichier |

### Capabilities importantes

| Capability | Usage |
|------------|-------|
| `CAP_NET_BIND_SERVICE` | Écouter sur un port < 1024 sans être root |
| `CAP_NET_RAW` | Utiliser des raw sockets (ping, tcpdump) |
| `CAP_SYS_ADMIN` | Nombreuses opérations système (mount, sethostname…) |
| `CAP_DAC_OVERRIDE` | Contourner les vérifications de permissions DAC |
| `CAP_SETUID` | Changer d'UID arbitrairement |
| `CAP_CHOWN` | Changer le propriétaire d'un fichier |
| `CAP_SYS_PTRACE` | Utiliser ptrace sur n'importe quel processus |
| `CAP_KILL` | Envoyer des signaux à n'importe quel processus |
| `CAP_NET_ADMIN` | Configuration réseau avancée |

```{admonition} Avertissement
:class: warning
`CAP_SYS_ADMIN` est surnommée "la nouvelle root". Elle englobe des dizaines d'opérations privilégiées. L'accorder est presque équivalent à accorder root.
```

### Lire les capabilities — getcap

```bash
# Capabilities d'un fichier
getcap /usr/bin/ping
```

```
/usr/bin/ping cap_net_raw=ep
```

La notation `=ep` signifie : la capability est dans les ensembles **e**ffective et **p**ermitted du fichier.

```bash
# Lister toutes les capabilities du système
getcap -r / 2>/dev/null
```

### Modifier les capabilities — setcap

```bash
# Permettre à nginx de s'attacher au port 443 sans root
setcap cap_net_bind_service=ep /usr/sbin/nginx

# Supprimer toutes les capabilities d'un fichier
setcap -r /usr/sbin/nginx

# Vérifier les capabilities d'un processus en cours
cat /proc/$(pgrep nginx | head -1)/status | grep Cap
```

```
CapInh: 0000000000000000
CapPrm: 0000000000000400
CapEff: 0000000000000400
CapBnd: 000001ffffffffff
CapAmb: 0000000000000000
```

Les valeurs sont des bitmasks hexadécimaux. L'outil `capsh --decode=0x0400` les traduit en noms lisibles.

### Remplacement du SUID

Au lieu du bit SUID root sur `ping` :

```bash
# Ancienne méthode (SUID root)
chmod u+s /usr/bin/ping

# Méthode moderne (capability ciblée)
chmod u-s /usr/bin/ping
setcap cap_net_raw=ep /usr/bin/ping
```

---

## Attributs étendus

Les attributs étendus (`xattr`) permettent d'associer des métadonnées supplémentaires aux fichiers, en dehors du modèle de permissions standard.

### Attributs chattr/lsattr

Le système `chattr`/`lsattr` utilise des attributs spécifiques au système de fichiers (ext2/ext3/ext4) :

```bash
# Voir les attributs d'un fichier
lsattr /etc/passwd
```

```
----i--------e-- /etc/passwd
```

```bash
# Rendre un fichier immuable (root uniquement)
chattr +i /etc/resolv.conf

# Rendre un fichier append-only
chattr +a /var/log/audit/audit.log

# Retirer l'attribut immuable
chattr -i /etc/resolv.conf
```

### Attributs importants

| Attribut | Flag | Description |
|----------|------|-------------|
| Immutable | `+i` | Aucune modification, suppression, renommage ou lien possible — même root |
| Append-only | `+a` | Écriture en mode ajout uniquement (idéal pour les logs) |
| No-dump | `+d` | Exclu des sauvegardes `dump` |
| Secure delete | `+s` | Écrasement sécurisé lors de la suppression (fiabilité dépend du SSD/HDD) |
| Synchronous | `+S` | Écriture synchrone immédiate sur disque |
| No-atime | `+A` | Pas de mise à jour de l'atime (performance) |

```{admonition} Note
:class: note
L'attribut `+i` protège même contre root. Pour modifier un fichier immutable, il faut d'abord retirer l'attribut avec `chattr -i`. Cela en fait un outil de protection contre les modifications accidentelles ou les attaques par ransomware sur les fichiers critiques.
```

### Attributs étendus génériques (xattr)

```bash
# Lire les xattr d'un fichier
getfattr -d /srv/projet/document.pdf

# Définir un xattr personnalisé (namespace user.*)
setfattr -n user.classification -v "confidentiel" /srv/projet/document.pdf

# Lire un xattr spécifique
getfattr -n user.classification /srv/projet/document.pdf
```

```
# file: srv/projet/document.pdf
user.classification="confidentiel"
```

---

## umask

L'umask est un masque de bits qui **soustrait** des permissions lors de la création de nouveaux fichiers et répertoires.

### Principe de calcul

La permission effective est : `permission_maximale & ~umask`

- Fichiers : permission maximale = `666` (pas d'exécution par défaut)
- Répertoires : permission maximale = `777`

Avec umask `022` :

- Fichier créé : `666 & ~022 = 666 & 755 = 644` → `rw-r--r--`
- Répertoire créé : `777 & ~022 = 777 & 755 = 755` → `rwxr-xr-x`

Avec umask `027` :

- Fichier créé : `666 & ~027 = 640` → `rw-r-----`
- Répertoire créé : `777 & ~027 = 750` → `rwxr-x---`

### Consulter et modifier l'umask

```bash
# Voir l'umask courant
umask
# 0022

# Voir l'umask en notation symbolique
umask -S
# u=rwx,g=rx,o=rx

# Modifier l'umask pour la session
umask 027
```

### Configuration persistante

```bash
# Pour tous les utilisateurs (Debian/Ubuntu)
# /etc/profile ou /etc/profile.d/umask.sh
umask 022

# Pour un utilisateur spécifique
# ~/.bashrc ou ~/.profile
umask 027
```

### umask via PAM

```bash
# /etc/pam.d/common-session (Debian)
session optional pam_umask.so umask=027

# /etc/login.defs
UMASK 027
```

### umask pour les services systemd

```ini
# Dans l'unité systemd du service
[Service]
UMask=0027
```

```{admonition} Tip
:class: tip
Pour les services qui créent des fichiers sensibles (clés, tokens), utilisez umask `0077`. Les fichiers seront en `600` et les répertoires en `700`, inaccessibles aux autres utilisateurs.
```

---

## Audit des permissions

### Trouver les fichiers SUID/SGID

```bash
# Tous les fichiers SUID
find / -perm -4000 -type f 2>/dev/null

# Tous les fichiers SGID
find / -perm -2000 -type f 2>/dev/null

# SUID ou SGID
find / -perm /6000 -type f 2>/dev/null

# SUID root uniquement (les plus dangereux)
find / -user root -perm -4000 -type f 2>/dev/null
```

### Détecter les permissions dangereuses

```bash
# Fichiers world-writable (hors /tmp et /proc)
find / -perm -0002 -type f ! -path "/proc/*" ! -path "/tmp/*" 2>/dev/null

# Fichiers sans propriétaire (UID/GID non existant)
find / -nouser -o -nogroup 2>/dev/null

# Répertoires world-writable sans sticky bit
find / -type d -perm -0002 ! -perm -1000 ! -path "/proc/*" 2>/dev/null
```

### Vérifier les capabilities non-standard

```bash
# Lister toutes les capabilities sur le système
getcap -r / 2>/dev/null | grep -v "^/usr/bin/ping\|^/usr/bin/traceroute"
```

---

## Démonstrations Python

### Lecture des permissions avec le module stat

<br>

```{code-cell} python3
import os
import stat
import pwd
import grp

def afficher_permissions(chemin):
    """Affiche les permissions détaillées d'un fichier."""
    try:
        s = os.stat(chemin)
    except PermissionError:
        print(f"{chemin} : accès refusé")
        return
    except FileNotFoundError:
        print(f"{chemin} : fichier introuvable")
        return

    mode = s.st_mode
    perm_octal = oct(stat.S_IMODE(mode))

    # Reconstruction rwx
    def bits_rwx(m):
        return (
            ("r" if m & 0o400 else "-") +
            ("w" if m & 0o200 else "-") +
            ("x" if m & 0o100 else "-") +
            ("r" if m & 0o040 else "-") +
            ("w" if m & 0o020 else "-") +
            ("x" if m & 0o010 else "-") +
            ("r" if m & 0o004 else "-") +
            ("w" if m & 0o002 else "-") +
            ("x" if m & 0o001 else "-")
        )

    # Bits spéciaux
    suid = "SUID " if mode & stat.S_ISUID else ""
    sgid = "SGID " if mode & stat.S_ISGID else ""
    sticky = "Sticky" if mode & stat.S_ISVTX else ""
    specials = (suid + sgid + sticky).strip() or "—"

    try:
        proprietaire = pwd.getpwuid(s.st_uid).pw_name
    except KeyError:
        proprietaire = str(s.st_uid)

    try:
        groupe = grp.getgrgid(s.st_gid).gr_name
    except KeyError:
        groupe = str(s.st_gid)

    type_fichier = {
        stat.S_IFREG: "fichier régulier",
        stat.S_IFDIR: "répertoire",
        stat.S_IFLNK: "lien symbolique",
        stat.S_IFCHR: "char device",
        stat.S_IFBLK: "block device",
        stat.S_IFIFO: "FIFO",
        stat.S_IFSOCK: "socket",
    }.get(stat.S_IFMT(mode), "inconnu")

    print(f"Fichier    : {chemin}")
    print(f"Type       : {type_fichier}")
    print(f"Octal      : {perm_octal}")
    print(f"rwx        : {bits_rwx(mode)}")
    print(f"Propriétaire: {proprietaire} (uid={s.st_uid})")
    print(f"Groupe     : {groupe} (gid={s.st_gid})")
    print(f"Bits spéciaux: {specials}")
    print(f"Taille     : {s.st_size} octets")
    print()

for fichier in ["/etc/passwd", "/etc/shadow", "/tmp"]:
    afficher_permissions(fichier)
```

### Heatmap des permissions

<br>

```{code-cell} python3
:tags: [hide-input]

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.patches as mpatches
import seaborn as sns

sns.set_theme(style="whitegrid", palette="muted", font_scale=1.1)

# Fichiers représentatifs avec leurs permissions en octal
fichiers = [
    ("/etc/passwd",       0o644),
    ("/etc/shadow",       0o640),
    ("/etc/sudoers",      0o440),
    ("/bin/bash",         0o755),
    ("/usr/bin/passwd",   0o4755),  # SUID
    ("/usr/bin/sudo",     0o4111),  # SUID
    ("/tmp",              0o1777),  # Sticky
    ("/var/log/syslog",   0o640),
    ("/home/alice/.ssh",  0o700),
    ("/srv/partage",      0o2775),  # SGID
]

categories = ["u:r", "u:w", "u:x", "g:r", "g:w", "g:x", "o:r", "o:w", "o:x"]
noms = [f[0].split("/")[-1] or f[0] for f in fichiers]
noms[6] = "/tmp"
noms[8] = ".ssh"

matrice = np.zeros((len(fichiers), 9), dtype=float)

for i, (_, perm) in enumerate(fichiers):
    bits = perm & 0o777
    for j, bit in enumerate([0o400, 0o200, 0o100, 0o040, 0o020, 0o010, 0o004, 0o002, 0o001]):
        matrice[i, j] = 1.0 if (bits & bit) else 0.0

# Marquer les bits spéciaux en valeur différente
for i, (_, perm) in enumerate(fichiers):
    if perm & 0o4000:  # SUID
        matrice[i, 2] = 2.0
    if perm & 0o2000:  # SGID
        matrice[i, 5] = 2.0
    if perm & 0o1000:  # Sticky
        matrice[i, 8] = 3.0

fig, ax = plt.subplots(figsize=(11, 6))

from matplotlib.colors import ListedColormap
cmap = ListedColormap(["#f0f0f0", "#4c9be8", "#e84c4c", "#f0c040"])
im = ax.imshow(matrice, cmap=cmap, aspect="auto", vmin=0, vmax=3)

ax.set_xticks(range(9))
ax.set_xticklabels(categories, fontsize=10)
ax.set_yticks(range(len(fichiers)))
ax.set_yticklabels(noms, fontsize=9)

for i in range(len(fichiers)):
    for j in range(9):
        val = matrice[i, j]
        if val > 0:
            label = {1.0: "✓", 2.0: "S", 3.0: "T"}.get(val, "")
            couleur = "white" if val >= 2 else "#333333"
            ax.text(j, i, label, ha="center", va="center", fontsize=9, color=couleur, fontweight="bold")

ax.set_title("Matrice des permissions — fichiers système représentatifs", fontsize=13, pad=14)
ax.set_xlabel("Bits de permission", fontsize=10)
ax.set_ylabel("Fichier", fontsize=10)

legende = [
    mpatches.Patch(facecolor="#f0f0f0", edgecolor="gray", label="Pas de droit"),
    mpatches.Patch(facecolor="#4c9be8", label="Droit actif"),
    mpatches.Patch(facecolor="#e84c4c", label="SUID/SGID"),
    mpatches.Patch(facecolor="#f0c040", label="Sticky bit"),
]
ax.legend(handles=legende, loc="upper right", bbox_to_anchor=(1.18, 1.0), fontsize=9)

plt.show()
```

### Simulation d'audit SUID

<br>

```{code-cell} python3
:tags: [hide-input]

import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns

sns.set_theme(style="whitegrid", palette="muted", font_scale=1.1)

# Liste simulée de fichiers SUID avec métadonnées
audit_suid = [
    {"fichier": "/usr/bin/passwd",    "proprio": "root", "taille": 59976,  "legitime": True,  "risque": 2},
    {"fichier": "/usr/bin/sudo",      "proprio": "root", "taille": 166056, "legitime": True,  "risque": 2},
    {"fichier": "/usr/bin/su",        "proprio": "root", "taille": 67816,  "legitime": True,  "risque": 2},
    {"fichier": "/usr/bin/newgrp",    "proprio": "root", "taille": 39648,  "legitime": True,  "risque": 1},
    {"fichier": "/usr/bin/chfn",      "proprio": "root", "taille": 72712,  "legitime": True,  "risque": 1},
    {"fichier": "/usr/bin/mount",     "proprio": "root", "taille": 122784, "legitime": True,  "risque": 2},
    {"fichier": "/usr/bin/umount",    "proprio": "root", "taille": 88408,  "legitime": True,  "risque": 1},
    {"fichier": "/usr/sbin/pppd",     "proprio": "root", "taille": 440664, "legitime": True,  "risque": 2},
    # Fichiers suspects simulés
    {"fichier": "/tmp/.hidden/shell", "proprio": "root", "taille": 1234,   "legitime": False, "risque": 5},
    {"fichier": "/home/bob/backdoor", "proprio": "root", "taille": 8192,   "legitime": False, "risque": 5},
    {"fichier": "/opt/custom/helper", "proprio": "root", "taille": 12288,  "legitime": False, "risque": 4},
]

df = pd.DataFrame(audit_suid)

# --- Affichage textuel ---
print("=== Rapport d'audit SUID ===\n")
print(f"Total fichiers SUID trouvés : {len(df)}")
print(f"  Légitimes   : {df['legitime'].sum()}")
print(f"  Suspects    : {(~df['legitime']).sum()}\n")

print("Fichiers suspects :")
suspects = df[~df["legitime"]][["fichier", "proprio", "taille", "risque"]]
print(suspects.to_string(index=False))

# --- Visualisation ---
fig, axes = plt.subplots(1, 2, figsize=(13, 5))

# Graphique 1 : distribution des niveaux de risque
couleurs_risque = {1: "#4caf50", 2: "#ff9800", 3: "#ff5722", 4: "#d32f2f", 5: "#7b1fa2"}
comptes = df["risque"].value_counts().sort_index()
bars = axes[0].bar(
    [f"Niveau {r}" for r in comptes.index],
    comptes.values,
    color=[couleurs_risque[r] for r in comptes.index],
    edgecolor="white", linewidth=1.2
)
axes[0].set_title("Distribution des niveaux de risque\n(fichiers SUID)", fontsize=11)
axes[0].set_ylabel("Nombre de fichiers")
axes[0].set_xlabel("Niveau de risque")
for bar, val in zip(bars, comptes.values):
    axes[0].text(bar.get_x() + bar.get_width() / 2, bar.get_height() + 0.05,
                 str(val), ha="center", va="bottom", fontsize=10, fontweight="bold")

# Graphique 2 : scatter taille vs risque, coloré par légitimité
couleurs_pt = df["legitime"].map({True: "#4c9be8", False: "#e84c4c"})
axes[1].scatter(df["taille"] / 1024, df["risque"],
                c=couleurs_pt, s=100, alpha=0.8, edgecolors="white", linewidth=0.8)
for _, row in df.iterrows():
    nom_court = row["fichier"].split("/")[-1]
    axes[1].annotate(nom_court, (row["taille"] / 1024, row["risque"]),
                     textcoords="offset points", xytext=(5, 3), fontsize=7, alpha=0.85)
axes[1].set_title("Taille vs niveau de risque\n(fichiers SUID)", fontsize=11)
axes[1].set_xlabel("Taille (Ko)")
axes[1].set_ylabel("Niveau de risque")
axes[1].set_yticks(range(1, 6))

import matplotlib.patches as mpatches
legende = [
    mpatches.Patch(color="#4c9be8", label="Légitime"),
    mpatches.Patch(color="#e84c4c", label="Suspect"),
]
axes[1].legend(handles=legende, fontsize=9)

plt.suptitle("Audit des fichiers SUID — résultats simulés", fontsize=13, y=1.01)
plt.show()
```

---

## Résumé

Ce chapitre a couvert les mécanismes de contrôle d'accès avancés de Linux :

**Modèle DAC et bits spéciaux**

- Le modèle rwx/UID/GID reste la base : les permissions sont vérifiées via l'UID effectif du processus
- SUID (4000) et SGID (2000) modifient l'identité effective lors de l'exécution ou l'héritage de groupe dans les répertoires
- Le sticky bit (1000) protège les fichiers dans les répertoires partagés

**ACL POSIX**

- `getfacl`/`setfacl` permettent des permissions granulaires par utilisateur ou groupe supplémentaire
- Le masque ACL est le plafond effectif des permissions ACL
- Les ACL par défaut propagent les règles aux nouveaux fichiers

**Capabilities**

- Remplacement moderne du bit SUID : chaque privilege est accordé individuellement
- Cinq ensembles (permitted, effective, inheritable, bounding, ambient) contrôlent la propagation
- `getcap`/`setcap` pour inspecter et modifier

**Attributs étendus**

- `chattr +i` (immutable) et `+a` (append-only) sont des protections complémentaires aux permissions standard
- Résistent même aux actions de root

**umask**

- Masque appliqué lors de la création : `permission_max & ~umask`
- Configurable par shell, PAM ou unité systemd

**Audit**

- `find -perm -4000/2000` pour détecter les SUID/SGID
- `getcap -r /` pour inventorier les capabilities
- Les fichiers SUID hors des chemins système standards sont systématiquement suspects
