Sophie

Sophie

distrib > Mandriva > 9.0 > i586 > by-pkgid > 0d5cd12c82d627a82c59047e1ba7b8a9 > files > 1265

howto-html-fr-9.0-0.2mdk.noarch.rpm

<HTML>
<HEAD>
<TITLE>Ajouter le support shadow &agrave; un programme en C. </TITLE>
</HEAD>
<BODY>
<A NAME="sec-prog"></A> <H1>8. <A NAME="s8"></A>Ajouter le support shadow &agrave; un programme en C. </H1>
<P>
<A HREF="Shadow-Password-HOWTO.html#toc8">Contenu de cette section</A></P>

<P>Ajouter le support shadow &agrave; un programme C est assez facile. Le
seul probl&egrave;me est que le programme doit &ecirc;tre lanc&eacute; par root (ou SUID root) 
pour qu'il puisse acc&eacute;der au fichier <CODE>/etc/shadow</CODE>.</P>
<P>Ceci pr&eacute;sente un r&eacute;el probl&egrave;me, il faut faire tr&egrave;s attention lors de la
cr&eacute;ation de programmes SUID. Par exemple, il ne faut pas qu'un programme
SUID root puisse permettre un acc&egrave;s au shell.</P>
<P>La meilleure solution pour qu'un programme puisse acc&eacute;der aux mots de passe
encod&eacute;s sans &ecirc;tre SUID root, est de lancer
ce programme SUID shadow &agrave; la place. C'est le cas par exemple du programme
<CODE>xlock</CODE>.</P>
<P>Dans l'exemple donn&eacute; pr&eacute;c&eacute;demment, <CODE>pppd-1.2.1d</CODE> fonctionne d&eacute;j&agrave;
SUID root, donc ajouter le support shadow ne le rendra pas plus vuln&eacute;rable.</P>
<P></P>
<H2>8.1 <A NAME="ss8.1"></A> Les fichiers d'en-t&ecirc;te</H2>

<P>Les fichiers d'en-t&ecirc;te doivent &ecirc;tre stock&eacute;s dans le r&eacute;pertoire
<CODE>/usr/include/shadow</CODE>. Le fichier <CODE>/usr/include/shadow.h</CODE>,
doit &ecirc;tre un lien symbolique vers <CODE>/usr/include/shadow/shadow.h</CODE>.</P>
<P>Pour ajouter le support shadow &agrave; un programme, vous devez inclure les
fichiers de header:
<PRE>
#include &lt;shadow/shadow.h&gt;
#include &lt;shadow/pwauth.h&gt;
</PRE>
</P>
<P>La meilleure solution est d'utiliser des directives de compilation pour
compiler conditionnellement le code shadow (Il y aura un exemple par la
suite).</P>
<P></P>

<H2>8.2 <A NAME="ss8.2"></A> La biblioth&egrave;que libshadow.a</H2>

<P>Quand vous avez install&eacute; <EM>l'ensemble shadow</EM>, le fichier
<CODE>libshadow.a</CODE> a &eacute;t&eacute; cr&eacute;&eacute; et install&eacute; dans le r&eacute;pertoire <CODE>/usr/lib</CODE>.</P>
<P>Lorsque vous compilez un programme avec le support shadow, vous devez
pr&eacute;ciser &agrave; l'&eacute;diteur de liens d'inclure la biblioth&egrave;que <CODE>libshadow.a</CODE>
dans le lien:</P>
<P>
<BLOCKQUOTE><CODE>
<PRE>
gcc programe.c -o program -lshadow
</PRE>
</CODE></BLOCKQUOTE>
</P>
<P>Ceci dit, et vous le verrez par la suite dans notre exemple, la plupart des
programmes plus ou moins gros utilisent un fichier <CODE>Makefile</CODE>, qui en g&eacute;n&eacute;ral,
utilise une variable appel&eacute;e LIBS=... que vous pourrez modifier.</P>
<P></P>

<H2>8.3 <A NAME="ss8.3"></A> La structure shadow</H2>

