                        Pluggable Authentication Modules

  Dag-Erling Smo/rgrav

   Contributed by  
   Version: 8def749c53

   Copyright (c) 2001-2003 Networks Associates Technology, Inc.

   Cet article a ete ecrit pour le Projet FreeBSD par ThinkSec AS et les
   laboratoires de Networks Associates, la division de recherche en securite
   de Networks Associates, Inc. sous le contrat DARPA/SPAWAR N66001-01-C-8035
   (<< CBOSS >>), en tant que partie du programme de recherche DARPA CHATS.

   Resume

   Cet article decrit les principes sous-jacent et les mecanismes de la
   bibliotheque PAM, il explique comment configurer PAM, l'integrer dans les
   applications, et ecrire ses propres modules PAM.

   Version franc,aise de Clement Mathieu <cykl@mAdchAt.org>.

   [ Multiples pages HTML / Page HTML unique ]

     ----------------------------------------------------------------------

   Table des matieres

   1. Introduction

   2. Termes et conventions

   3. Les bases de PAM

   4. Configuration de PAM

   5. Les modules PAM de FreeBSD

   6. Programmation d'applications PAM

   7. Programmation de modules PAM

   A. Exemples d'application PAM

   B. Exemple d'un module PAM

   C. Exemple d'une fonction de conversation PAM

   Lectures complementaires

1. Introduction

   La bibliotheque PAM est une API generalisee pour les services relevant de
   l'authentification permettant `a un administrateur systeme d'ajouter une
   nouvelle methode d'authentification en ajoutant simplement un nouveau
   module PAM, ainsi que de modifier les regles d'authentification en editant
   les fichiers de configuration.

   PAM a ete conc,u et developpe en 1995 par Vipin Samar et Charlie Lai de
   Sun Microsystems, et n'a pas beaucoup evolue depuis. En 1997 l'Open Group
   publie les premieres specifications XSSO qui standardisent l'API PAM et
   ajoute des extensions pour un simple (ou plutot integre) "sign-on". Lors
   de l'ecriture de cet article, la specification n'a toujours pas ete
   adoptee comme standard.

   Bien que cet article se concentre principalement sur FreeBSD 5.x, qui
   utilise OpenPAM, il devrait egalement etre applicable `a FreeBSD 4.x qui
   utilise Linux-PAM, ainsi qu'`a d'autres systemes d'exploitations tels que
   Linux ou Solaris.

  1.1. Marques deposees

   Sun, Sun Microsystems, SunOS and Solaris are trademarks or registered
   trademarks of Sun Microsystems, Inc.

   UNIX and The Open Group are trademarks or registered trademarks of The
   Open Group.

   All other brand or product names mentioned in this document may be
   trademarks or registered trademarks of their respective owners.

2. Termes et conventions

  2.1. Definitions

   La terminologie de PAM est plutot confuse. Ni la publication originale de
   Samar et Lai, ni la specification XSSO n'ont essaye de definir
   formellement des termes pour les acteurs et les entites intervenant dans
   PAM, les termes qu'ils utilisent (mais ne definissent pas) sont parfois
   trompeurs et ambigus. Le premier essai d'etablir une terminologie
   consistante et non ambigue fut un papier ecrit par Andrew G. Morgan
   (l'auteur de Linux-PAM) en 1999. Bien que les choix de Morgan furent un
   enorme pas en avant, ils ne sont pas parfait d'apres l'auteur de ce
   document. Ce qui suit, largement inspire par Morgan, est un essai de
   definir precisement et sans ambiguite des termes pour chaque acteur ou
   entite utilise dans PAM.

   compte

           L'ensemble de permissions que le demandeur demande a l'arbitre.

   demandeur

           L'utilisateur ou l'entite demandant authentification.

   arbitre

           L'utilisateur ou l'entite possedant les privileges necessaires
           pour verifier la requete du demandeur ainsi que l'autorite
           d'accorder ou de rejeter la requete.

   chaine

           Une sequence de modules qui sera invoquee pour repondre `a une
           requete PAM. La chaine comprend les informations concernant
           l'ordre dans lequel invoquer les modules, les arguments `a leur
           passer et la fac,on d'interpreter les resultats.

   client

           L'application responsable de la requete d'authentification au nom
           du demandeur et de recueillir l'information d'authentification
           necessaire.

   mecanisme

           Il s'agit de l'un des quatre groupes basiques de fonctionnalites
           fournit par PAM : authentification, gestion de compte, gestion de
           session et mise `a jour du jeton d'authentification.

   module

           Une collection d'une ou plusieurs fonctions implementant un
           service d'authentification particulier, rassemblees dans un
           fichier binaire (normalement chargeable dynamiquement) et
           identifie par un nom unique.

   regles

           Le jeu complet de configuration des regles decrivant comment
           traiter les requetes PAM pour un service particulier. Une regle
           consiste normalement en quatre chaines, une pour chaque mecanisme,
           bien que quelques services n'utilisent pas les quatre mecanismes.

   serveur

           L'application agissant au nom de l'arbitre pour converser avec le
           client, recuperer les informations d'authentification, verifier
           les droits du demandeur et autoriser ou rejeter la requete.

   service

           Un ensemble de serveurs fournissant des fonctionnalites similaires
           ou liees et necessitant une authentification similaire. Les regles
           de PAM sont definies sur un le principe de par-service; ainsi tous
           les serveurs qui demandent le meme nom de service seront soumis
           aux memes regles.

   session

           Le contexte dans lequel le service est delivre au demandeur par le
           serveur. L'un des quatre mecanismes de PAM, la gestion de session,
           s'en occupe exclusivement par la mise en place et le relachement
           de ce contexte.

   jeton

           Un morceau d'information associe avec un compte tel qu'un mot de
           passe ou une passphrase que le demandeur doit fournir pour prouver
           son identite.

   transaction

           Une sequence de requetes depuis le meme demandeur vers la meme
           instance du meme serveur, commenc,ant avec l'authentification et
           la mise en place de la session et se terminant avec le demontage
           de la session.

  2.2.  Exemples d'utilisation

   Cette section a pour but d'illustrer quelques-uns des termes definis
   precedemment `a l'aide d'exemples basiques.

    2.2.1. Client et serveurs ne font qu'un

   Cet exemple simple montre alice utilisant su(1) pour devenir root.

 % whoami
 alice
 % ls -l `which su`
 -r-sr-xr-x  1 root  wheel  10744 Dec  6 19:06 /usr/bin/su
 % su -
 Password: xi3kiune
 # whoami
 root

     * Le demandeur est alice.

     * Le compte est root.

     * Le processus su(1) est `a la fois client et serveur.

     * Le jeton d'authentification est xi3kiune.

     * L'arbitre est root, ce qui explique pourquoi su(1) est setuid root.

    2.2.2. Client et serveur sont distincts.

   L'exemple suivant montre eve essayant d'initier une connexion ssh(1) vers
   login.exemple.com, en demandant `a se logguer en tant que bob. La
   connexion reussit. Bob aurait du choisir un meilleur mot de passe !

 % whoami
 eve
 % ssh bob@login.example.com
 bob@login.example.com's password: god
 Last login: Thu Oct 11 09:52:57 2001 from 192.168.0.1
 Copyright (c) 1980, 1983, 1986, 1988, 1990, 1991, 1993, 1994
         The Regents of the University of California.  All rights reserved.
 FreeBSD 4.4-STABLE (LOGIN) #4: Tue Nov 27 18:10:34 PST 2001

 Welcome to FreeBSD!
 %

     * Le demandeur est eve.

     * Le client d'eve est represente par les processus ssh(1)

     * Le serveur est le processus sshd(8) sur login.example.com

     * Le compte est bob.

     * Le jeton d'identification est god.

     * Bien que cela ne soit pas montre dans cet exemple, l'arbitre est root.

    2.2.3. Exemple de regles

   Les lignes qui suivent sont les regles par defaut de sshd:

 sshd    auth            required        pam_nologin.so  no_warn
 sshd    auth            required        pam_unix.so     no_warn try_first_pass
 sshd    account         required        pam_login_access.so
 sshd    account         required        pam_unix.so
 sshd    session         required        pam_lastlog.so  no_fail
 sshd    password        required        pam_permit.so

     * Cette politique s'applique au service sshd (qui n'est pas
       necessairement restreint au serveur sshd(8))

     * auth, account, session et password sont des mecanismes.

     * pam_nologin.so, pam_unix.so, pam_login_access.so, pam_lastlog.so et
       pam_permit.so sont des modules. Il est clair dans cet exemple que
       pam_unix.so fournit au moins deux mecanismes ( authentification et
       gestion de compte).

  2.3. Conventions

   Cette section n'a pas encore ete ecrite.

