LINUX:SELinux-Exercice de création d'un module personnel

De WIKI sur Linux (ADB)
Aller à la navigation Aller à la recherche

retour à SELinux


But

J'ai voulu essayer de créer un module pour une application personnelle élémentaire.

Elle est basée sur deux programmes effectuant le même type de tâche mais écrits dans deux langages différents. L'un utilisera les droits SUID et SGID. La tâche consiste à lire un fichier texte et d'en ajouter le contenu à un autre fichier.


Python

Le premier programme est écrit en Python qui est un langage interprété.

Voici la source mise dans le fichier "lireecrire.py":


#!/usr/bin/python3
 
def main():
  filein  = open("/gestion/data/data.in","rt")
  fileout = open("/gestion/data/data.out","at")
 
  ligne = filein.readline()
  while ligne != "":
   fileout.write(ligne)
   ligne = filein.readline()
 
  filein.close()
  fileout.close()
 
main()

On remarque la première ligne qui permet de lancer directement ce fichier pour autant qu'il soit exécutable.

L'interpréteur Python est normalement installé au vu du nombre de logiciels qui l'utilisent.

On crée un fichier de données "data.in".


C

Le second programme est écrit en C; il sera compilé.

Voici la source mise dans le fichier "readwrite.c":


#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
 
void main()
 {
 char nomfichin[50];
 char nomfichout[50];
 char ligne[500];
 FILE   *fpti;
 FILE   *fpto;
 
 strcpy(nomfichin,"/gestion/data/datac.in");
 strcpy(nomfichout,"/gestion/data/datac.out");
 
 fpti=fopen(nomfichin,"rt");
 fpto=fopen(nomfichout,"at");
 
 while(fgets(ligne, sizeof(ligne), fpti) != NULL)
  {
  fputs(ligne, fpto);
  }
 
 fclose(fpto);
 fclose(fpti);
 
 exit(0);
}

Les fichiers d'entrée et de sortie portent des noms différents de ceux du programme fait en Python.

On doit compiler ce programme avec la commande suivante:

cc readwrite.c -lm -o readwrite.exe

L'exécutable porte le nom "readwrite.exe".

Il faut évidemment avoir installé le nécessaire minimum pour le développement en C.

dnf install cpp

On crée un fichier de données "datac.in".


Structure

Une arborescence est créée:

  • /gestion: répertoire de base
  • /gestion/bin: répertoire pour les programmes
  • /gestion/data: répertoire pour les données

Toute ce contenu appartient à l'utilisateur "root":

chown -R root:root /gestion

Les droits d'accès sont les suivants:

chmod  777 /gestion
chmod  777 /gestion/data
chmod  644 /gestion/data/data.in
chmod  640 /gestion/data/datac.in
chmod  755 /gestion/bin
chmod 6711 /gestion/bin/readwrite.exe
chmod  755 /gestion/bin/lireecrire.py

Tout le monde doit pouvoir lire, écrire et créer de nouveaux fichiers dans le répertoire "/gestion/data".

Le script Python doit être lisible et exécutable par tout le monde et son fichier de données doit être lisible par tous. Le fichier de données résultant appartiendra à l'utilisateur qui a lancé ce programme.

Par contre l'exécutable C a les privilèges SUID et SGID et doit pouvoir être exécuté par tous. Ce qui implique que quand un utilisateur exécute ce programme, il s'exécutera avec les droits de son propriétaire "root" et donc le fichier à lire ne nécessite pas des droits d'accès à tout le monde et le fichier résultant appartiendra à l'utilisateur "root" avec ses droits.

Notons qu'il n'est possible de donner les droits SUID et SGID au script python; ils ne peuvent être donnés qu'à un exécutable binaire; c'est notamment le cas de notre programme C compilé. Il faudrait les donner à l'interpréteur Python. Je n'ai pas essayé au vu des dégâts potentiels; Python est largement utilisé au travers de la machine, ce qui amènerait des changements de droits et des problèmes de sécurité. Cet exemple me permet d'approcher deux méthodes d'accès.

Remarquons que le "fcontext" de ce nouvel espace n'est pas défini ("/gestion"), il acquiert le Fcontext "system_u:object_r:default_t:s0" par défaut. On rencontrera ce type à la fin de cet article.


On crée un utilisateur classique nommé "dupont" qui appartient au groupe "users" et son espace est dans le répertoire "/home/dupont". Il rejoint les utilisateurs SELinux "user_u":

adduser dupont -g users -Z user_u
passwd dupont

On lui attribue un mot de passe.

Quand on exécute ces programmes à partir de cet utilisateur hors du contexte SELinux, tout se passe bien. Mais dès que l'on active SELinux, leur exécution est interdite.

A noter que si on transposait ces applications dans l'espace de cet utilisateur, avec SELinux actif, il n'y aurait aucun problème.


Module pour les applications

Nous allons créer un premier module destinés aux applications. On le nomme "my-gestion".

Voici le premier fichier "my-gestion.te":


module my-gestion 1.0;
 
require {
       attribute domain;
       attribute entry_type, exec_type, file_type;
       class file { open map read execute ioctl entrypoint lock };
       class dir { add_name write search };
       class file { append create getattr };
       type bin_t;
       class dir { open read };
}
 
# création du domain
type gestion_t, domain;
 