<P>La biblioth&egrave;que <CODE>libshadow.a</CODE> utilise une structure appel&eacute;e 
<CODE>spwd</CODE> pour r&eacute;cup&eacute;rer les informations contenues dans le fichier
<CODE>/etc/shadow</CODE>. Voici la d&eacute;finition de la structure <CODE>spwd</CODE>
provenant de <CODE>/usr/include/shadow/shadow.h</CODE>:
<HR>
<PRE>
struct spwd
{
  char *sp_namp;         /* nom de login */
  char *sp_pwdp;         /* mot de passe encode */
  sptime sp_lstchg;      /* date de la derniere modification */
  sptime sp_min;         /* nombre de jours minimum entre les modifs */
  sptime spmax;          /* nombre de jours maximum entre les modifs*/
  sptime sp_warn;        /* nombre de jours de warning avant l'expiration
                            du mot de passe */
  sptime sp_inact;       /* nombre de jours d'utilisation du compte
                            apres l'expiration. */
  sptime sp_expire;      /* nombre de jours a partir du 01/01/70 jusqu'a
                               l'expiration du compte */
  unsigned long sp_flag; /* reserve pour une utilisation future */
};
</PRE>
<HR>
</P>
<P>L'<EM>ensemble shadow</EM> peut placer des donn&eacute;es dans le champ <CODE>sp_pwdp</CODE>
juste apr&egrave;s le mot de passe encod&eacute;, le champ password pourrait contenir:
<BLOCKQUOTE><CODE>
<PRE>
username:Npge08pfz4wuk;@/sbin/extra:9479:0:10000::::
</PRE>
</CODE></BLOCKQUOTE>
</P>
<P>Cela signifie qu'en plus du mot de passe, le programme <CODE>/sbin/extra</CODE> sera
appel&eacute; pour proc&eacute;der &agrave; une authentification suppl&eacute;mentaire. Le programme appel&eacute;
recevra comme argument, le nom d'utilisateur et un <EM>switch</EM> qui indiquera pourquoi il
est appel&eacute;. Regardez le fichier <CODE>/usr/include/shadow/pwauth.h</CODE> et le
code source de <CODE>pwauth.c</CODE> pour plus d'informations.</P>
<P>La fonction d'authentification <CODE>pwauth</CODE> est toujours utilis&eacute;e
avant la deuxi&egrave;me authentification..</P>
<P></P>

<H2>8.4 <A NAME="ss8.4"></A> Les fonctions Shadow.</H2>

<P>Le fichier <CODE>shadow.h</CODE> contient aussi la d&eacute;claration des
fonctions contenues dans la biblioth&egrave;que <CODE>libshadow.a</CODE>:
<HR>
<PRE>
extern void setspent __P ((void));
extern void endspent __P ((void));
extern struct spwd *sgetspent __P ((__const char *__string));
extern struct spwd *fgetspent __P ((FILE *__fp));
extern struct spwd *getspent __P ((void));
extern struct spwd *getspnam __P ((__const char *__name));
extern int putspent __P ((__const struct spwd *__sp, FILE *__fp));
</PRE>
<HR>
</P>
<P>La fonction que nous allons &eacute;tudier est <CODE>getspnam</CODE>, elle r&eacute;cup&egrave;re une
structure <CODE>spwd</CODE> &agrave; partir d'un nom donn&eacute;.</P>
<P></P>

<H2>8.5 <A NAME="ss8.5"></A> Exemple</H2>

