Guida a Linux-PAM per il progettista di applicazioni

 

1 Introduzione 

 

Le applicazioni che desiderano utilizzare i servizi forniti da Linux-PAM devono includere nel codice sorgente C la linea:

#include <security/pam_appl.h>

 

La linea di compilazione sarà invece la seguente:

cc –o applicazione ….. –lpam –ldl

 

In aggiunta a libpam, c’è un’altra  libreria di funzioni che rendono più semplice scrivere un’applicazione PAM-aware:

#include <security/pam_misc.h>

 

La linea di compilazione sarà la seguente:

cc –o applicazione –lpam  –lpam_misc –ldl

 

Lo scopo del progetto Linux-PAM è di alleggerire lo sviluppo di software che garantisce privilegi dallo sviluppo di schemi di autenticazione sicuri ed appropriati. Ciò viene ottenuto fornendo una libreria di funzioni, che un’applicazione può utilizzare per tutte le forme di gestione di autenticazione dell’utente. La libreria  carica dinamicamente i moduli di autenticazione configurati, i quali effettivamente realizzano i task di autenticazione.

Dal punto di vista del progettista di applicazioni, l’informazione contenuta nella configurazione locale della libreria PAM non è rilevante. Ciò che si richiede è che un’applicazione tratti le funzioni seguenti come “scatole nere” che si occuperanno dei vari aspetti per l’autenticazione degli utenti, che includono la verifica dell’utente, la gestione dell’account, la inizializzazione/terminazione della sessione ed anche l’aggiornamento della password.

 

2 Panoramica Generale

 

Il processo di autenticazione è realizzato dalla libreria PAM tramite una chiamata a pam_authenticate(). Il valore di ritorno di questa funzione indicherà se un client (l’utente) è stato  autenticato. Se PAM avrà bisogno di fare il prompt all’utente per qualche informazione, come il nome o la password, allora lo farà. Se la libreria PAM è configurata per autenticare l’utente utilizzando un  protocollo silenzioso, farà anche questo. E’ importante notare che l’applicazione deve lasciare tutte le decisioni su quando fare il prompt all’utente alla libreria PAM.

PAM, comunque, deve lavorare ugualmente bene per diversi tipi di applicazioni. Alcune applicazioni, come login e passwd , sono “terminal based”; in questo caso lo scambio di informazioni con l’utente sono messaggi di testo in chiaro. Applicazioni grafiche, invece, hanno un’interfaccia più sofisticata. Di solito, esse interagiscono con l’utente tramite una finestra di dialogo . In aggiunta, i servizi di rete richiedono che i messaggi di testo scambiati con il client siano formattati in un determinato modo: un tale esempio è ftpd che prefissa ogni messaggio scambiato con un identificatore numerico.

La presentazione di semplici richieste al client è strettamente dipendente dal protocollo che l’applicazione server utilizza. A dispetto della esigenza di PAM di guidare l’intero processo di autenticazione, non è possibile lasciare tali sottigliezze di protocollo alla libreria PAM. Per superare questo problema, l’applicazione fornisce alla libreria PAM una funzione di conversazione. Questa funzione è chiamata dall’interno della libreria PAM e l’abilita ad interagire direttamente con il client. Ciò che questa funzione di conversazione deve essere in grado di fare è di ottenere input  dall’utente e di fare il prompt all’utente.

Oltre all’autenticazione, PAM fornisce altre forme di gestione. La gestione della sessione è fornita con le chiamate pam_open_session() e pam_close_session().Ciò che queste funzioni realmente fanno è responsabilità dell’amministratore locale, ma, di solito, esse vengono utilizzate per fare il log delle entrate e delle uscite dal sistema o per montare/smontare l’home directory dell’utente.

 

La gestione dell’account è fornita con la chiamata a pam_acct_mgmt(). Tale chiamata effettuerà controlli sulla consistenza dell’account dell’utente. Una delle cose che questa funzione può controllare è se il token di autenticazione dell’utente è scaduto.

 

PAM è anche in grado di modificare e cancellare le credenziali degli utenti con una chiamata a pam_setcred(). Questa funzione dovrebbe essere sempre chiamata dopo che l’utente è stato autenticato e prima che gli venga fornito il servizio. Per convenzione, questa dovrebbe essere l’ultima chiamata alla libreria PAM prima di dare il servizio all’utente.

 