# création du type pour les exécutables
type gestion_exec_t, entry_type, exec_type, file_type;
allow gestion_t gestion_exec_t:file { entrypoint execute getattr ioctl lock map open read };
 
# création du type pour les données
type gestion_data_t, file_type;
# nécessaire lors de l'exécution pour l'accès aux fichiers de données
allow gestion_t gestion_data_t:dir { add_name search write open read };
allow gestion_t gestion_data_t:file { append create getattr ioctl open read };
 
# accès à python
allow gestion_t bin_t:file { execute map };

On crée les types "gestion_t" pour le domain (attribut: "domain"), "gestion_exec_t" (attributs: "entry_type, exec_type, file_type") pour les exécutables et "gestion_data_t" (attribut: "file_type") pour les données. Chaque attribut doit être déclaré. L'attribut "entry_type" sera utilisé dans le second module pour la "transition" des droits des exécutables.

Les règles d'accès pour les types "gestion_exec_t" et "gestion_data_t" ont été déduits des alertes et semblent logiques.

Des droits d'accès spéciaux s'ajoutent à la fin pour pouvoir avoir accès à l'interpréteur Python par l'application "lireecrire.py".


Le second fichier concerne le "Fcontext"; ce fichier se nomme "my-gestion.fc":


/gestion/bin/readwrite.exe   -- gen_context(system_u:object_r:gestion_exec_t,s0)
/gestion/bin/lireecrire.py   -- gen_context(system_u:object_r:gestion_exec_t,s0)
/gestion/data(/.*)?             gen_context(system_u:object_r:gestion_data_t,s0)

On y lie les fichiers exécutables au type "gestion_exec_t" et le répertoire des données et son contenu au type "gestion_data_t".

Le fichier "my-gestion.if" est vide.


On compile et installe ce module comme décrit dans l'article sur les Modules.

rm -f my-gestion.pp ; make -f /usr/share/selinux/devel/Makefile my-gestion.pp
semodule -X 300 -i my-gestion.pp

Ensuite on n'oublie pas d'appliquer ce Fcontext:

restorecon -RFv /gestion/bin/readwrite.exe
restorecon -RFv /gestion/bin/lireecrire.py
restorecon -RFv /gestion/data/


Module pour donner l'accès aux utilisateurs

La seconde étape est donner l'accès aux utilisateur "user_u" au travers du rôle "user_r".

On nommera ce module "my-gestion-user".

Voici le contenu du fichier "my-gestion-user.te"; les fichiers "my-gestion-user.fc" et "my-gestion-user.if" sont vides.


module my-gestion-user 0.1 ;
 
require {
       role user_r;
       type user_t;
       type gestion_t;
       type gestion_exec_t;
 
       class process { transition noatsecure rlimitinh siginh };
       class file { execute read getattr open };
 
       type user_devpts_t;
       type user_home_dir_t;
       type gconf_home_t;
       class chr_file { read write getattr ioctl };
       class dir search;
}
 
role user_r types gestion_t;
type_transition user_t gestion_exec_t:process gestion_t;
allow user_t gestion_t:process transition;
 
# nécessaire pour exécution
allow user_t gestion_t:process { noatsecure rlimitinh siginh };
allow user_t gestion_exec_t:file { execute read getattr open };
 
# nécessaire pour des droits d'accès à l'espace de l'utilisateur notamment pour l'environnement de Python.
allow gestion_t user_devpts_t:chr_file { read write getattr ioctl };
allow gestion_t user_home_dir_t:dir search;
allow gestion_t gconf_home_t:dir search;

En premier, on ajoute l'accès du type "gestion_t" au rôle "user_r".

Ensuite on transmet les droits de lancement du process de "gestion_t" au type "user_t" via la règle "type_transition"; autrement dit, quand "user_t" exécute une tâche de "gestion_exec_t", il l'exécute au nom de "gestion_t". Et on lui donne les droits de "transition".

Et on donne les droits à l'utilisateur de lire et d'exécuter les programmes.


Le dernier bloc sert à éliminer des alertes non bloquantes suite à la demande d'accès de "gestion_t" à l'espace de l'utilisateur notamment pour l'environnement par Python.


On compile le module et on l'installe.

Maintenant l'utilisateur peut exécuter ces programmes sans soucis en mode SELinux actif.


Pour information, c'est au cours de la création de ce module que j'ai dû désactiver au début et pour un court moment les règles "dontaudit" par la commande:

semodule -DB

afin de débloquer la situation. Car à cet étape, il n'y avait plus d'alertes mais l'exécution des programmes n'était pas permise.


Module complémentaire

Notons que si l'utilisateur veut parcourir cette arborescence et visualiser les données liées au programme Python, on doit lui donner encore d'autres droits.

Pour information, voici un module complémentaire non nécessaire pour donner ces droits:


module my-gestion-user-suite 0.1 ;
 
require {
       type user_t;
       type default_t;
       type gestion_data_t;
       class dir { getattr read open search };
       class file { getattr read open };
}
 
# nécessaire pour explorer /gestion
allow user_t default_t:dir read;
allow user_t default_t:file getattr;
# nécessaire pour explorer les données
allow user_t gestion_data_t:dir { getattr read open search };
allow user_t gestion_data_t:file { getattr read open };




retour à SELinux