Sophie

Sophie

distrib > Mandriva > 9.1 > i586 > by-pkgid > f1098342ec4a2b28475e34123ce17201 > files > 1125

howto-html-it-9.1-0.5mdk.noarch.rpm

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
<HTML>
<HEAD>
<TITLE>Linux Shadow Password HOWTO: Aggiungere il supporto shadow ad un programma C</TITLE>
<LINK HREF="Shadow-Password-HOWTO-9.html" REL=next>
<LINK HREF="Shadow-Password-HOWTO-7.html" REL=previous>
<LINK HREF="Shadow-Password-HOWTO.html#toc8" REL=contents>
</HEAD>
<BODY>
<A HREF="Shadow-Password-HOWTO-9.html">Avanti</A>
<A HREF="Shadow-Password-HOWTO-7.html">Indietro</A>
<A HREF="Shadow-Password-HOWTO.html#toc8">Indice</A>
<HR>
<H2><A NAME="sez-agg"></A> <A NAME="s8">8. Aggiungere il supporto shadow ad un programma C</A></H2>

<P>Aggiungere il supporto shadow ad un programma &egrave; in realt&agrave; abbastanza semplice. L'unico problema &egrave; che il programma deve essere eseguito da root (o SUID root) in modo che il programma possa accedere al file <CODE>/etc/shadow</CODE>.
<P>Questo presenta un grande problema: occorre seguire una condotta di programmazione molto attenta quando si creano programmi SUID. Per esempio, se un programma ha un comando che invoca una shell, questa non deve essere eseguita con i diritti di root anche se il programma &egrave; SUID root.
<P>Per aggiungere il supporto shadow ad un programma in modo che possa controllare le password, ma per il resto non necessita di essere eseguito da root, &egrave; molto pi&ugrave; sicuro eseguire il programma SGID shadow. Il programma <CODE>xlock</CODE> ne &egrave; un esempio.
<P>Nell'esempio fatto prima, <CODE>pppd-1.2.1d</CODE> gi&agrave; viene eseguito SUID root, perci&ograve; aggiungere il supporto shadow non dovrebbe rendere il programma pi&ugrave; vulnerabile.
<P>
<H2><A NAME="ss8.1">8.1 File di intestazione (header)</A>
</H2>