3 L’interfaccia pubblica di Linux-PAM    

 

Il file di include rilevante per la libreria Linux-PAM è <security/pam_appl.h>, che contiene le definizioni della maggior parte delle funzioni.

 

3.1 Le funzioni della libreria Linux-Pam fornite all’applicazione

 

Le seguenti sono quelle funzioni della libreria Linux-Pam che possono essere chiamate dall’applicazione.

 

3.1.1 Inizializzazione di Linux-PAM

 

extern int pam_start(const char *service_name, const char *user,

                     const struct pam_conv *pam_conversation,

                     pam_handle_t **pamh);

 

Questa è la prima funzione di Linux-PAM che un’applicazione deve chiamare. Essa inizializza l’interfaccia e legge il file di configurazione /etc/pam.conf. In seguito a un successo (PAM_SUCCESS) il contenuto di *pamh  è un handle che fornisce continuità per le chiamate successive alla libreria. Gli argomenti attesi da pam_start sono il service_name del programma, l’username  dell’individuo da autenticare, un puntatore alla struttura pam_conv e un puntatore a un puntatore a pam_handle_t.

 

La struttura pam_conv è considerata in dettaglio nella sezione seguente. La struttura pam_handle_t può essere modificata tramite le funzioni pam_set_item() e pam_get_item().

 

3.1.2 Terminazione della libreria

 

extern int pam_end(pam_handle_t *pamh, int pam_status);

 

Questa è l’ultima funzione di Linux-PAM che un’applicazione deve chiamare. Dopo la chiamata l’handle pamh e tutta la memoria associata con esso non sono più validi.

In condizioni normali pam_status ha valore PAM_SUCCESS, ma se si verifica un insuccesso di un’applicazione di servizio, dovrebbe essere utilizzato il codice di errore appropriato.

 

3.1.3 Modifica dei Pam item

 

extern int pam_set_item(pam_handle_t *pamh, int item_type, const void *item)  

 

Questa funzione è utilizzata per modificare il valore dei seguenti item_type:

 

PAM_SERVICE: Il nome del servizio.

PAM_USER: Il nome dell’utente.

PAM_TTY: Il nome del terminale.

PAM_RHOST: Nome dell’host remoto.

PAM_CONV: La struttura di conversazione.

PAM_RUSER: Il nome dell’utente remoto.

PAM_USER_PROMPT: La stringa usata quando si richiede il nome all’utente. Il valore di default è “ Please enter username: “.

 

Per tutti gli item_type, tranne PAM_CONV, item è un puntatore a una stringa di caratteri che termina con un valore <NUL>

Per PAM_CONV, item punta a una struttura di tipo pam_conv inizializzata.

 

Una chiamata con successo restituisce PAM_SUCCESS.  Eventuali errori restituiti sono i seguenti:

PAM_PERM_DENIED: E’ stato fatto un tentativo di sostituire la struttura di converszione con un valore NULL.

PAM_BUFF_ERROR: La memoria a disposizione si è esaurita facendo una copia dell’item.

PAM_BAD_ITEM: L’applicazione ha tentato di modificare un item non definito.

 

3.1.4 Ottenere Pam item

 

extern int pam_get_item(const pam_handle_t *pamh, int item_type,

                        const void **item);

 

Questa funzione è utilizzata per ottenere il valore dell’item_type indicato. In caso di successo, *item contiene un puntatore al valore dell’item corrispondente. Si noti che esso è un puntatore ai dati reali e quindi la memoria ad esso associata non deve essere rilasciata o sovrascritta. Una chiamata che termina con successo restituisce PAM_SUCCESS. Se si tenta di ottenere un item non definito, viene restituito PAM_BAD_ITEM.

 

3.1.5 Comprendere gli errori

 

extern const char *pam_strerror(pam_handle_t *pamh, int errnum);

 

Questa funzione restituisce del testo che descrive l’errore associato con l’argomento errnum. Se l’errore non è riconosciuto, viene restituita la stringa “unknown Linux-PAM error”.

 

3.1.6 Progettare i ritardi

 