3. Les bases de PAM

  3.1. Mecanismes et primitives

   L'API PAM fournit six primitives d'authentification differentes regroupees
   dans quatre mecanismes qui seront decrits dans la partie suivante.

   auth

           Authentification. Ce mecanisme concerne l'authentification du
           demandeur et etablit les droits du compte. Il fournit deux
           primitives :

              * pam_authenticate(3) authentifie le demandeur, generalement en
                demandant un jeton d'identification et en le comparant a une
                valeur stockee dans une base de donnees ou obtenue par le
                biais d'un serveur d'authentification.

              * pam_setcred(3) etabli les parametres du compte tel que l'uid,
                les groupes dont le compte fait parti ou les limites sur
                l'utilisation des ressources.

   account

           Gestion de compte. Ce mecanisme concerne la disponibilite du
           compte pour des raisons autres que l'authentification. Par exemple
           les restrictions basees sur l'heure courante ou la charge du
           serveur. Il fournit une seule primitive:

              * pam_acct_mgmt(3) verifie que le compte demande est
                disponible.

   session

           Gestion de session. Ce mecanisme concerne la mise en place de la
           session et sa terminaison, par exemple l'enregistrement de la
           session dans les journaux. Il fournit deux primitives:

              * pam_open_session(3) accomplie les taches associees `a la mise
                en place d'une session : ajouter une entree dans les bases
                utmp et wtmp, demarrer un agent SSH...

              * pam_close_session(3) accomplie les taches associees `a la
                terminaison d'une session : ajouter une entree dans les bases
                utmp et wtmp, arreter l'agent SSH...

   password

           Gestion des mots de passe. Ce mecanisme est utilise pour modifier
           le jeton d'authentification associe `a un compte, soit parce qu'il
           a expire, soit parce que l'utilisateur desire le changer. Il
           fournit une seule primitive:

              * pam_chauthtok(3) modifie le jeton d'authentification, et
                eventuellement verifie que celui-ci est assez robuste pour ne
                pas etre devine facilement ou qu'il n'a pas dej`a utilise.

  3.2. Modules

   Les modules sont le concept clef de PAM; apres tout ils constituent le
   << M >> de PAM. Un module PAM est lui-meme un morceau de code qui
   implemente les primitives d'un ou plusieurs mecanismes pour une forme
   particuliere d'authentification; par exemple, les bases de mots de passe
   UNIX que sont NIS, LDAP et Radius.

    3.2.1. Nom des modules

   FreeBSD implemente chaque mecanismes dans un module distinct nomme
   pam_mecanisme.so (par exemple pam_unix.so pour le mecanisme Unix .) Les
   autres implementations possedent parfois des modules separes pour des
   mecanismes separes et incluent aussi bien le nom du service que celui du
   mecanisme dans le nom du module. Un exemple est le module
   pam_dial_auth.so.1 de Solaris qui est utilise pour authentifier les
   utilisateurs dialup.

    3.2.2. Gestion des versions de module

   L'implementation originale de PAM par FreeBSD, basee sur Linux-PAM,
   n'utilisait pas de numero de version pour les modules PAM. Ceci peut poser
   des problemes avec les applications tiers qui peuvent etre liees avec
   d'anciennes bibliotheques systemes, puisqu'il n'y a pas possibilite de
   charger la version correspondante du module desire.

   Pour sa part, OpenPAM cherche les modules qui ont la meme version que la
   bibliotheque PAM (pour le moment 2) et se rabat sur un module sans version
   si aucun module avec version n'a put etre charge. Ainsi les anciens
   modules peuvent etre fournis pour les anciennes applications, tout en
   permettant aux nouvelles applications (ou bien nouvellement compilees) de
   tirer parti des modules les plus recents.

   Bien que les modules PAM de Solaris possedent generalement un numero de
   version, ils ne sont pas reellement versionnes car le numero correspond `a
   une partie du nom du module et doit etre inclus dans la configuration.

  3.3. Chaines et politiques

   Lorsqu'un serveur initie une transaction PAM, la bibliotheque PAM essaie
   de charger une politique pour le service specifie dans l'appel a
   pam_start(3) . La politique indique comment la requete d'authentification
   doit etre traitee et est definie dans un fichier de configuration. Il
   s'agit de l'autre concept clef de PAM : la possibilite pour
   l'administrateur de configurer la politique de securite d'un systeme en
   editant simplement une fichier texte.

   Une politique consiste en quatre chaines, une pour chacune des quatre
   mecanismes de PAM. Chaque chaine est une suite de regles de configuration,
   chacune specifiant un module `a invoquer, des parametres, options, `a
   passer au module et un drapeau de controle qui decrit comment interpreter
   le code de retour du module.

   Comprendre le drapeau de controle est essentiel pour comprendre les
   fichiers de configuration de PAM. Il existe quatre drapeaux de controle
   differents :

   binding

           Si le module reussit et qu'aucun module precedent de la chaine n'a
           echoue, la chaine s'interrompt immediatement et la requete est
           autorisee. Si le module echoue le reste de la chaine est execute,
           mais la requete est rejetee au final.

           Ce drapeau de controle a ete introduit par Sun Solaris dans la
           version 9 (SunOS 5.9); il est aussi supporte par OpenPAM.

   required

           Si le module reussit, le reste de la chaine est execute, et la
           requete est autorisee si aucun des autres modules n'echoue. Si le
           module echoue, le reste de la chaine est execute, mais au final la
           requete est rejetee.

   requisite

           Si le module reussit le reste de la chaine est execute, et la
           requete est autorisee sauf si d'autres modules echoues. Si le
           module echoue la chaine est immediatement terminee et la requete
           est rejetee.

   sufficient

           Si le module reussit et qu'aucun des modules precedent n'a echoue
           la chaine est immediatement terminee et la requete est allouee. Si
           le module echoue il est ignore et le reste de la chaine est
           execute.

           Puisque la semantique de ce drapeau peut etre un peu confuse,
           specialement lorsqu'il s'agit de celui du dernier module de la
           chaine, il est recommande d'utiliser le drapeau binding `a la
           place de celui-ci sous la condition que l'implementation le
           supporte.

   optional

           Le module est execute mais le resultat est ignore. Si tout les
           modules de la chaine sont marques optional, toutes les requetes
           seront toujours acceptees.

   Lorsqu'un serveur invoque l'une des six primitives PAM, PAM recupere la
   chaine du mecanisme `a laquelle la requete correspond et invoque chaque
   module de la chaine dans l'ordre indique, jusqu'`a ce que la fin soit
   atteinte ou qu'aucune execution supplementaire ne soit necessaire (soit `a
   cause du succes d'un module en binding ou sufficient, soit `a cause de
   l'echec d'un module requisite). La requete est acceptee si et seulement si
   au moins un module a ete invoque, et que tout les modules non optionnels
   ont reussi.

   Notez qu'il est possible, bien que peu courant, d'avoir le meme module
   liste plusieurs fois dans la meme chaine. Par exemple un module qui
   determine le nom utilisateur et le mot de passe `a l'aide d'un serveur
   directory peut etre invoque plusieurs fois avec des parametres specifiant
   differents serveurs a contacter. PAM considere les differentes occurrences
   d'un meme module dans une meme chaine comme des modules differents et non
   lies.

  3.4. Transactions

   Le cycle de vie d'une transaction PAM typique est decrit ci-dessous. Notez
   que si l'une de ces etapes echoue, le serveur devrait reporter un message
   d'erreur au client et arreter la transaction.

    1. Si necessaire, le serveur obtient les privileges de l'arbitre par le
       biais d'un mecanisme independant de PAM - generalement en ayant ete
       demarre par root ou en etant setuid root.

    2. Le serveur appel pam_start(3) afin d'initialiser la bibliotheque PAM
       et indique le service et le compte cible, et enregistre une fonction
       de conversation appropriee.

    3. Le serveur obtient diverses informations concernant la transaction
       (tel que le nom d'utilisateur du demandeur et le nom d'hote de la
       machine sur lequel le client tourne) et les soumet `a PAM en utilisant
       la fonction pam_set_item(3).

    4. Le serveur appel pam_authenticate(3) pour authentifier le demandeur.

    5. Le serveur appel la fonction pam_acct_mgmt(3) qui verifie que le
       compte est valide et disponible. Si le mot de passe est correct mais a
       expire, pam_acct_mgmt(3) retournera PAM_NEW_AUTHTOK_REQD `a la place
       de PAM_SUCCESS.

    6. Si l'etape precedente a retourne PAM_NEW_AUTHTOK_REQD, le serveur
       appel maintenant pam_chauthtok(3) pour obliger l'utilisateur `a
       changer le jeton d'authentification du compte desire.

    7. Maintenant que le demandeur a ete correctement authentifie, le serveur
       appelle pam_setcred(3) pour obtenir les privileges du compte desire.
       Il lui est possible de faire ceci parce qu'il agit au nom de l'arbitre
       dont il possede les privileges.

    8. Lorsque les privileges corrects ont ete etabli le serveur appelle
       pam_open_session(3) pour mettre en place la session.

    9. Maintenant le serveur effectue les services demandes par le client -
       par exemple fournir un shell au demandeur.

   10. Lorsque le serveur a fini de servir le client, il appelle
       pam_close_session(3) afin de terminer la session.

   11. Pour finir, le serveur appelle pam_end(3) afin signaler `a la
       bibliotheque PAM que la transaction se termine et qu'elle peut liberer
       les ressources qu'elle a alloue au cours de la transaction.

4. Configuration de PAM

  4.1. Emplacement des fichiers de configuration

   Le fichier de configuration de PAM est traditionnellement /etc/pam.conf.
   Ce fichier contient toutes les politiques de PAM pour votre systeme.
   Chaque ligne du fichier decrit une etape dans une chaine, tel que nous
   allons le voir ci-dessous:

 login   auth    required        pam_nologin.so  no_warn

   Les champs sont respectivement, le service, le nom du mecanisme, le
   drapeau de controle, le nom du module et les arguments du module. Tout
   champ additionnel est considere comme argument du module.

   Une chaine differente est construite pour chaque couple service/mecanisme;
   ainsi, alors que l'ordre des lignes est important lorsqu'il s'agit des
   memes services ou mecanismes, l'ordre dans lequel les differents services
   et mecanismes apparaissent ne l'est pas - excepte l'entree pour le service
   other, qui sert de reference par defaut et doit etre place `a la fin.
   L'exemple du papier original sur PAM regroupait les lignes de
   configurations par mecanisme et le fichier pam.conf de Solaris le fait
   toujours, mais FreeBSD groupe les lignes de configuration par service.
   Toutefois il ne s'agit pas de la seule possibilite et les autres possedent
   aussi un sens.

   OpenPAM et Linux-PAM offrent un mecanisme de configuration alternatif ou
   les politiques sont placees dans des fichiers separes portant le nom du
   service auquel ils s'appliquent. Ces fichiers sont situes dans /etc/pam.d/
   et ne contiennent que quatre champs `a la place de cinq - le champ
   contenant le nom du service est omis. Il s'agit du mode par defaut dans
   FreeBSD 4.x. Notez que si le fichier /etc/pam.conf existe et contient des
   informations de configuration pour des services qui n'ont pas de politique
   specifiee dans /etc/pam.d, ils seront utilises pour ces services.

   Le gros avantage de /etc/pam.d/ sur /etc/pam.conf est qu'il est possible
   d'utiliser la meme politique pour plusieurs services en liant chaque nom
   de service `a un fichier de configuration. Par exemple pour utiliser la
   meme politique pour les services su et sudo, nous pouvons faire comme ceci
   :

 # cd /etc/pam.d
 # ln -s su sudo

   Ceci fonctionne car le nom de service est determine a partir du nom de
   fichier plutot qu'indique `a l'interieur du fichier de configuration,
   ainsi le meme fichier peut etre utilise pour des services nommes
   differemment.

   Un autre avantage est qu'un logiciel tiers peu facilement installer les
   politiques pour ses services sans avoir besoin d'editer /etc/pam.conf.
   Pour continuer la tradition de FreeBSD, OpenPAM regardera dans
   /usr/local/etc/pam.d pour trouver les fichiers de configurations; puis si
   aucun n'est trouve pour le service demande, il cherchera dans /etc/pam.d/
   ou /etc/pam.conf.

   Finalement, quelque soit le mecanisme que vous choisissiez, la politique
   << magique >> other est utilisee par defaut pour tous les services qui
   n'ont pas leur propre politique.

  4.2. Breakdown of a configuration line

   Comme explique dans Section 4.1, << Emplacement des fichiers de
   configuration >>, chaque ligne de pam.conf consiste en quatre champs ou
   plus: le nom de service, le nom du mecanisme, le drapeau de controle, le
   nom du module et la presence ou non d'arguments pour le module.

   Le nom du service est generalement, mais pas toujours, le nom de
   l'application auquelle les regles s'appliquent. Si vous n'etes pas sur,
   referez vous `a la documentation de l'application pour determiner quel nom
   de service elle utilise.

   Notez que si vous utilisez /etc/pam.d/ `a la place de /etc/pam.conf, le
   nom du service est specifie par le nom du fichier de configuration et
   n'est pas indique dans les lignes de configuration qui, des lors,
   commencent par le nom du mecanisme.

   Le mecanisme est l'un des quatre mots clef decrit dans Section 3.1,
   << Mecanismes et primitives >>.

   De meme, le drapeau de controle est l'un des quatre mots clef decrits dans
   Section 3.3, << Chaines et politiques >> et decrit comment le module doit
   interpreter le code de retour du module. Linux-PAM supporte une syntaxe
   alternative qui vous laisse specifier l'action `a associer `a chaque code
   de retour possible; mais ceci devrait etre evite puisque ce n'est pas
   standard et etroitement lie `a la fac,on dont Linux-PAM appelle les
   services (qui differe grandement de la fac,on de Solaris et OpenPAM).
   C'est sans etonnement que l'on apprend qu'OpenPAM ne supporte pas cette
   syntaxe.

  4.3. Politiques

   Pour configurer PAM correctement, il est essentiel de comprendre comment
   les politiques sont interpretees.

   Lorsqu'une application appelle pam_start(3) la bibliotheque PAM charge la
   politique du service specifie et construit les quatre chaines de module
   (une pour chaque mecanisme). Si une ou plusieurs chaines sont vides, les
   chaines de la politique du service other sont utilisees.

   Plus tard, lorsque l'application appelle l'une des six primitives PAM, la
   bibliotheque PAM recupere la chaine du mecanisme correspondant et appelle
   la fonction appropriee avec chaque module liste dans la chaine. Apres
   chaque appel d'une fonction de service, le type du module et le code
   d'erreur sont retournes par celle-ci pour determiner quoi faire. A
   quelques exceptions pres, dont nous parlerons plus tard, la table suivante
   s'applique:

   Tableau 1. Resume de la chaine d'execution PAM

   +-------------------------------------------------------------------+
   |            |    PAM_SUCCESS    | PAM_IGNORE |        other        |
   |------------+-------------------+------------+---------------------|
   | binding    | if (!fail) break; | -          | fail = true;        |
   |------------+-------------------+------------+---------------------|
   | required   | -                 | -          | fail = true;        |
   |------------+-------------------+------------+---------------------|
   | requisite  | -                 | -          | fail = true; break; |
   |------------+-------------------+------------+---------------------|
   | sufficient | if (!fail) break; | -          | -                   |
   |------------+-------------------+------------+---------------------|
   | optional   | -                 | -          | -                   |
   +-------------------------------------------------------------------+

   Si fail est vrai `a la fin de la chaine, ou lorsqu'un << break >> est
   atteint, le dispatcheur retourne le code d'erreur renvoye par le premier
   module qui a echoue. Autrement PAM_SUCCESS est retourne.

   La premiere exception est que le code d'erreur PAM_NEW_AUTHOK_REQD soit
   considere comme un succes, sauf si aucun module n'echoue et qu'au moins un
   module retourne PAM_NEW_AUTHOK_REQD le dispatcheur retournera
   PAM_NEW_AUTHOK_REQD.

   La seconde exception est que pam_setcred(3) considere les modules binding
   et sufficient comme s'ils etaient required.

   La troisieme et derniere exception est que pam_chauthtok(3) execute la
   totalite de la chaine deux fois (la premiere pour des verifications
   preliminaires et la deuxieme pour mettre le mot de passe) et lors de la
   premiere execution il considere les modules binding et sufficient comme
   s'ils etaient required.