<P>I file di intestazione (header) dovrebbero stare in <CODE>/usr/include/shadow</CODE>. Ci dovrebbe anche essere un <CODE>/usr/include/shadow.h</CODE>, ma sarebbe un link simbolico a <CODE>/usr/include/shadow/shadow.h</CODE>.
<P>Per aggiungere il supporto shadow ad un programma, dovete includere i file di intestazione:
<PRE>
#include &lt;shadow/shadow.h>
#include &lt;shadow/pwauth.h>
</PRE>
<P>Potrebbe essere una buona idea usare le direttive del compilatore in modo da condizionare la compilazione del codice shadow (io lo faccio nell'esempio che segue).
<P>
<H2><A NAME="ss8.2">8.2 La libreria libshadow.a</A>
</H2>

<P>Quando avete installato la <EM>Shadow Suite</EM> il file <CODE>libshadow.a</CODE> &egrave; stato creato ed installato in <CODE>/usr/lib</CODE>.
<P>Quando si compila il supporto shadow in un programma, bisogna dire al linker di includere la libreria <CODE>libshadow.a</CODE>.
<P>Questo viene fatto da:
<BLOCKQUOTE><CODE>
<PRE>
gcc program.c -o program -lshadow
</PRE>
</CODE></BLOCKQUOTE>
<P>Comunque, come vedremo nell'esempio che segue, la maggior parte dei programmi di grandi dimensioni usa un <CODE>Makefile</CODE>, che di solito ha una variabile chiamata <CODE>LIBS=...</CODE> che noi modificheremo.
<P>
<H2><A NAME="ss8.3">8.3 La struttura Shadow</A>
</H2>

<P>La libreria <CODE>libshadow.a</CODE> usa una struttura chiamata  <CODE>spwd</CODE> per le informazioni che preleva dal file <CODE>/etc/shadow</CODE>. Questa &egrave; la definizione della struttura <CODE>spwd</CODE> dal file di intestazione <CODE>/usr/include/shadow/shadow.h</CODE>:
<HR>
<PRE>
struct spwd
{
  char *sp_namp;                /* nome di login  */
  char *sp_pwdp;                /* password codificata */
  sptime sp_lstchg;             /* data dell'ultimo cambiamento */
  sptime sp_min;                /* minimo numero di giorni tra cambiamenti */
  sptime sp_max;                /* massimo numero di giorni tra cambiamenti */
  sptime sp_warn;               /* numero di giorni di avvertimento prima
                                   che scada la password */
  sptime sp_inact;              /* numero di giorni dopo la scadenza della
                                   password prima che l'account venga
                                   disabilitato */ 
  sptime sp_expire;             /* giorni dal 1/1/70 fino alla scadenza 
                                   dell'account */ 
  unsigned long sp_flag;        /* riservato per uso futuro */
};
</PRE>
<HR>
<P>La <EM>Shadow Suite</EM> pu&ograve; mettere altre cose nel campo <CODE>sp_pwdp</CODE> proprio a fianco della password codificata. Il campo della password potrebbe contenere:
<BLOCKQUOTE><CODE>
<PRE>
nomeutente:Npge08pfz4wuk;@/sbin/extra:9479:0:10000::::
</PRE>
</CODE></BLOCKQUOTE>
<P>Questo significa che, oltre alla password, dovrebbe essere chiamato il programma <CODE>/sbin/extra</CODE> per ulteriori autenticazioni. Il programma chiamato ricever&agrave; il nome utente e un'opzione che indica perch&eacute; viene chiamato. Vedere il file <CODE>/usr/include/shadow/pwauth.h</CODE> e il codice sorgente di <CODE>pwauth.c</CODE> per ulteriori informazioni.
<P>Ci&ograve; che voglio dire &egrave; che dovremmo usare la funzione <CODE>pwauth</CODE> per eseguire la vera autenticazione, dato che si occuper&agrave; anche dell'autenticazione secondaria. L'esempio sotto fa proprio questo.
<P>L'autore della <EM>Shadow Suite</EM> fa presente che poich&eacute; molti dei programmi esistenti non la usano potrebbe essere rimossa o cambiata dalle future versioni della <EM>Shadow Suite</EM>.
<P>
<H2><A NAME="ss8.4">8.4 Funzioni Shadow </A>
</H2>

<P>Il file <CODE>shadow.h</CODE> contiene anche i prototipi delle funzioni contenute nella libreria <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>La funzione che useremo nell'esempio &egrave;: <CODE>getspnam</CODE> che ritorna una struttura <CODE>spwd</CODE> per il nome passato per argomento.
<P>
<H2><A NAME="ss8.5">8.5 Esempio</A>
</H2>

<P>Questo &egrave; un esempio di aggiunta del supporto shadow ad un programma che ne ha bisogno, ma non lo possiede.
<P>Questo esempio usa il <EM>Point-to-Point Protocol Server</EM> (pppd-1.2.1d), che ha una modalit&agrave; in cui esegue l'autenticazione <EM>PAP</EM> usando i nomi e le password degli utenti dal file <CODE>/etc/passwd</CODE> anzich&eacute; dai file <EM>PAP</EM> o <EM>CHAP</EM>.  Non dovreste aver bisogno di aggiungere questo codice a <CODE>pppd-2.2.0</CODE> perch&eacute; c'&egrave; gi&agrave;.
<P>Questa caratteristica del pppd probabilmente non &egrave; molto usata, ma se avete installato la <EM>Shadow Suite</EM>, non funzioner&agrave; comunque perch&eacute; le password non si trovano pi&ugrave; in <CODE>/etc/passwd</CODE>.
<P>Il codice per l'autenticazione degli utenti sotto <CODE>pppd-1.2.1d</CODE> si trova nel file <CODE>/usr/src/pppd-1.2.1d/pppd/auth.c</CODE>.
<P>Il seguente codice deve essere aggiunto all'inizio del file dove si trovano tutte le altre direttive <CODE>#include</CODE>. Abbiamo racchiuso gli <CODE>#include</CODE> tra direttive condizionali (i.e. vengono presi in considerazione solo se stiamo compilando per il supporto shadow).
<P>
<HR>
<PRE>
#ifdef HAS_SHADOW
#include &lt;shadow.h>
#include &lt;shadow/pwauth.h>
#endif
</PRE>
<HR>
 
<P>Il passo successivo consiste nel modificare il codice vero e proprio. Stiamo ancora apportando cambiamenti al file <CODE>auth.c</CODE>.
<P>Funzione <CODE>auth.c</CODE> prima delle modifiche:
<HR>
<PRE>
/*
 * login - Controlla il nome e la password dell'utente nel database delle
 * password di sistema, e permette il login se l'utente &egrave; OK.
 *
 * restituisce:
 *      UPAP_AUTHNAK: Login fallito.
 *      UPAP_AUTHACK: Login riuscito.
 * In entrambi i casi, msg punta al messaggio appropriato.
 */
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 Se non c'&egrave; nessuna password, li lascia collegare senza.
     */
    if (pw->pw_passwd == '\0') {
        return (UPAP_AUTHACK);
    }

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

    syslog(LOG_INFO, "user %s logged in", user);

    /*
     * Scrive una voce wtmp per questo utente.
     */
    tty = strrchr(devname, '/');
    if (tty == NULL)
        tty = devname;
    else
        tty++;
    logwtmp(tty, user, "");         /* Aggiunge una voce di login al wtmp */
    logged_in = TRUE;

    return (UPAP_AUTHACK);
}
</PRE>
<HR>
<P>La password dell'utente viene messa in <CODE>pw->pw_passwd</CODE>, cos&igrave; tutto quello che dobbiamo fare in realt&agrave; &egrave; aggiungere la funzione <CODE>getspnam</CODE>. Questa metter&agrave; la password in <CODE>spwd->sp_pwdp</CODE>.
<P>Aggiungeremo la funzione <CODE>pwauth</CODE> per eseguire l'autenticazione vera e propria. 
Questa eseguir&agrave; automaticamente l'autenticazione secondaria se il file shadow &egrave; impostato per farlo.
<P>Funzione <CODE>auth.c</CODE> dopo le modifiche per il supporto shadow:
<HR>
<PRE>
/*
 * login - Controlla il nome e la password dell'utente nel database delle
 * password di sistema, e permette il login se l'utente &egrave; OK.
 *
 * Questa funzione &egrave; stata modificata in modo da supportare la
 * Linux Shadow Password Suite se USE_SHADOW &egrave; definito.
 *
 * restituisce:
 *      UPAP_AUTHNAK: Login fallito.
 *      UPAP_AUTHACK: Login riuscito.
 * In entrambi i casi, msg punta al messaggio appropriato.
 */
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
        spwd = getspnam(user);
        if (spwd)
                pw->pw_passwd = spwd->sp-pwdp;