extern int pam_fail_delay(pam_handle_t *pamh, unsigned int micro_sec);

 

Questa funzione è offerta da Linux-PAM per generare ritardi di tempo in seguito a una chiamata a pam_authenticate() fallita e prima che il controllo venga restituito all’applicazione.

Generalmente, un’applicazione richiede che l’utente venga autenticato da Linux-PAM tramite una chiamata a pam_authenticate() o a pam_chauthtok(). Queste funzioni chiamano ognuno dei moduli di autenticazione nello stack elencati nel file di configurazione Linux-PAM rilevante per l’applicazione. Uno o più moduli potrebbero fallire. E’ desiderabile a questo punto che ci sia una pausa prima che l’applicazione continui. La ragione principale di questo ritardo è la sicurezza: un ritardo scoraggia gli attacchi di forza bruta.

La funzione pam_fail_delay() fornisce un meccanismo tramite cui un’applicazione o un modulo può suggerire un ritardo minimo (micro-sec).

 

3.1.7 Autenticazione dell’utente

 

extern int pam_authenticate(pam_handle_t *pamh, int flags);

 

Questa funzione fa da interfaccia ai meccanismi di autenticazione dei moduli caricati. Il campo flag, che è opzionale e può essere in OR con PAM_SILENT, può avere il seguente valore:

 

PAM_DISALLOW_NULL_AUTHTOK: Questo valore fa si che i moduli di autenticazione restituiscano.

PAM_AUTH_ERR se l’utente non ha un token di autorizzazione registrato.

 

Il valore restituito dalla funzione è uno dei seguenti:

 

PAM_AUTH_ERR: L’utente non è stato autenticato.

PAM_CRED_INSUFFICENT: Per qualche ragione l’applicazione non ha sufficienti credenziali per autenticare l’utente.

PAM_AUTHINFO_UNAVAIL: I moduli non sono stati in grado di accedere all’informazione per l’autenticazione. Ciò potrebbe essere a causa di fallimenti hardware o rete etc.

PAM_USER_UNKNOWN: Il nome utente fornito non è stato riconosciuto dal servizio di autenticazione.

PAM_MAXTRIES: Uno o piu moduli di autenticazione hanno raggiunto il limite di tentativi di autenticazione dell’utente.

 

Se uno o più dei moduli di autenticazione dovesse fallire il caricamento, per qualunque ragione, la funzione restituirà PAM_ABORT.

 

3.1.8 Modifica delle credenziali dell’utente

 

extern int pam_setcred(pam_handle_t *pamh, int flags);

 

Questa funzione è utilizzata per modificare le credenziali dell’utente specifiche del modulo. E’ solitamente chiamata dopo che l’utente è stato autenticato, dopo aver chiamato la funzione di account management e dopo che la sessione è stata aperta dall’utente.

Una credenziale è qualcosa che l’utente possiede. E’ qualche proprietà, come un ticket di Kerberos. L’UID e GID  di un sistema Linux sono credenziali.

Questa funzione semplicemente chiama le funzioni  pam_sm_setcred() di ognuno dei moduli caricati. I flag validi che possono essere messi ognuno in OR con PAM_SILENT, sono:

 

PAM_ESTABLISH_CRED: Modifica le credenziali per il servizio di autenticazione.

PAM_DELETE_CRED: Cancella le credenziali associate col servizio di autenticazione.

PAM_REINITIALIZE_CRED: Reinizializza le credenziali dell’utente.

PAM_REFRESH_CRED: Estende il tempo di vita delle credenziali dell’utente.

 

In caso di successo il valore restituito è PAM_SUCCESS. In caso di errore viene restituito uno dei seguenti valori:

 

PAM_CRED_UNAVAIL: Un modulo non può recuperare le credenziali dell’utente.

PAM_CRED_EXPIRED: Le credenziali dell’utente sono scadute.

PAM_USER_UNKNOWN: L’utente non è noto al modulo di autenticazione.

PAM_CRED_ERR: Un modulo non è stato in grado di modificare le credenziali dell’utente.

 

3.1.9 Gestione dell’account

 

extern int pam_acct_mgmt(pam_handle_t *pamh, int flags);

 