5. Les modules PAM de FreeBSD

  5.1. pam_deny(8)

   Le module pam_deny(8) est l'un des modules disponibles les plus simples;
   il repond `a n'importe qu'elle requete par PAM_AUTH_ERR. Il est utile pour
   desactiver rapidement un service (ajoutez-le au debut de chaque chaine),
   ou pour terminer les chaines de modules sufficient.

  5.2. pam_echo(8)

   Le module pam_echo(8) passe simplement ses arguments `a la fonction de
   conversation comme un message PAM_TEXT_INFO. Il est principalement utilise
   pour le debogage mais il peut aussi servir `a afficher un message tel que
   << Les acces illegaux seront poursuivits >> avant de commencer la
   procedure d'authentification.

  5.3. pam_exec(8)

   Le module pam_exec(8) prend comme premier argument le nom du programme `a
   executer, les arguments restant etant utilises comme arguments pour ce
   programme. L'une des applications possibles est d'utiliser un programme
   qui monte le repertoire de l'utilisateur lors du login.

  5.4. pam_ftp(8)

   Le module pam_ftp(8)

  5.5. pam_ftpusers(8)

   Le module pam_ftpusers(8)

  5.6. pam_group(8)

   Le module pam_group(8) accepte ou rejette le demandeur `a partir de son
   appartenance `a un groupe particulier (generalement wheel pour su(1)). Il
   a pour but premier de conserver le comportement traditionnel de su(1) mais
   possede d'autres applications comme par exemple exclure un certain groupe
   d'utilisateurs d'un service particulier.

  5.7. pam_krb5(8)

   Le module pam_krb5(8)

  5.8. pam_ksu(8)

   Le module pam_ksu(8)

  5.9. pam_lastlog(8)

   Le module pam_lastlog(8)

  5.10. pam_login_access(8)

   Le module pam_login_access(8)

  5.11. pam_nologin(8)

   Le module pam_nologin(8)

  5.12. pam_opie(8)

   Le module pam_opie(8) implemente la methode d'authentification opie(4). Le
   systeme opie(4) est un mecanisme de challenge-response ou la reponse `a
   chaque challenge est une fonction directe du challenge et une phrase de
   passe, ainsi la reponse peut facilement etre calculee << en temps voulu >>
   par n'importe qui possedant la phrase de passe ce qui elimine le besoin
   d'une liste de mots de passe. De plus, puisque opie(4) ne reutilise jamais
   un mot de passe qui a rec,u une reponse correcte, il n'est pas vulnerable
   aux attaques basee sur le rejouage.

  5.13. pam_opieaccess(8)

   Le module pam_opieaccess(8) est un compagnon du module pam_opie(8). Son
   but est de renforcer les restrictions codifiees dans opieaccess(5), il
   regule les conditions sous lesquelles un utilisateur qui normalement
   devrait s'authentifier par opie(4) est amene `a utiliser d'autres
   methodes. Ceci est generalement utilise pour interdire l'authentification
   par mot de passe depuis des hotes non digne de confiance.

   Pour etre reellement effectif, le module pam_opieaccess(8) doit etre liste
   comme requisite immediatement apres une entree sufficient pour pam_opie(8)
   et avant tout autre module, dans la chaine auth.

  5.14. pam_passwdqc(8)

   Le module pam_passwdqc(8)

  5.15. pam_permit(8)

   Le module pam_permit(8) est l'un des modules disponibles les plus simples;
   il repond `a n'importe quelle requete par PAM_SUCCESS. Il est utile pour
   les services ou une ou plusieurs chaines auraient autrement ete vides.

  5.16. pam_radius(8)

   Le module pam_radius(8)

  5.17. pam_rhosts(8)

   Le module pam_rhosts(8)

  5.18. pam_rootok(8)

   Le module pam_rootok(8) retourne un succes si et seulement si
   l'identifiant d'utilisateur reel du processus appelant est 0. Ceci est
   utile pour les services non bases sur le reseau tel que su(1) ou passwd(1)
   ou l'utilisateur root doit avoir un acces automatique.

  5.19. pam_securetty(8)

   Le module pam_securetty(8)

  5.20. pam_self(8)

   Le module pam_self(8) retourne un succes si et seulement si le nom du
   demandeur correspond au nom du compte desire. Il est utile pour les
   services non bases sur le reseau tel que su(1) ou l'identite du demandeur
   peut etre verifiee facilement .

  5.21. pam_ssh(8)

   Le module pam_ssh(8)

  5.22. pam_tacplus(8)

   Le module pam_tacplus(8)

  5.23. pam_unix(8)

   Le module pam_unix(8) implemente l'authentification Unix traditionnelle
   par mot de passe, il utilise getpwnam(3) pour obtenir le mot de passe du
   compte vise et le compare avec celui fournit par le demandeur. Il fournit
   aussi des services de gestion de compte (desactivation du compte et date
   d'expiration) ainsi que des services pour le changement de mot de passe.
   Il s'agit certainement du module le plus utile car la plupart des
   administrateurs desirent garder le comportement historique pour quelques
   services.

6. Programmation d'applications PAM

   Cette section n'a pas encore ete ecrite.

7. Programmation de modules PAM

   Cette section n'a pas ete encore ecrite.

A. Exemples d'application PAM

   Ce qui suit est une implementation minimale de su(1) en utilisant PAM.
   Notez qu'elle utilise la fonction de conversation openpam_ttyconv(3)
   specifique `a OpenPAM qui est prototypee dans security/openpam.h. Si vous
   desirez construire cette application sur un systeme utilisant une
   bibliotheque PAM differente vous devrez fournir votre propre fonction de
   conversation. Une fonction de conversation robuste est etonnamment
   difficile `a implementer; celle presentee dans Annexe C, Exemple d'une
   fonction de conversation PAM est un bon point de depart, mais ne devrait
   pas etre utilisee dans des applications reelles.

 /*-
  * Copyright (c) 2002,2003 Networks Associates Technology, Inc.
  * All rights reserved.
  *
  * This software was developed for the FreeBSD Project by ThinkSec AS and
  * Network Associates Laboratories, the Security Research Division of
  * Network Associates, Inc.  under DARPA/SPAWAR contract N66001-01-C-8035
  * ("CBOSS"), as part of the DARPA CHATS research program.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
  * are met:
  * 1. Redistributions of source code must retain the above copyright
  *    notice, this list of conditions and the following disclaimer.
  * 2. Redistributions in binary form must reproduce the above copyright
  *    notice, this list of conditions and the following disclaimer in the
  *    documentation and/or other materials provided with the distribution.
  * 3. The name of the author may not be used to endorse or promote
  *    products derived from this software without specific prior written
  *    permission.
  *
  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  *
  * $P4: //depot/projects/openpam/bin/su/su.c#10 $
  * $FreeBSD$
  */

 #include <sys/param.h>
 #include <sys/wait.h>

 #include <err.h>
 #include <pwd.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <syslog.h>
 #include <unistd.h>

 #include <security/pam_appl.h>
 #include <security/openpam.h>   /* for openpam_ttyconv() */

 extern char **environ;

 static pam_handle_t *pamh;
 static struct pam_conv pamc;

 static void
 usage(void)
 {

         fprintf(stderr, "Usage: su [login [args]]\n");
         exit(1);
 }

 int
 main(int argc, char *argv[])
 {
         char hostname[MAXHOSTNAMELEN];
         const char *user, *tty;
         char **args, **pam_envlist, **pam_env;
         struct passwd *pwd;
         int o, pam_err, status;
         pid_t pid;

         while ((o = getopt(argc, argv, "h")) != -1)
                 switch (o) {
                 case 'h':
                 default:
                         usage();
                 }

         argc -= optind;
         argv += optind;

         if (argc > 0) {
                 user = *argv;
                 --argc;
                 ++argv;
         } else {
                 user = "root";
         }

         /* initialize PAM */
         pamc.conv = &openpam_ttyconv;
         pam_start("su", user, &pamc, &pamh);

         /* set some items */
         gethostname(hostname, sizeof(hostname));
         if ((pam_err = pam_set_item(pamh, PAM_RHOST, hostname)) != PAM_SUCCESS)
                 goto pamerr;
         user = getlogin();
         if ((pam_err = pam_set_item(pamh, PAM_RUSER, user)) != PAM_SUCCESS)
                 goto pamerr;
         tty = ttyname(STDERR_FILENO);
         if ((pam_err = pam_set_item(pamh, PAM_TTY, tty)) != PAM_SUCCESS)
                 goto pamerr;

         /* authenticate the applicant */
         if ((pam_err = pam_authenticate(pamh, 0)) != PAM_SUCCESS)
                 goto pamerr;
         if ((pam_err = pam_acct_mgmt(pamh, 0)) == PAM_NEW_AUTHTOK_REQD)
                 pam_err = pam_chauthtok(pamh, PAM_CHANGE_EXPIRED_AUTHTOK);
         if (pam_err != PAM_SUCCESS)
                 goto pamerr;

         /* establish the requested credentials */
         if ((pam_err = pam_setcred(pamh, PAM_ESTABLISH_CRED)) != PAM_SUCCESS)
                 goto pamerr;

         /* authentication succeeded; open a session */
         if ((pam_err = pam_open_session(pamh, 0)) != PAM_SUCCESS)
                 goto pamerr;

         /* get mapped user name; PAM may have changed it */
         pam_err = pam_get_item(pamh, PAM_USER, (const void **)&user);
         if (pam_err != PAM_SUCCESS || (pwd = getpwnam(user)) == NULL)
                 goto pamerr;

         /* export PAM environment */
         if ((pam_envlist = pam_getenvlist(pamh)) != NULL) {
                 for (pam_env = pam_envlist; *pam_env != NULL; ++pam_env) {
                         putenv(*pam_env);
                         free(*pam_env);
                 }
                 free(pam_envlist);
         }

         /* build argument list */
         if ((args = calloc(argc + 2, sizeof *args)) == NULL) {
                 warn("calloc()");
                 goto err;
         }
         *args = pwd->pw_shell;
         memcpy(args + 1, argv, argc * sizeof *args);

         /* fork and exec */
         switch ((pid = fork())) {
         case -1:
                 warn("fork()");
                 goto err;
         case 0:
                 /* child: give up privs and start a shell */

                 /* set uid and groups */
                 if (initgroups(pwd->pw_name, pwd->pw_gid) == -1) {
                         warn("initgroups()");
                         _exit(1);
                 }
                 if (setgid(pwd->pw_gid) == -1) {
                         warn("setgid()");
                         _exit(1);
                 }
                 if (setuid(pwd->pw_uid) == -1) {
                         warn("setuid()");
                         _exit(1);
                 }
                 execve(*args, args, environ);
                 warn("execve()");
                 _exit(1);
         default:
                 /* parent: wait for child to exit */
                 waitpid(pid, &status, 0);

                 /* close the session and release PAM resources */
                 pam_err = pam_close_session(pamh, 0);
                 pam_end(pamh, pam_err);

                 exit(WEXITSTATUS(status));
         }

 pamerr:
         fprintf(stderr, "Sorry\n");
 err:
         pam_end(pamh, pam_err);
         exit(1);
 }


B. Exemple d'un module PAM

   Ce qui suit est une implementation minimale de pam_unix(8) offrant
   uniquement les services d'authentification. Elle devrait compiler et
   tourner avec la plupart des implementations PAM, mais tire parti des
   extensions d'OpenPAM si elles sont disponibles : notez l'utilisation de
   pam_get_authtok(3) qui simplifie enormement l'affichage de l'invite pour
   demander le mot de passe `a l'utilisateur.

 /*-
  * Copyright (c) 2002 Networks Associates Technology, Inc.
  * All rights reserved.
  *
  * This software was developed for the FreeBSD Project by ThinkSec AS and
  * Network Associates Laboratories, the Security Research Division of
  * Network Associates, Inc.  under DARPA/SPAWAR contract N66001-01-C-8035
  * ("CBOSS"), as part of the DARPA CHATS research program.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
  * are met:
  * 1. Redistributions of source code must retain the above copyright
  *    notice, this list of conditions and the following disclaimer.
  * 2. Redistributions in binary form must reproduce the above copyright
  *    notice, this list of conditions and the following disclaimer in the
  *    documentation and/or other materials provided with the distribution.
  * 3. The name of the author may not be used to endorse or promote
  *    products derived from this software without specific prior written
  *    permission.
  *
  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  *
  * $P4: //depot/projects/openpam/modules/pam_unix/pam_unix.c#3 $
  * $FreeBSD$
  */

 #include <sys/param.h>

 #include <pwd.h>
 #include <stdlib.h>
 #include <stdio.h>
 #include <string.h>
 #include <unistd.h>

 #include <security/pam_modules.h>
 #include <security/pam_appl.h>

 #ifndef _OPENPAM
 static char password_prompt[] = "Password:";
 #endif

 #ifndef PAM_EXTERN
 #define PAM_EXTERN
 #endif

 PAM_EXTERN int
 pam_sm_authenticate(pam_handle_t *pamh, int flags,
         int argc, const char *argv[])
 {
 #ifndef _OPENPAM
         struct pam_conv *conv;
         struct pam_message msg;
         const struct pam_message *msgp;
         struct pam_response *resp;
 #endif
         struct passwd *pwd;
         const char *user;
         char *crypt_password, *password;
         int pam_err, retry;

         /* identify user */
         if ((pam_err = pam_get_user(pamh, &user, NULL)) != PAM_SUCCESS)
                 return (pam_err);
         if ((pwd = getpwnam(user)) == NULL)
                 return (PAM_USER_UNKNOWN);

         /* get password */
 #ifndef _OPENPAM
         pam_err = pam_get_item(pamh, PAM_CONV, (const void **)&conv);
         if (pam_err != PAM_SUCCESS)
                 return (PAM_SYSTEM_ERR);
         msg.msg_style = PAM_PROMPT_ECHO_OFF;
         msg.msg = password_prompt;
         msgp = &msg;
 #endif
         for (retry = 0; retry < 3; ++retry) {
 #ifdef _OPENPAM
                 pam_err = pam_get_authtok(pamh, PAM_AUTHTOK,
                     (const char **)&password, NULL);
 #else
                 resp = NULL;
                 pam_err = (*conv->conv)(1, &msgp, &resp, conv->appdata_ptr);
                 if (resp != NULL) {
                         if (pam_err == PAM_SUCCESS)
                                 password = resp->resp;
                         else
                                 free(resp->resp);
                         free(resp);
                 }
 #endif
                 if (pam_err == PAM_SUCCESS)
                         break;
         }
         if (pam_err == PAM_CONV_ERR)
                 return (pam_err);
         if (pam_err != PAM_SUCCESS)
                 return (PAM_AUTH_ERR);

         /* compare passwords */
         if ((!pwd->pw_passwd[0] && (flags & PAM_DISALLOW_NULL_AUTHTOK)) ||
             (crypt_password = crypt(password, pwd->pw_passwd)) == NULL ||
             strcmp(crypt_password, pwd->pw_passwd) != 0)
                 pam_err = PAM_AUTH_ERR;
         else
                 pam_err = PAM_SUCCESS;
 #ifndef _OPENPAM
         free(password);
 #endif
         return (pam_err);
 }

 PAM_EXTERN int
 pam_sm_setcred(pam_handle_t *pamh, int flags,
         int argc, const char *argv[])
 {

         return (PAM_SUCCESS);
 }

 PAM_EXTERN int
 pam_sm_acct_mgmt(pam_handle_t *pamh, int flags,
         int argc, const char *argv[])
 {

         return (PAM_SUCCESS);
 }

 PAM_EXTERN int
 pam_sm_open_session(pam_handle_t *pamh, int flags,
         int argc, const char *argv[])
 {

         return (PAM_SUCCESS);
 }

 PAM_EXTERN int
 pam_sm_close_session(pam_handle_t *pamh, int flags,
         int argc, const char *argv[])
 {

         return (PAM_SUCCESS);
 }

 PAM_EXTERN int
 pam_sm_chauthtok(pam_handle_t *pamh, int flags,
         int argc, const char *argv[])
 {

         return (PAM_SERVICE_ERR);
 }

 #ifdef PAM_MODULE_ENTRY
 PAM_MODULE_ENTRY("pam_unix");
 #endif