<P>Voici un exemple d'ajout du support shadow &agrave; un programme qui en n&eacute;cessite
mais pour qui ce support n'existe pas par d&eacute;faut.</P>
<P>Nous allons nous baser sur l'exemple du serveur <CODE>pppd-1.2.1d</CODE> (
<EM>Serveur Point-to-Point protocol</EM>) configur&eacute; avec l'option
<EM>login</EM>:
il va chercher les mots de passe pour son authentification PAP dans le fichier
<CODE>/etc/passwd</CODE> au lieu des fichiers PAP ou CHAP. Vous n'avez pas besoin
d'ajouter ce code &agrave; <CODE>pppd-2.2.0</CODE>, c'est d&eacute;j&agrave; fait.</P>
<P>Bien que cette possibilit&eacute; de pppd ne soit pas tr&egrave;s utilis&eacute;e, 
elle ne fonctionnera plus d&egrave;s lors que vous aurez install&eacute; l'ensemble
shadow: les mots de passe ne sont plus stock&eacute;s dans <CODE>/etc/passwd</CODE>.</P>
<P>La partie du code source d'authentification des utilisateurs avec
<CODE>pppd-1.2.1d </CODE> se trouve dans le fichier
<CODE>/usr/src/pppd-1.2.1d/pppd/auth.c</CODE>.</P>
<P>Le code qui suit doit &ecirc;tre ajout&eacute; au d&eacute;but du fichier, l&agrave; o&ugrave; sont toutes les
autres directives <CODE>#include</CODE>.
<HR>
<PRE>
#ifdef HAS_SHADOW
#include &lt;shadow.h&gt;
#include &lt;shadow/pwauth.h&gt;
#endif
</PRE>
<HR>
</P>
<P>Maintenant, il faut modifier le code actuel. Nous sommes toujours avec le
fichier <CODE>auth.c</CODE>.</P>
<P>La fonction <CODE>auth.c</CODE> avant les modifications:
<HR>
<PRE>
/*
 * login - Controle le nom d'utilisateur et le mot de passe par rapport
 * a ceux stockes sur le systeme.
 * Accepte la connection si l'utilisateur est OK.
 *
 * retourne:
 *      UPAP_AUTHNAK: Connection refusee.
 *      UPAP_AUTHACK: Connection Acceptee.
 * Dans un cas comme dans l'autre, msg pointe sur le message approprie.
 */
static int
login(user, passwd, msg, msglen)
    char *user;
    char *passwd;
    char **msg;
    int *msglen;
{
    struct passwd *pw;
    char *epasswd;
    char *tty;

    if ((pw = getpwnam(user)) == NULL) {
        return (UPAP_AUTHNAK);
    }
     /*
     * XXX Si il n'y a pas de mots de passe, accepte la connection.
     */
    if (pw-&gt;pw_passwd == '\0') {
        return (UPAP_AUTHACK);
    }

    epasswd = crypt(passwd, pw-&gt;pw_passwd);
    if (strcmp(epasswd, pw-&gt;pw_passwd)) {
        return (UPAP_AUTHNAK);
    }

    syslog(LOG_INFO, &quot;user %s logged in&quot;, user);

    /*
     * Ecris une entree wtmp pour cet utilisateur.
     */
    tty = strrchr(devname, '/');
    if (tty == NULL)
        tty = devname;
    else
        tty++;
    logwtmp(tty, user, &quot;&quot;);    /* Ajoute une entree wtmp de connection */
    logged_in = TRUE;

    return (UPAP_AUTHACK);
}
</PRE>
<HR>
</P>
<P></P>
<P>Le mot de passe de l'utilisateur est plac&eacute; dans <CODE>pw->pw_passwd</CODE>,
donc, nous devons ajouter la fonction <CODE>getspnam</CODE> qui placera le mot
de passe dans <CODE>spwd->sp_pwdp</CODE>.</P>
<P>Nous rajouterons la fonction <CODE>pwauth</CODE> pour l'authentification
actuelle. Une seconde authentification sera effectu&eacute;e si le fichier
shadow est configur&eacute; pour.</P>
<P>Voici la fonction auth.c apres les modifications pour le support de shadow:
<HR>
<PRE>
/*
 * login - Controle le nom d'utilisateur et le mot de passe par rapport
 * a ceux stockes sur le systeme.
 * Accepte la connection si l'utilisateur est OK.
 *
 * Cette fonction a ete modifiee pour etre compatible avec les mots de
 * passe Shadow Linux si USE_SHADOW a ete defini
 *
 * retourne:
 *      UPAP_AUTHNAK: Connection refusee.
 *      UPAP_AUTHACK: Connection Acceptee.
 * Dans un cas comme dans l'autre, msg pointe sur le message approprie.
 */