Questa funzione è chiamata dopo che l’utente è stato autenticato e stabilisce se la sua l’account è abilitata, ovvero se è ancora attiva e se all’utente è permesso di accedere al sistema ad un dato tempo. I flag validi, ognuno dei quali può  essere in OR con PAM_SILENT, sono gli stessi di quelli applicabili all’argomento flag di pam_authenticate(). La funzione semplicemente chiama le funzioni corrispondenti ad ognuno dei moduli caricati, come determinato dal file di configurazione /etc/pam.conf

In caso di successo la funzione restituisce PAM_SUCCESS. Altrimenti uno dei seguenti codici di errore viene restituito.

 

PAM_AUTHTOKEN_REQD: L’utente è valido ma il suo token di autenticazione è scaduto. Il corretto comportamento dopo la ricezione di questo valore di ritorno è di richiedere che l’utente soddisfi la funzione pam_chauthtok() prima di ottenere il servizio.

PAM_ACCT_EXPIRED: All’utente non è più permesso di accedere al sistema.

PAM_AUTH_ERR: C’è stato un errore di autenticazione.

PAM_ERR_DENIED: All’utente non è permesso di accedere in questo momento.

PAM_USER_UNKNOWN: L’utente non è noto alla componente del modulo di gestione dell’account.

 

3.1.10 Modifica dei token di autenticazione

 

extern int pam_chauthtok(pam_handle_t *pamh, const int flags);

 

Questa funzione è utilizzata per modificare il token di autenticazione di un utente.  Il flags opzionale valido che può essere in OR con PAM_SILENT  è:

 

PAM_CHANGE_EXPIRED_AUTHTOK: questo argomento indica ai moduli che il token di autenticazione dell’utente deve essere modificato solo se scaduto.

PAM_SUCCESS è il solo valore di ritorno che indica successo.

 

Valori di errore validi che possono essere restituiti sono:

PAM_AUTHTOK_ERR: Un modulo non è stato in grado di ottenere il nuovo token di autenticazione.

PAM_AUTHTOK_RECOVERY_ERR: Un modulo non è stato in grado di ottenere il vecchio token di autenticazione.

PAM_AUTHTOK_LOCK_BUSY: Uno o più dei moduli non sono stati in grado di modificare il token di autenticazione poiché era bloccato.

PAM_AUTHTOK_DISABLE_AGING: L’ ”AGING” del token di autenticazione è stato disabilitato per almeno uno dei moduli.

PAM_PERM_DENIED: Permesso negato.

PAM_TRY_AGAIN: Non tutti i moduli erano in grado di modificare il token di autenticazione. In tale caso nessuno dei token di autenticazione dell’utente viene modificato.

PAM_USER_UNKNOWN: L’utente non è noto al servizio di modifica del token di autenticazione .

 

3.1.11 Inizializzazione e terminazione  della sessione

 

extern int pam_open_session(pam_handle_t *pamh, int flags);

extern int pam_close_session(pam_handle_t *pamh, int flags);

 

Questa funzione è utilizzata per indicare che una sessione di autenticazione è iniziata/terminata e per informare il modulo che l’utente sta entrando/uscendo dalla sessione. Dovrebbe essere possibile per Linux-PAM aprire una sessione e chiudere la stessa da applicazioni differenti.

Attualmente, questa funzione chiama semplicemente ognuna delle corrispondenti funzioni dei moduli caricati. L’unico flag valido è PAM_SILENT ed è, ovviamente, opzionale.

Se uno qualsiasi dei moduli required caricati non è in grado di aprire/chiudere la sessione, la funzione restituisce PAM_SESSION_ERR.

 

3.1.12 Modifica delle variabili d’ambiente

 

extern int pam_putenv(pam_handle_t *pamh, const char *name_value);

 

Questa funzione tenta di (re)settare una variabile d’ambiente Linux-PAM. L’argomento name_value è una singola stringa terminata da NUL che assume uno dei seguenti valori:

 

NAME=valore della variabile” : In questo caso la variabile d’ambiente NAME è settata a “valore della variabile”. Se questa variabile esiste, viene sovrascritta. Altrimenti è aggiunta all’enviroment Linux-PAM.

NAME=” : La funzione imposta la variabile a un valore vuoto.

NAME : La funzione cancella la variabile corrispondente dell’enviroment Linux-PAM.

 

