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.
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.
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.
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”.
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.
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>.
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.
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
extern const char
*pam_misc_conv_warn_line
extern time_t
pam_misc_conv_die_time
extern const char
*pam_misc_conv_die_line
extern int pam_misc_conv_died
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,
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().