C. Exemple d'une fonction de conversation PAM

   La fonction de conversation presentee ci-dessous est une version
   grandement simplifiee de la fonction openpam_ttyconv(3) d'OpenPAM. Elle
   est pleinement fonctionnelle et devrait donner au lecteur une bonne idee
   de comment doit se comporter une fonction de conversation, mais elle est
   trop simple pour une utilisation reelle. Meme si vous n'utilisez pas
   OpenPAM, N'hesitez pas `a telecharger le code source et d'adapter
   openpam_ttyconv(3) `a vos besoins, nous pensons qu'elle est
   raisonnablement aussi robuste qu'une fonction de conversation orientee tty
   peut l'etre.

 /*-
  * Copyright (c) 2002 Networks Associates Technology, Inc.
  * All rights reserved.
  *
  * This software was developed for the FreeBSD Project by ThinkSec AS and
  * Network Associates Laboratories, the Security Research Division of
  * Network Associates, Inc.  under DARPA/SPAWAR contract N66001-01-C-8035
  * ("CBOSS"), as part of the DARPA CHATS research program.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
  * are met:
  * 1. Redistributions of source code must retain the above copyright
  *    notice, this list of conditions and the following disclaimer.
  * 2. Redistributions in binary form must reproduce the above copyright
  *    notice, this list of conditions and the following disclaimer in the
  *    documentation and/or other materials provided with the distribution.
  * 3. The name of the author may not be used to endorse or promote
  *    products derived from this software without specific prior written
  *    permission.
  *
  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  *
  * $FreeBSD$
  */

 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <unistd.h>

 #include <security/pam_appl.h>

 int
 converse(int n, const struct pam_message **msg,
         struct pam_response **resp, void *data)
 {
         struct pam_response *aresp;
         char buf[PAM_MAX_RESP_SIZE];
         int i;

         data = data;
         if (n <= 0 || n > PAM_MAX_NUM_MSG)
                 return (PAM_CONV_ERR);
         if ((aresp = calloc(n, sizeof *aresp)) == NULL)
                 return (PAM_BUF_ERR);
         for (i = 0; i < n; ++i) {
                 aresp[i].resp_retcode = 0;
                 aresp[i].resp = NULL;
                 switch (msg[i]->msg_style) {
                 case PAM_PROMPT_ECHO_OFF:
                         aresp[i].resp = strdup(getpass(msg[i]->msg));
                         if (aresp[i].resp == NULL)
                                 goto fail;
                         break;
                 case PAM_PROMPT_ECHO_ON:
                         fputs(msg[i]->msg, stderr);
                         if (fgets(buf, sizeof buf, stdin) == NULL)
                                 goto fail;
                         aresp[i].resp = strdup(buf);
                         if (aresp[i].resp == NULL)
                                 goto fail;
                         break;
                 case PAM_ERROR_MSG:
                         fputs(msg[i]->msg, stderr);
                         if (strlen(msg[i]->msg) > 0 &&
                             msg[i]->msg[strlen(msg[i]->msg) - 1] != '\n')
                                 fputc('\n', stderr);
                         break;
                 case PAM_TEXT_INFO:
                         fputs(msg[i]->msg, stdout);
                         if (strlen(msg[i]->msg) > 0 &&
                             msg[i]->msg[strlen(msg[i]->msg) - 1] != '\n')
                                 fputc('\n', stdout);
                         break;
                 default:
                         goto fail;
                 }
         }
         *resp = aresp;
         return (PAM_SUCCESS);
  fail:
         for (i = 0; i < n; ++i) {
                 if (aresp[i].resp != NULL) {
                         memset(aresp[i].resp, 0, strlen(aresp[i].resp));
                         free(aresp[i].resp);
                 }
         }
         memset(aresp, 0, n * sizeof *aresp);
         *resp = NULL;
         return (PAM_CONV_ERR);
 }


Lectures complementaires

  Publications

   Rendre les services de connexion independants des technologies
   d'authentification . Vipin Samar et Charlie Lai. Sun Microsystems.

   X/Open Single Sign-on Preliminary Specification. The Open Group.
   1-85912-144-6. June 1997.

   Pluggable Authentication Modules. Andrew G. Morgan. 1999-10-06.

  Guides utilisateur

   Administration de PAM . Sun Microsystems.

  Page internet liees

   La page d'OpenPAM. Dag-Erling Smo/rgrav. ThinkSec AS.

   La page de Linux-PAM. Andrew G. Morgan.

   La page de Solaris PAM. Sun Microsystems.