In caso di successo la funzione restituisce PAM_SUCCESS. In caso di errore è restituito uno dei seguenti valori:

 

PAM_PERM_DENIED: Il nome dato è un puntatore NULL.

PAM_BAD_ITEM: La variabile da cancellare non esiste.

PAM_ABORT: L’handle Linux-PAM, pamh, è corrotto.

PAM_BUF_ERR: E’ fallita l’allocazione della memoria durante l’aggiornamento.

 

3.1.13 Ottenere una variabile d’ambiente

 

extern const char *pam_getenv(pam_handle_t *pamh, const char *name);

 

Ottiene il valore della variabile ambiente indicata da name. In caso di errore la funzione restituisce NULL.

 

3.1.14 Ottenere l’insieme delle variabili d’ambiente

 

extern const char * const *pam_getenvlist(pam_handle_t *pamh);

 

Questa funzione restituisce un puntatore a tutto l’enviroment Linux-PAM. Esso è un puntatore a una lista a sola lettura di variabili d’ambiente.

 

3.2 La funzione di conversazione

 

Un’applicazione deve fornire una “funzione di conversazione”. Essa è utilizzata per la comunicazione diretta tra un modulo caricato e un’applicazione e deve fornire al modulo un mezzo per richiedere la password ecc. La struttura, pam_conv, è definita includendo <securiry/pam_appl.h>:

 

struct pam_conv {

    int (*conv)(int num_msg,

        const struct pam_message **msg,

        struct pam_response **resp,

        void *appdata_ptr);

    void *appdata_ptr;

};

 

La struttura è inizializzata dall’applicazione prima di essere passata alla libreria. Il suo contenuto è attaccato all’handle *pamh. La finalità di questo argomento è di fornire ad ogni modulo caricato un meccanismo per interagire direttamente con l’applicazione. Questo è il motivo per cui essa è detta struttura di conversazione.

Quando un modulo chiama la funzione conv(), all’argomento *appdata_ptr è assegnato il valore del secondo elemento di questa struttura.

Gli altri argomenti di una chiamata alla funzione conv() riguardano l’informazione scambiata tra il modulo e l’applicazione.  num_msg contiene la lunghezza dell’array di puntatori msg. In caso di successo, il puntatore *resp punta ad un array di strutture pam_response che contengono il testo fornito all’applicazione. Si noti che *resp è un array di strutture pam_response e non un array di puntatori.

La struttura per lo scambio di  messaggi, dal modulo all’applicazione, è definita da <security/pam_appl.h> :

 

struct pam_message {

    int msg_style;

    const char *msg;

};

 

Scelte valide per il campo msg_style sono:

PAM_PROMPT_ECHO_OFF: Ottiene una stringa senza visualizzare il testo.

PAM_PROMPT_ECHO_ON: Ottiene una stringa visualizzando il testo.

PAM_ERROR_MSG: Visualizza un errore.

PAM_TEXT_INFO: Visualizza testo.

 

La finalità di avere un array di messaggi è di rendere possibile passare diverse cose all’applicazione in  una singola chiamata da parte del  modulo.

La struttura per lo scambio di  messaggi, dall’applicazione al modulo è definita includendo <security/pam_appl.h> :

 

 struct pam_response {

    char *resp;

    int resp_retcode;

};

Attualmente, non ci sono definizioni per i valori resp_retcode; il valore normale è zero.
Nelle versioni più attuali di Linux-PAM il numero di risposte è sempre uguale al valore dell’argomento num_msg della funzione di conversazione. In
questo caso l’indice delle risposte corrisponde direttamente all’indice  nell’array pam_message.
La massima lunghezza delle stringhe di caratteri  pam_msg.msg e pam_response.resp è PAM_MAX_MSG_SIZE.
Il valore di ritorno atteso da questa funzione è PAM_SUCCESS. Se si verifica un errore il valore restituito è PAM_CONV_ERR.

 

4 Una libreria di varie funzioni di aiuto

 

E’ fornita una libreria, libpam_misc, che contiene funzioni per allocare memoria, una funzione di conversazione “text-based” e un insieme di routine per migliorare il supporto standard fornito per le variabili di ambiente PAM.

 

