Objectif du cours
Ce cours explique deux mécanismes importants de Linux :
- Les attributs étendus, aussi appelés extended attributes ou xattr.
- Les ACL, c'est-à-dire les Access Control Lists.
L'idée centrale à retenir est simple :
Code: Select all
stat = métadonnées classiques du fichier
xattr = métadonnées supplémentaires attachées au fichier
ACL = permissions avancées, souvent stockées via les xattr
Sous Linux, un fichier n'est pas seulement un bloc de données. Il possède aussi des informations associées, stockées dans l'inode.
Ces informations sont les métadonnées classiques.
Exemples :
- le numéro d'inode ;
- la taille du fichier ;
- le propriétaire ;
- le groupe propriétaire ;
- les permissions ;
- les timestamps ;
- le type du fichier ;
- le nombre de liens physiques.
Code: Select all
stat()
fstat()
lstat()
Code: Select all
#include <stdio.h>
#include <sys/stat.h>
int main(void)
{
struct stat st;
if (stat("test.txt", &st) == -1) {
perror("stat");
return 1;
}
printf("Taille : %ld octets\n", (long)st.st_size);
printf("Inode : %lu\n", (unsigned long)st.st_ino);
printf("UID : %u\n", st.st_uid);
printf("GID : %u\n", st.st_gid);
return 0;
}
Code: Select all
struct stat
Par exemple, on ne peut pas ajouter :
Code: Select all
st_commentaire
st_signature
st_label_selinux
2. Les Extended Attributes : définition
Les extended attributes, ou xattr, sont des métadonnées supplémentaires attachées à un fichier.
Elles fonctionnent sous forme de paires :
Code: Select all
nom = valeur
Code: Select all
user.note = "fichier important"
user.author = "jean"
security.selinux = "system_u:object_r:httpd_sys_content_t:s0"
Code: Select all
inode
├── métadonnées classiques
│ ├── taille
│ ├── uid
│ ├── gid
│ ├── permissions
│ └── timestamps
│
└── attributs étendus
├── user.note
├── user.author
├── security.selinux
└── system.posix_acl_access
3. Différence entre stat et xattr
stat donne les métadonnées standard du fichier.
xattr permet d'ajouter des métadonnées supplémentaires.
Tableau récapitulatif :
Code: Select all
+----------------------+------------------------------+------------------------------+
| Élément | stat | xattr |
+----------------------+------------------------------+------------------------------+
| Type | Métadonnées classiques | Métadonnées étendues |
| Structure | Champs fixes | Nom = valeur |
| Exemple | st_size, st_uid, st_mode | user.note, security.selinux |
| Dynamique | Non | Oui |
| Attaché à l'inode | Oui | Oui |
| Utilisé par sécurité | Partiellement | Oui, très souvent |
+----------------------+------------------------------+------------------------------+
Code: Select all
stat = carte d'identité officielle du fichier
xattr = post-it supplémentaires attachés au fichier
Les xattr servent à stocker des informations qui ne rentrent pas dans les métadonnées classiques.
Cas réels :
- SELinux utilise des labels de sécurité.
- Les capabilities Linux peuvent être stockées sur des exécutables.
- Les ACL POSIX sont souvent stockées sous forme d'attributs étendus.
- Des environnements graphiques peuvent stocker des tags ou commentaires.
- Des outils de sauvegarde peuvent conserver des métadonnées spécifiques.
- Des outils forensic peuvent inspecter ces métadonnées.
Code: Select all
security.selinux
security.capability
system.posix_acl_access
system.posix_acl_default
Les noms des xattr sont organisés en namespaces.
Les principaux sont :
Code: Select all
user.*
security.*
system.*
trusted.*
Le namespace user.* est destiné aux attributs définis par les utilisateurs.
Exemples :
Code: Select all
user.note
user.author
user.comment
user.tag
Code: Select all
user.note = "cours linux"
Le namespace security.* est utilisé par les mécanismes de sécurité.
Exemples :
Code: Select all
security.selinux
security.capability
security.capability peut contenir les capabilities données à un binaire.
5.3. system.*
Le namespace system.* est utilisé par le noyau et le système de fichiers.
Exemples :
Code: Select all
system.posix_acl_access
system.posix_acl_default
5.4. trusted.*
Le namespace trusted.* est réservé à des usages privilégiés.
En général, il faut des droits élevés pour le manipuler.
6. Commandes shell pour les xattr
Deux commandes principales :
Code: Select all
getfattr
setfattr
Code: Select all
touch fichier.txt
setfattr -n user.note -v "fichier important" fichier.txt
- -n indique le nom de l'attribut.
- -v indique la valeur.
- fichier.txt est le fichier cible.
Code: Select all
getfattr -d fichier.txt
Code: Select all
# file: fichier.txt
user.note="fichier important"
Code: Select all
setfattr -x user.note fichier.txt
Les APIs principales sont :
Code: Select all
setxattr()
getxattr()
listxattr()
removexattr()
Code: Select all
setxattr() travaille avec un chemin
fsetxattr() travaille avec un descripteur de fichier
lsetxattr() travaille avec un chemin sans suivre les liens symboliques
Code: Select all
getxattr() / fgetxattr() / lgetxattr()
listxattr() / flistxattr() / llistxattr()
removexattr() / fremovexattr() / lremovexattr()
Prototype simplifié :
Code: Select all
int setxattr(const char *path,
const char *name,
const void *value,
size_t size,
int flags);
Code: Select all
Ajouter ou modifier un attribut étendu sur un fichier.
- path : chemin du fichier.
- name : nom de l'attribut.
- value : valeur à écrire.
- size : taille de la valeur.
- flags : comportement spécial.
Prototype simplifié :
Code: Select all
ssize_t getxattr(const char *path,
const char *name,
void *value,
size_t size);
Code: Select all
Lire la valeur d'un attribut étendu.
Prototype simplifié :
Code: Select all
ssize_t listxattr(const char *path,
char *list,
size_t size);
Code: Select all
Lister les noms des attributs étendus d'un fichier.
Prototype simplifié :
Code: Select all
int removexattr(const char *path,
const char *name);
Code: Select all
Supprimer un attribut étendu.
Deux flags importants :
Code: Select all
XATTR_CREATE
XATTR_REPLACE
Code: Select all
XATTR_CREATE
Code: Select all
Créer l'attribut uniquement s'il n'existe pas déjà.
8.2. XATTR_REPLACE
Code: Select all
XATTR_REPLACE
Code: Select all
Remplacer l'attribut uniquement s'il existe déjà.
8.3. Pourquoi c'est utile ?
Ces flags évitent certaines erreurs logiques.
Exemples :
- éviter d'écraser une valeur déjà existante ;
- forcer la modification uniquement si l'attribut existe ;
- avoir un comportement plus strict ;
- réduire certains risques de race condition logique.
Beaucoup d'APIs Linux fonctionnent ainsi :
Code: Select all
1. Appeler une fonction avec buffer = NULL et size = 0
2. Récupérer la taille nécessaire
3. Allouer un buffer avec malloc()
4. Appeler à nouveau la fonction pour récupérer les données
Code: Select all
ssize_t size = getxattr("fichier.txt", "user.note", NULL, 0);
Ensuite :
Code: Select all
char *buffer = malloc(size + 1);
Code: Select all
getxattr("fichier.txt", "user.note", buffer, size);
10. Programme complet xattr en C
Ce programme montre :
- création d'un fichier ;
- ajout d'un xattr ;
- lecture du xattr ;
- listing des xattr ;
- modification avec XATTR_REPLACE ;
- suppression du xattr.
Code: Select all
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/xattr.h>
#include <errno.h>
int main(void)
{
const char *path = "test_xattr.txt";
const char *attr_name = "user.note";
const char *attr_value = "Fichier important pour comprendre les xattr";
/*
open() crée ou ouvre le fichier.
O_CREAT : crée le fichier s'il n'existe pas.
O_RDWR : ouvre en lecture/écriture.
O_TRUNC : vide le fichier s'il existe déjà.
0644 : permissions classiques du fichier.
*/
int fd = open(path, O_CREAT | O_RDWR | O_TRUNC, 0644);
if (fd == -1) {
perror("open");
return 1;
}
/*
On écrit un contenu normal dans le fichier.
Important :
le contenu du fichier et les xattr sont deux choses différentes.
*/
if (write(fd, "Hello\n", 6) == -1) {
perror("write");
close(fd);
return 1;
}
/*
fsetxattr() ajoute un attribut étendu via un file descriptor.
fd : fichier cible déjà ouvert.
attr_name : nom de l'attribut.
attr_value : valeur à écrire.
strlen() : taille de la valeur.
XATTR_CREATE : échoue si l'attribut existe déjà.
*/
if (fsetxattr(fd,
attr_name,
attr_value,
strlen(attr_value),
XATTR_CREATE) == -1) {
perror("fsetxattr");
close(fd);
return 1;
}
/*
Première lecture :
on demande seulement la taille nécessaire.
value = NULL
size = 0
fgetxattr() retourne la taille de la valeur.
*/
ssize_t size = fgetxattr(fd, attr_name, NULL, 0);
if (size == -1) {
perror("fgetxattr size");
close(fd);
return 1;
}
/*
On alloue un buffer assez grand.
+1 est utile si on veut afficher la valeur comme une chaîne C.
*/
char *buffer = malloc(size + 1);
if (buffer == NULL) {
perror("malloc");
close(fd);
return 1;
}
/*
Deuxième lecture :
on récupère réellement la valeur.
*/
ssize_t ret = fgetxattr(fd, attr_name, buffer, size);
if (ret == -1) {
perror("fgetxattr value");
free(buffer);
close(fd);
return 1;
}
/*
Les xattr ne sont pas forcément des chaînes C.
Ici, notre valeur est du texte, donc on ajoute un '\0'.
*/
buffer[size] = '\0';
printf("Attribut lu : %s = %s\n", attr_name, buffer);
free(buffer);
/*
Maintenant, on liste les noms des xattr.
Même pattern :
- premier appel pour la taille
- malloc
- deuxième appel pour récupérer la liste
*/
ssize_t list_size = flistxattr(fd, NULL, 0);
if (list_size == -1) {
perror("flistxattr size");
close(fd);
return 1;
}
char *list = malloc(list_size);
if (list == NULL) {
perror("malloc list");
close(fd);
return 1;
}
list_size = flistxattr(fd, list, list_size);
if (list_size == -1) {
perror("flistxattr list");
free(list);
close(fd);
return 1;
}
printf("\nListe des attributs :\n");
/*
La liste retournée par flistxattr() est une suite de chaînes C :
user.note\0user.test\0...
Il faut donc avancer de strlen(nom) + 1 à chaque tour.
*/
for (ssize_t i = 0; i < list_size; ) {
printf("- %s\n", &list[i]);
i += strlen(&list[i]) + 1;
}
free(list);
/*
Modification de l'attribut.
XATTR_REPLACE signifie :
remplacer uniquement si l'attribut existe déjà.
*/
const char *new_value = "Nouvelle valeur";
if (fsetxattr(fd,
attr_name,
new_value,
strlen(new_value),
XATTR_REPLACE) == -1) {
perror("fsetxattr replace");
close(fd);
return 1;
}
printf("\nAttribut modifié avec XATTR_REPLACE.\n");
/*
Suppression de l'attribut étendu.
*/
if (fremovexattr(fd, attr_name) == -1) {
perror("fremovexattr");
close(fd);
return 1;
}
printf("Attribut supprimé.\n");
close(fd);
return 0;
}
Code: Select all
gcc xattr_demo.c -o xattr_demo
Code: Select all
./xattr_demo
À retenir absolument :
Code: Select all
setxattr écrire un attribut étendu
getxattr lire un attribut étendu
listxattr lister les noms des attributs
removexattr supprimer un attribut
Code: Select all
f* = travaille avec un file descriptor
l* = ne suit pas les liens symboliques
Code: Select all
xattr = métadonnée supplémentaire attachée à l'inode
ACL signifie :
Code: Select all
Access Control List
Le modèle UNIX classique repose sur :
Code: Select all
owner / group / other
Code: Select all
-rwxr-x---
Code: Select all
owner : rwx
group : r-x
other : ---
On ne peut pas facilement dire :
Code: Select all
jean : rw-
paul : r--
alice : rwx
groupe_admin : rwx
groupe_dev : r--
13. Problème des permissions UNIX classiques
Avec chmod, on peut faire :
Code: Select all
chmod 750 fichier
Code: Select all
owner : rwx
group : r-x
other : ---
- le propriétaire doit avoir rwx ;
- le groupe principal doit avoir r--;
- un utilisateur précis doit avoir rw- ;
- un autre utilisateur doit avoir r--;
- un groupe supplémentaire doit avoir rwx.
Les ACL permettent d'ajouter ces règles spécifiques.
14. Structure logique d'une ACL
Une ACL est une liste d'entrées.
Chaque entrée contient généralement :
Code: Select all
type + identifiant éventuel + permissions
Code: Select all
user:paul:rw-
Code: Select all
type = user
identifiant = paul
permissions = rw-
Code: Select all
group:dev:r-x
Code: Select all
type = group
identifiant = dev
permissions = r-x
Les entrées importantes à connaître sont :
Code: Select all
ACL_USER_OBJ
ACL_USER
ACL_GROUP_OBJ
ACL_GROUP
ACL_MASK
ACL_OTHER
Représente le propriétaire classique du fichier.
Dans getfacl, cela apparaît comme :
Code: Select all
user::rw-
Code: Select all
utilisateur propriétaire, pas un utilisateur nommé spécifique
Représente un utilisateur spécifique.
Exemple :
Code: Select all
user:paul:rwx
15.3. ACL_GROUP_OBJ
Représente le groupe propriétaire classique du fichier.
Dans getfacl :
Code: Select all
group::r--
Représente un groupe spécifique.
Exemple :
Code: Select all
group:dev:r-x
Représente les autres utilisateurs.
Dans getfacl :
Code: Select all
other::---
Le mask est un point très important.
Il agit comme un plafond de permissions.
Il limite les permissions effectives de certaines entrées ACL.
Exemple :
Code: Select all
user:paul:rwx
mask::r--
Code: Select all
r--
Code: Select all
permissions demandées : rwx
mask : r--
résultat effectif : r--
Code: Select all
ACL_MASK = plafond des permissions pour les entrées ACL nommées et les groupes
La commande principale pour lire les ACL est :
Code: Select all
getfacl
Code: Select all
getfacl fichier.txt
Code: Select all
# file: fichier.txt
# owner: jean
# group: users
user::rw-
user:paul:rwx
group::r--
mask::rwx
other::---
Code: Select all
user::rw- propriétaire du fichier
user:paul:rwx utilisateur paul
group::r-- groupe propriétaire
mask::rwx mask ACL
other::--- autres utilisateurs
Code: Select all
-rw-rwx---+ 1 jean users 12 mai 27 20:00 fichier.txt
Code: Select all
ce fichier possède des ACL étendues
La commande principale pour modifier les ACL est :
Code: Select all
setfacl
Code: Select all
setfacl -m u:paul:rwx fichier.txt
Code: Select all
u:paul:rwx
Code: Select all
donner rwx à l'utilisateur paul
Code: Select all
setfacl -m g:dev:r-x fichier.txt
Code: Select all
donner r-x au groupe dev
Code: Select all
setfacl -x u:paul fichier.txt
17.4. Supprimer toutes les ACL étendues
Code: Select all
setfacl -b fichier.txt
18. Default ACL
Les default ACL concernent les dossiers.
Elles permettent de définir des permissions héritées automatiquement par les nouveaux fichiers créés dans un dossier.
Exemple :
Code: Select all
mkdir projet
setfacl -d -m g:dev:rwx projet
Code: Select all
-d
Code: Select all
default ACL
Code: Select all
les nouveaux fichiers créés dans projet hériteront d'une ACL pour le groupe dev
- dossiers partagés ;
- serveurs ;
- environnements multi-utilisateurs ;
- Samba ;
- NFS ;
- projets d'entreprise.
Point fondamental :
Code: Select all
Les ACL Linux sont souvent stockées dans les extended attributes.
Code: Select all
system.posix_acl_access
system.posix_acl_default
Code: Select all
xattr = mécanisme générique de métadonnées
ACL = système de permissions avancées construit dessus
Code: Select all
inode
├── stat
│ ├── uid
│ ├── gid
│ ├── mode
│ └── timestamps
│
└── xattr
├── system.posix_acl_access
├── system.posix_acl_default
├── security.selinux
└── security.capability
Les ACL peuvent être manipulées en C avec la libacl.
Les fonctions importantes sont :
Code: Select all
acl_get_file()
acl_set_file()
acl_get_entry()
acl_get_permset()
acl_add_perm()
acl_free()
Il faut surtout connaître :
Code: Select all
getfacl
setfacl
ACL_USER
ACL_GROUP
ACL_MASK
default ACL
relation ACL/xattr
Code: Select all
#include <stdio.h>
#include <sys/types.h>
#include <sys/acl.h>
int main(void)
{
acl_t acl;
/*
acl_get_file() récupère l'ACL d'accès du fichier.
ACL_TYPE_ACCESS signifie :
on veut l'ACL qui contrôle l'accès normal au fichier.
*/
acl = acl_get_file("fichier.txt", ACL_TYPE_ACCESS);
if (acl == NULL) {
perror("acl_get_file");
return 1;
}
/*
acl_to_text() convertit l'ACL en texte lisible.
C'est pratique pour l'affichage.
*/
ssize_t len;
char *text = acl_to_text(acl, &len);
if (text == NULL) {
perror("acl_to_text");
acl_free(acl);
return 1;
}
printf("%s\n", text);
/*
Les objets retournés par la libacl doivent être libérés avec acl_free().
*/
acl_free(text);
acl_free(acl);
return 0;
}
Code: Select all
gcc acl_read.c -o acl_read -lacl
On crée un fichier :
Code: Select all
touch demo.txt
Code: Select all
setfattr -n user.note -v "cours linux" demo.txt
Code: Select all
getfattr -d demo.txt
Code: Select all
setfacl -m u:paul:r-- demo.txt
Code: Select all
getfacl demo.txt
Code: Select all
ls -l demo.txt
Code: Select all
-rw-r--r--+ 1 jean users 0 mai 27 20:00 demo.txt
22. Différences fondamentales à retenir
22.1. stat vs xattr
Code: Select all
stat = métadonnées classiques
xattr = métadonnées complémentaires
Code: Select all
permissions UNIX = owner / group / other
ACL = permissions détaillées par utilisateur ou groupe
Code: Select all
xattr = mécanisme générique nom/valeur
ACL = système de permissions avancées pouvant être stocké via xattr
23.1. SELinux
SELinux utilise des labels de sécurité.
Exemple :
Code: Select all
security.selinux
23.2. Capabilities
Les capabilities permettent de donner à un programme certains privilèges précis sans lui donner tout le pouvoir de root.
Exemple :
Code: Select all
security.capability
Sur un serveur multi-utilisateurs, on peut donner des droits différents à plusieurs utilisateurs sans créer une organisation compliquée de groupes.
Exemple :
Code: Select all
setfacl -m u:alice:rw- rapport.txt
setfacl -m u:bob:r-- rapport.txt
setfacl -m g:admin:rwx rapport.txt
En analyse forensic, il est utile de vérifier :
- les xattr suspects ;
- les capabilities inhabituelles ;
- les ACL trop permissives ;
- les labels SELinux modifiés.
Code: Select all
getfattr -d fichier
getfacl fichier
getcap fichier
ls -l
stat fichier
Pour ton niveau actuel, retiens surtout :
24.1. xattr
À connaître en API :
Code: Select all
setxattr()
getxattr()
listxattr()
removexattr()
Code: Select all
f* = file descriptor
l* = lien symbolique non suivi
Code: Select all
size = getxattr(..., NULL, 0);
buffer = malloc(size);
getxattr(..., buffer, size);
À connaître surtout en concept :
Code: Select all
ACL = permissions avancées
getfacl = lire les ACL
setfacl = modifier les ACL
ACL_MASK = plafond de permissions
default ACL = héritage sur les dossiers
25. Pièges fréquents
25.1. Croire que ls -l montre tout
Code: Select all
ls -l
Il faut utiliser :
Code: Select all
getfacl
Une entrée peut afficher :
Code: Select all
user:paul:rwx
Code: Select all
mask::r--
25.3. Penser que les xattr sont forcément du texte
Un xattr peut contenir des données binaires.
Donc quand on lit un xattr en C, il ne faut pas toujours supposer qu'il s'agit d'une chaîne C.
25.4. Oublier les variantes f*
Quand on travaille déjà avec un fichier ouvert, les fonctions f* sont souvent plus propres :
Code: Select all
fgetxattr()
fsetxattr()
flistxattr()
fremovexattr()
Les fonctions l* ne suivent pas les liens symboliques.
C'est important en sécurité.
Exemple :
Code: Select all
lgetxattr()
lsetxattr()
llistxattr()
lremovexattr()
À retenir :
Code: Select all
stat
└── métadonnées classiques du fichier
xattr
└── métadonnées étendues nom = valeur
ACL
└── permissions avancées par utilisateur/groupe
Les ACL permettent de dépasser le modèle classique :
Code: Select all
owner / group / other
Code: Select all
Les ACL Linux peuvent être stockées dans les xattr.
Code: Select all
system.posix_acl_access
system.posix_acl_default
security.selinux
security.capability
Code: Select all
setxattr
getxattr
listxattr
removexattr
Code: Select all
getfacl
setfacl
Code: Select all
Linux moderne ne se limite pas aux permissions rwx classiques.
Il utilise aussi des métadonnées étendues et des systèmes de permissions plus fins.
Code: Select all
stat()
Lire les métadonnées classiques.
setxattr()
Ajouter ou modifier un attribut étendu.
getxattr()
Lire un attribut étendu.
listxattr()
Lister les attributs étendus.
removexattr()
Supprimer un attribut étendu.
getfattr
Commande shell pour lire les xattr.
setfattr
Commande shell pour écrire/supprimer les xattr.
getfacl
Commande shell pour afficher les ACL.
setfacl
Commande shell pour modifier les ACL.
ACL_MASK
Plafond de permissions effectives.
Default ACL
ACL héritée automatiquement dans un dossier.
system.posix_acl_access
xattr contenant l'ACL d'accès.
system.posix_acl_default
xattr contenant l'ACL par défaut d'un dossier.