static int
login(user, passwd, msg, msglen)
    char *user;
    char *passwd;
    char **msg;
    int *msglen;
{
    struct passwd *pw;
    char *epasswd;
    char *tty;

#ifdef USE_SHADOW
    struct spwd *spwd;
    struct spwd *getspnam();
#endif

    if ((pw = getpwnam(user)) == NULL) {
        return (UPAP_AUTHNAK);
    }

#ifdef USE_SHADOW
    if ((spwd = getspnam(user)) == NULL) {
           pw-&gt;pw_passwd = &quot;&quot;;
    } else {
    pw-&gt;pw_passwd = spwd-&gt;sp_pwdp;
    }
#endif

     /*
     *  XXX Si il n'y a pas de mots de passe, accepte la connection.
     */
    if (pw-&gt;pw_passwd == '\0') {
        return (UPAP_AUTHNAK);
    }
#ifdef HAS_SHADOW
    if ((pw-&gt;pw_passwd &amp;&amp; pw-&gt;pw_passwd[0] == '@'
         &amp;&amp; pw_auth (pw-&gt;pw_passwd+1, pw-&gt;pw_name, PW_LOGIN, NULL))
        || !valid (passwd, pw)) {
        return (UPAP_AUTHNAK);
    }
#else
    epasswd = crypt(passwd, pw-&gt;pw_passwd);
    if (strcmp(epasswd, pw-&gt;pw_passwd)) {
        return (UPAP_AUTHNAK);
    }
#endif

    syslog(LOG_INFO, &quot;user %s logged in&quot;, user);

    /*
     * Ecris une entree wtmp pour cet utilisateur.
     */
    tty = strrchr(devname, '/');
    if (tty == NULL)
        tty = devname;
    else
        tty++;
    logwtmp(tty, user, &quot;&quot;);     /* Ajoute une entree wtmp de connection  */
    logged_in = TRUE;

    return (UPAP_AUTHACK);
}
</PRE>
<HR>
</P>
<P>En examinant pr&eacute;cis&eacute;ment le code, vous verrez que d'autres modifications ont
&eacute;t&eacute; effectu&eacute;es. La version originale autorisait l'acc&egrave;s (en retournant 
<CODE>UPAP_AUTHACK</CODE>) quand il n'y avait
pas de mots de passe dans le fichier <CODE>passwd</CODE>. Il ne fallait
<EM>pas</EM> laisser ceci car utilis&eacute; avec l'option <CODE>login</CODE>, pppd
utilise le nom d'utilisateur dans <CODE>/etc/passwd</CODE> et le mot de passe dans
<CODE>/etc/shadow</CODE> pour son authentification PAP.</P>
<P>Donc si nous avions gard&eacute; la version originale, n'importe qui aurait pu
&eacute;tablir une connexion ppp avec un mot de passe vide.</P>
<P>Nous avons arrang&eacute; &ccedil;a en retournant <CODE>UPAP_AUTHNAK</CODE> &agrave; la place de
<CODE>UPAP_AUTHACK</CODE> dans le cap ou le champ mot de passe est vide.</P>
<P>A savoir que pppd-2.2.0 poss&egrave;de le m&ecirc;me probl&egrave;me.</P>
<P>Nous devons modifier le Makefile pour que deux choses soient
prises en compte:
<CODE>USE_SHADOW</CODE> doit &ecirc;tre d&eacute;fini, et <CODE>libshadow.a</CODE> doit &ecirc;tre ajout&eacute;
au processus d'&eacute;dition de liens.</P>
<P>Editez le Makefile, et ajoutez:
<BLOCKQUOTE><CODE>
<PRE>
LIBS = -shadow
</PRE>
</CODE></BLOCKQUOTE>
</P>
<P>Alors, trouvez la ligne:
<BLOCKQUOTE><CODE>
<PRE>
COMPILE_FLAGS = -I.. -D_linux_=1 -DGIDSET_TYPE=gid_t
</PRE>
</CODE></BLOCKQUOTE>
</P>
<P>et replacez-la par:
<BLOCKQUOTE><CODE>
<PRE>
COMPILE_FLAGS = -I.. -D_linux_=1 -DGIDSET_TYPE=gid_t -DUSE_SHADOW
</PRE>
</CODE></BLOCKQUOTE>
</P>
<P>Maintenant, lancez make et installez.</P>
<P></P>

<HR>
<P>
Chapitre <A HREF="Shadow-Password-HOWTO-9.html">suivant</A>,
Chapitre <A HREF="Shadow-Password-HOWTO-7.html">Pr&eacute;c&eacute;dent</A>
<P>
Table des mati&egrave;res de <A HREF="Shadow-Password-HOWTO.html#toc8">ce chapitre</A>,
 <A HREF="Shadow-Password-HOWTO.html#toc">Table des mati&egrave;res</A> g&eacute;n&eacute;rale</P>
<P>
<A HREF="Shadow-Password-HOWTO.html">D&eacute;but</A> du document,
 <A HREF="#0"> D&eacute;but de ce chapitre</A></P>
</BODY>
</HTML>