Le funzioni, le strutture e le macro, rese disponibili da questa libreria, possono essere definite includendo il file <security/pam_misc.h>.

 

4.1 Duplicazione di una stringa “sicura”

 

extern char *xstrdup(const char *s)

 

Ritorna una copia della stringa s. Viene restituito NULL se non c’è memoria sufficiente per duplicare o se s=NULL.

 

4.2 Una funzione di conversazione “text-based

 

extern int misc_conv(int num_msg, const struct pam_message **msgm,

                     struct pam_response **response, void *appdata_ptr);

 

Questa funzione farà il prompt all’utente  e otterrà l’input da questi, così come stabilito dai moduli di autenticazione.

La funzione esporta cinque variabili che possono essere utilizzate dal programmatore di applicazioni per limitare la quantità di tempo che la funzione di conversazione utilizzerà, in attesa che l’utente digiti qualcosa.

Le cinque variabili sono le seguenti:

extern time_t pam_misc_conv_warn_time: Questa variabile contiene il tempo dopo il quale l’utente deve essere per la prima volta avvertito che il tempo a disposizione sta scadendo. Il valore di default è zero, che indica di non dare nessun avvertimento. L’applicazione può settare il suo valore, ma ciò dovrebbe esser fatto prima di passare il controllo alla libreria Linux-PAM .

extern const char *pam_misc_conv_warn_line: Usata in concomitanza con pam_misc_conv_warn_time, questa variabile è un puntatore alla stringa che sarà visualizzata per avvertire l’utente che il timeout sta per scadere. Il suo valore di default è “../a.Time is running out…\n”, ma può esser cambiato dall’applicazione prima di passare il controllo a Linux-PAM.

extern time_t pam_misc_conv_die_time: Questa variabile contiene il tempo dopo cui la conversazione termina. Il valore di default è zero, che indica che la conversazione non ha scadenze di tempo. L’applicazione può settare il suo valore, ma ciò dovrebbe esser fatto prima di passare il controllo alla libreria Linux-PAM.   

extern const char *pam_misc_conv_die_line: Usa in concomitanza con pam_misc_conv_die_time, questa variabile è un puntatore alla stringa che sarà visualizzata quando la conversazione termina. Il suo valore di default è “..\a. Sorry, your time is up! \n”, ma può  esser modificato dall’applicazione prima di passare il controllo alla libreria Linux-PAM.

extern int pam_misc_conv_died: Dopo la terminazione della libreria Linux-PAM, il valore di questa variabile indica se il tempo per la conversazione è scaduto. Il valore 1 indica che si è verificato il timeout.

extern int (*pam_binary_handler_fn)(const union pam_u_packet_p send, union pam_u_packet_p *receive): Questo puntatore a funzione è inizializzato a NULL, ma può puntare a una funzione che fornisce lo scambio di messaggi (nascosto ) da macchina a macchina. E’ pensato per essere utilizzato con protocolli di autenticazione segreti come lo scambio di chiavi RSA o Diffie-Hellman (Ancora in via di sviluppo).

 

4.3 Copiare un’insieme di variabili d’ambiente in quello di Linux-PAM

 

extern int pam_misc_paste_env(pam_handle_t *pamh, const char * const * user_env);

 

Questa funzione prende l’insieme di puntatori a variabili d’ambiente e aggiorna il suo contenuto a quello dell’insieme di variabili d’ambiente Linux-PAM. Il successo è indicato da PAM_SUCCESS.

 

4.4 Salvare l’insieme di variabili d’ambiente Linux-PAM per un successivo uso

 

extern char **pam_misc_copy_env(pam_handle_t *pamh);

 

Questa funzione restituisce un puntatore ad una lista di variabili d’ambiente che sono una copia diretta dell’ambiente Linux-PAM. La memoria associata a queste variabili dovrebbe essere liberata dall’applicazione con una chiamata a pam_misc_drop_env().

 

4.5 Liberare la memoria associata ad un insieme di variabili d'ambiente locale

 

extern char **pam_misc_drop_env(char **env);

 

Questa funzione è definita per essere il complemento della funzione pam_misc_copy_env(). Essa libera la memoria associata con env, sovrascrivendo con zero tutta la memoria prima di liberarla con una chiamata alla funzione free().