#endif
 
     /*
     * XXX Se non c'&egrave; nessuna password, NON li lascia collegare senza.
     */
    if (pw->pw_passwd == '\0') {
        return (UPAP_AUTHNAK);
    }
#ifdef HAS_SHADOW
    if ((pw->pw_passwd &amp;&amp; pw->pw_passwd[0] == '@'
         &amp;&amp; pw_auth (pw->pw_passwd+1, pw->pw_name, PW_LOGIN, NULL))
        || !valid (passwd, pw)) {
        return (UPAP_AUTHNAK);
    }
#else
    epasswd = crypt(passwd, pw->pw_passwd);
    if (strcmp(epasswd, pw->pw_passwd)) {
        return (UPAP_AUTHNAK);
    }
#endif

    syslog(LOG_INFO, "user %s logged in", user);

    /*
     * Scrive una voce wtmp per questo utente.
     */
    tty = strrchr(devname, '/');
    if (tty == NULL)
        tty = devname;
    else
        tty++;
    logwtmp(tty, user, "");        /* Aggiunge una voce di login al wtmp */
    logged_in = TRUE;

    return (UPAP_AUTHACK);
}
</PRE>
<HR>
<P>Un attento esame riveler&agrave; che abbiamo fatto un'altra modifica. La versione originale permetteva l'accesso (restituiva <CODE>UPAP_AUTHACK</CODE>) se non c'era NESSUNA password nel file <CODE>/etc/passwd</CODE>. Questo <EM>non</EM> &egrave; una buona cosa, perch&eacute; un uso comune di questa caratteristica di login &egrave; quello di usare un account che permetta l'accesso al processo ppp e quindi confrontare il nome utente e la password forniti da PAP con il nome utente nel file <CODE>/etc/passwd</CODE> e la password nel file <CODE>/etc/shadow</CODE>.
<P>Perci&ograve; se abbiamo impostato la versione originale in modo da eseguire, al posto della shell per un utente, ad esempio <CODE>ppp</CODE>, allora chiunque potrebbe ottenere una connessione ppp impostando la sua PAP con utente <CODE>ppp</CODE> e senza password.
<P>Abbiamo risolto questo anche restituendo <CODE>UPAP_AUTHNAK</CODE> invece che <CODE>UPAP_AUTHACK</CODE> nel caso in cui il campo password fosse vuoto.
<P>&Egrave; abbastanza interessante il fatto che <CODE>pppd-2.2.0</CODE> abbia lo stesso problema.
<P>Poi abbiamo bisogno di modificare il Makefile in modo che avvengano due cose:
<CODE>USE_SHADOW</CODE> deve essere definita, e <CODE>libshadow.a</CODE> deve essere aggiunta al processo di link.
<P>Editate il Makefile, e aggiungete:
<BLOCKQUOTE><CODE>
<PRE>
LIBS = -lshadow
</PRE>
</CODE></BLOCKQUOTE>
<P>Quindi troviamo la riga:
<BLOCKQUOTE><CODE>
<PRE>
COMPILE_FLAGS = -I.. -D_linux_=1 -DGIDSET_TYPE=gid_t
</PRE>
</CODE></BLOCKQUOTE>
<P>E la cambiamo in:
<BLOCKQUOTE><CODE>
<PRE>
COMPILE_FLAGS = -I.. -D_linux_=1 -DGIDSET_TYPE=gid_t -DUSE_SHADOW
</PRE>
</CODE></BLOCKQUOTE>
<P>Ora eseguite il make ed installate.
<P>
<HR>
<A HREF="Shadow-Password-HOWTO-9.html">Avanti</A>
<A HREF="Shadow-Password-HOWTO-7.html">Indietro</A>
<A HREF="Shadow-Password-HOWTO.html#toc8">Indice</A>
</BODY>
</HTML>