Artisan Numérique

/système/sécurité/pam/ Petite introduction à PAM

PAM, le système d'authentification centralisé de Linux même s'il n'est pas parfait, a le mérite de mettre d'accord la majorité des applications sur un mécanisme commun de gestion des crédits utilisateurs tout en restant étonnamment évolutif. L'objectif de ce billet est juste un premier dégrossissage de la manière dont PAM fonctionne et de ce qu'il est possible d'en faire.

Principe

PAM et sa configuration sont les garants de la sécurité de votre système. Je me dégage avec agilité de toute mauvaise manoeuvre qui transformerait votre système en moulin !!

PAM (pour Pluggable Authentication Modules) est un système inventé par SUN Microsystems permettant d'intégrer simplement divers stratégies liées à l'authentification. Depuis 2006, PAM s'est rependu sur les systèmes Linux, Solaris (logique ;-), *BSD et même les Unix propriétaires comme IBM-AIX ou HP-UX.

L'approche modulaire de PAM permet de combiner des mécanismes d'authentification (kerberos, clef USB, lecteur d'empreinte, etc.) mais aussi de procéder à des traitements spécifiques automatisés sur différentes phases de cette authentification (par exemple vider les dossiers temporaires à la déconnexion).

PAM est aussi une API permettant à n'importe qui de développer des modules spécifiques qui peuvent être facilement accrochés à l'une des phases d'authentification.

Enfin PAM est une librairie utilisable par n'importe quelle application pour intégrer ces mécanismes d'authentification à moindre coût, mais surtout en minimisant au maximum les risques de sécurité. Ainsi il existe des librairies PAM pour à peu prés tous les langages (perl, Java, PHP, etc...)

Fonctionnement

Pour commencer, PAM définit des services qui ont généralement pour nom celui des applications qui l'utilisent. Par exemple nous avons le service login qui correspond à la commande du même nom, chargée de la connexion de l'utilisateur en mode console. De même nous avons les services ftp, gdm, ssh, etc. En fait chaque application qui utilise PAM aura son propre service. Service qui généralement prendra la forme d'un fichier du même nom dans le dossier /etc/pam.d.

Chaque service contient une liste de règles. Chaque règle est soit composée d'un groupe de gestion, d'un contrôle , d'un module et des paramètres du module (ex. auth sufficient pam_unix.so shadow), soit d'un groupe de gestion, d'une commande et d'un service (ex. auth include sous-service).

Le groupe de gestion définit un segment de la tâche d'authentification :

  • auth - Vérification de l'identité d'un utilisateur et mise en place des accréditations associées.
  • account - Vérification du compte d'un utilisateur (expiration, plannings, etc.).
  • password - Vérification de la validité des mots de passes lorsqu'ils sont assignés à un utilisateur.
  • session - Vérification de la session d'un utilisateur, de son dossier de travail, etc.

La liste des règles appartenant à un même group de gestion pour un service donné forme ce que l'on appelle la pile de règles du service pour ce groupe de gestion. Par exemple la pile de règle du service login pour le groupe auth pourrait être :

auth        required      pam_env.so
auth        sufficient    pam_unix.so
auth        requisite     pam_deny.so

Ici pour le groupe auth du service login, nous avons une pile de 3 règles : required pam_env.so, sufficient pam_unix.so et requisite pam_deny.so.

Comme nous l'avons vu plus haut, une règle de la pile peut être composé soit d'un contrôle associé à un module et ses paramètres, soit d'une commande associée à un service.

Un module est une librairie exécutable (.so) qui est chargé de vérifier un aspect de l'authentification dans le contexte du groupe de gestion dans lequel il est exécuté. Chaque module va ainsi renvoyer un succès ou un échec. La manière dont ce retour est géré par PAM dans le déroulement du reste de la pile est fonction du contrôle porté par la règle.

  • required - Tous les modules utilisant ce contrôle doivent passer avec succès pour que la vérification soit accordée. Le cas échéant l'utilisateur n'est averti qu'à la fin du traitement de la pile.
  • requisite - La même chose que required sauf que l'utilisateur est averti tout de suite.
  • optionnal - L'échec ou le succès de ce module importe peu et ne peut faire échouer la vérification.
  • sufficient - S'il réussi et qu'il n'y a pas de required en échec, le traitement s'arrête là. Le reste de la pile n'est alors pas traité.

Ainsi dans notre exemple, pam_env.so doit avoir un succès pour que pam_unix.so puisse se lancer, ce qui est toujours le cas. Ensuite pam_unix.so va demander le mot de passe à l'utilisateur et le vérifier avec /etc/shadow. Si c'est un succès, le contrôle sufficient implique que le traitement s'arrête là. Sinon le module pam_deny.so est lancé, et renvoie systématiquement un échec. Le contrôle requisite propage ce problème vers l'utilisateur.

Maintenant que nous voyons comment fonctionnent les règles de type contrôle - module, voyons les commande - service.

  • include - Cette commande demande à PAM de remplacer la règle courante par la pile de même groupe de gestion contenue dans le service en paramètre. Un exemple classique est auth include system-auth. Vu qu'il s'agit d'un simple remplacement, on comprend bien que tous les contrôles s'appliquent et que si les règles de la pile incluse provoquent un échec, la pile qui contient la règle include ... va récupérer cet échec pour elle.
  • substack - Le comportement est à peu prés le même que pour include sauf que là les lignes ne sont pas incluses, le service est appelé. Cela implique qu'en cas d'échec dans la sous-pile, la pile principale poursuit son traitement.

Pour terminer il est est intéressant de savoir que les contrôles sont en réalité des raccourcis d'une syntaxe plus avancée qui va définir l'état de notre pile sous la forme [ valeur1=action1 ... valeurn=actionn]. Par exemple sufficient peut aussi s'écrire [success=done new_authtok_reqd=done default=ignore]. Les valeurs correspondent à des états de la pile. Les actions peuvent être ignore (ne pas prendre en compte), ok (prendre en compte pour un succès), done (prendre en compte pour un succès ET s'arrêter là), bad (pendre en compte pour un échec), die (prendre en compte pour un échec et s'arrêter là) et reset pour réinitialiser les états de la pile. L'action peut aussi être un entier positif N. Dans ce cas, cela agit comme un goto en sautant l'exécution de N modules.

Les valeurs classiques sont success indiquant l'action à mener lorsque le module renvoie un succès, default pour l'action à effectuer par défaut, ignore pour l'action à effectuer si l'on doit ignorer le résultat (souvent on a ignore=ignore ;-)). new_authtok_reqd pour indiquer l'état de l'authentification (AUThenticationToken).

Voilà, fin de la rapide introduction. Pour plus d'information sur PAM, la source est ici.

PAM dans la vraie vie

Si l'on cherche à comprendre comment fonctionne PAM pour un cas concret, il faut commencer par analyser le service associé au programme que l'on cherche à étudier. Prenons l'exemple (au hasard ;-)) de GDM, le gestionnaire de session de Gnome.

GDM a sur la majorité des distribution un service PAM associé en le fichier /etc/pam.d/gdm :

#%PAM-1.0
auth       required     pam_env.so
auth       substack     system-auth
auth       optional     pam_gnome_keyring.so
auth       required     pam_nologin.so

account    include      system-auth

password   include      system-auth

session    optional     pam_console.so
session    optional     pam_gnome_keyring.so auto_start
session    include      system-auth

Comme vous le voyez, le service gdm est complet et définit une pile pour chaque groupe de gestion. Si l'on se concentre sur le groupe auth, nous avons le classique pam_env.so qui va mettre en forme les variables d'environnement en fonction du fichier /etc/security/pam_env.conf. D'un point de vue général, /etc/security est l'endroit où l'on trouve la configuration des modules PAM.

Ensuite nous avons une règle de type command-service qui va faire appel à system-auth/auth. Il s'agit d'une commande de type substack ce qui implique que si cela se passe mal dans ce service, les deux règles suivantes s'exécuteront tout de même. A savoir l'optionnel pam_gnome_keyring qui dérouille le gestionnaire de mots de passe de Gnome, et pam_nologin en contrôle required qui empêche l'utilisateur de se connecter lorsqu'existe le fichier /etc/nologin.

Maintenant jetons un œil au service system-auth qui est le plus intéressant car c'est lui que finalement, par des include ou des substack, tous les services appellent à un moment ou à un autre. Ici j'ai limité la liste de règles au groupe de gestion auth:

#%PAM-1.0

auth        required      pam_env.so
auth        sufficient    pam_tcb.so shadow fork nullok prefix=$2a$ count=8
auth        sufficient    pam_ldap.so use_first_pass
auth        requisite pam_deny.so
...

Le déroulement du service pour auth est donc :

  1. pam_env, encore une fois.
  2. sufficient pam_tcb. Sur d'autres distributions pam_tcb sera plutôt pam_unix. le système TCB est un système qui vient remplacer, de manière plus sécurisée, le vénérable mécanisme basé sur shadow. Comme aucun mot de passe n'est fournit à ce module, il va chercher à en demander un à l'utilisateur et vérifier si ce mot de passe lui plaît. Si c'est le cas, l'exécution de la pile s'arrête là.
  3. pam_ldap, si pam_tcb échoue, le module LDAP tente sa chance. Le mot clef use_first_pass indique au module de ne pas redemander de mot de passe mais d'utiliser celui qui lui a été fournit par pam_tcb. Sufficient indique que si le module LDAP valide ce mot de passe, l'exécution s'arrête là.
  4. Arrive enfin le module pam_deny qui récupère la balle et qui ne sait faire qu'une seule chose : échouer.

Conclusion

PAM est une très belle brique de l'ensemble GNU/Linux qui a permis aux applications de complètement se décharger des délicates tâches liées à l'authentification. C'est grâce à elle que l'ajout d'un serveur LDAP, d'une authentification par clef USB et autre spécificité peut être implémenté sans crainte de voir certains services ne plus fonctionner.