Manuale di Sun Enterprise Authentication Mechanism

Capitolo 8 Programmazione sicura in rete con RPCSEC_GSS

Le applicazioni che svolgono spesso operazioni di rete devono controllare che le loro transazioni siano sicure. La API RPCSEC_GSS permette agli sviluppatori di usare una serie di meccanismi di sicurezza, tra cui SEAM e Kerberos V5. Inoltre, RPCSEC_GSS include i servizi di integrità e riservatezza, che offrono una protezione aggiuntiva oltre all'autenticazione. Benché RPCSEC_GSS non faccia parte di SEAM, esso rappresenta un ottimo modo per sfruttare le funzioni di Kerberos V5 nella programmazione delle applicazioni. Infatti, poiché RPCSEC_GSS è indipendente dal meccanismo, può essere usato anche dagli sviluppatori che non utilizzino SEAM/Kerberos V5 come meccanismo di sicurezza ma vogliano usufruire delle funzioni di integrità e riservatezza.

Le informazioni fornite in questo capitolo presuppongono una buona conoscenza della programmazione RPC; per informazioni su RPC, vedere la ONC+ Developer's Guide. Questo capitolo offre una trattazione a livello generale; per informazioni su aspetti specifici di RPCSEC_GSS, ad esempio sulle funzioni o sulle strutture di dati, vedere la pagina man rpcsec_gss(3N), oppure le pagine man delle funzioni descritte in questo capitolo.

Questo capitolo affronta i seguenti argomenti:

Sistemi di sicurezza

Questa sezione descrive lo sviluppo e la natura della API RPCSEC_GSS.

La sicurezza prima di RPCSEC_GSS

Uno dei primi sistemi di sicurezza supportati da RPC è stato AUTH_SYS (noto anche come AUTH_UNIX). AUTH_SYS utilizzava una credenziale in stile UNIX, basata sugli ID dell'utente e del gruppo, per identificare il mittente e il destinatario di un messaggio. AUTH_SYS è facile da implementare, ma è anche facile da aggirare poiché non utilizza una vera e propria autenticazione: il server non può infatti verificare che un client sia realmente chi dichiara di essere. Di conseguenza, è relativamente semplice creare una richiesta di rete fittizia in AUTH_SYS.

Successivamente ad AUTH_SYS è stato sviluppato un sistema di sicurezza più evoluto, AUTH_DES. AUTH_DES è basato su un'autenticazione a chiave pubblica: utilizza uno scambio di chiavi Diffie-Hellman per produrre una chiave comune tra la chiave privata del client e la chiave pubblica del server. La chiave comune viene quindi usata per cifrare una chiave di sessione DES, che viene decifrata dal server per l'apertura di una sessione.

AUTH_DES rappresenta un passo avanti rispetto ad AUTH_SYS, ma presenta alcune limitazioni che ne ostacolano l'utilizzo su larga scala. L'obiezione principale di molti utenti riguarda il fatto che la chiave appare sottodimensionata in rapporto agli standard di cifratura odierni.

È stato infine introdotto un altro sistema di sicurezza RPC. AUTH_KERB, basato su Kerberos V4, offre una sicurezza superiore rispetto a AUTH_DES o AUTH_SYS. Ma anche in questo caso non è garantita una protezione assoluta.

Per maggiori informazioni sui meccanismi di sicurezza, vedere la ONC+ Developer's Guide.

Integrità e riservatezza: la GSS-API

Per migliorare ulteriormente la sicurezza è stato aggiunto un nuovo livello di rete, la GSS-API (Generic Security Standard API), la quale, oltre all'autenticazione, che offre due servizi di sicurezza aggiuntivi:


Nota -

Attualmente, la GSS-API non è esposta. Alcune funzioni della GSS-API, tuttavia, sono "visibili" attraverso le funzioni di RPCSEC_GSS -- possono cioè essere manipolate in modo "indiretto". Non è necessario che i programmatori gestiscano direttamente i loro valori.


La API RPCSEC_GSS

Il sistema di sicurezza RPCSEC_GSS permette alle applicazioni RPC ONC di usufruire delle funzioni della GSS-API. RPCSEC_GSS risiede "al di sopra" della GSS-API, come illustrato in questa figura:

Figura 8-1 Livelli di sicurezza della GSS-API e di RPCSEC_GSS

Graphic

Usando la API per RPCSEC_GSS, le applicazioni RPC ONC possono specificare questi elementi:

meccanismo

Un paradigma di sicurezza. Ogni meccanismo di sicurezza offre un diverso tipo di protezione dei dati e uno o più livelli di protezione. In questo caso, si può trattare di qualunque meccanismo di sicurezza supportato dalla GSS-API (Kerberos V5, chiave pubblica RSA, ecc.).

servizio di sicurezza

Può essere il servizio di riservatezza o integrità (o nessuno). Il servizio predefinito è quello di integrità. Il servizio è indipendente dal meccanismo.

qualità della protezione (QOP)

La qualità della protezione specifica il tipo di algoritmo crittografico da utilizzare per implementare i servizi di riservatezza o di integrità. Ogni meccanismo di sicurezza può essere associato a uno o più QOP.

Le applicazioni possono ottenere un elenco dei QOP e dei meccanismi validi usando le funzioni fornite da RPCSEC_GSS. (Vedere "Altre funzioni".) Gli sviluppatori dovrebbero evitare di inserire QOP e meccanismi di hard-coding nelle applicazioni, in modo che non sia necessario modificarle per usare meccanismi e QOP nuovi o differenti.


Nota -

Storicamente, i termini "sistema di sicurezza" e "sistema di autenticazione" sono stati usati con lo stesso significato. Con l'introduzione di RPCSEC_GSS, il termine "sistema di sicurezza" ha acquisito un senso differente. Un sistema può ora includere un servizio (integrità o riservatezza) in aggiunta all'autenticazione, anche se attualmente RPCSEC_GSS è l'unico ad offrire questa possibilità.


Con RPCSEC_GSS, le applicazioni RPC ONC possono stabilire un contesto di sicurezza con un sistema paritetico, scambiarsi dati e distruggere il contesto esattamente come con altri meccanismi. Una volta stabilito un contesto, l'applicazione può cambiare il QOP e il servizio per ogni unità di dati trasmessa.

Per maggiori informazioni su RPCSEC_GSS, inclusi i tipi di dati di RPCSEC_GSS, vedere la pagina man rpcsec_gss(3N).

Routine di RPCSEC_GSS

La Tabella 8-1 riassume i comandi di RPCSEC_GSS. Essa offre una panoramica generale su RPCSEC_GSS e non una descrizione specifica delle singole funzioni. Per maggiori informazioni sulle funzioni, vedere le relative pagine man, oppure consultare la pagina man generale rpcsec_gss(3N), che include un elenco delle strutture di dati di RPCSEC_GSS.

Tabella 8-1 Funzioni di RPCSEC_GSS
AzioneFunzioneInputOutput
 Creare un contesto di sicurezzarpc_gss_seccreate() Handle CLIENT, nome principale, meccanismo, QOP, tipo di servizioHandle AUTH
 Cambiare il QOP o il tipo di servizio per il contestorpc_gss_set_defaults() Vecchio QOP o servizio Nuovo QOP o servizio
 Mostrare la dimensione massima dei dati prima della trasformazione di sicurezzarpc_gss_max_data_length() Dimensione massima dei dati consentita dal meccanismo di trasporto Dimensione massima dei dati pre-trasformazione
 Mostrare la dimensione massima dei dati prima della trasformazione di sicurezzarpc_gss_svc_max_data_length() Dimensione massima dei dati consentita dal meccanismo di trasporto Dimensione massima dei dati pre-trasformazione
 Impostare i nomi principali da rappresentare per il serverrpc_gss_set_svc_name() Nome principale, programma RPC, n. di versione TRUE in caso di operazione riuscita
 Acquisire le credenziali del chiamante (client)rpc_gss_getcred()Puntatore alla struttura svc_req Credenziali UNIX, credenziali RPCSEC_GSS, cookie
 Specificare una funzione di callback (scritta dall'utente)rpc_gss_set_callback() Puntatore alla funzione di callback TRUE in caso di operazione riuscita
 Creare una struttura RPCSEC_GSS per i nomi principali da parametri unicirpc_gss_get_principal_name() Meccanismo, nome utente, nome del sistema, nome del dominio Struttura dei nomi principali RPCSEC_GSS
 Ricavare un codice di errore in caso di mancata esecuzione di una routine RPCSEC_GSSrpc_gss_get_error()   Numero di errore RPCSEC_GSS, errno se applicabile
 Ottenere le stringhe per i meccanismi installatirpc_gss_get_mechanisms()    Elenco dei meccanismi validi
 Ottenere stringhe QOP validirpc_gss_get_mech_info() Meccanismo QOP validi per il meccanismo specificato
 Ottenere i numeri di versione più alto e più basso del RPCSEC_GSS supportatorpc_gss_get_versions()    Numeri di versione più alto e più basso
 Controllare se un meccanismo è installatorpc_gss_is_installed() Meccanismo TRUE se il meccanismo è installato
 Convertire un meccanismo ASCII in un identificativo di oggetti RPCrpc_gss_mech_to_oid() Meccanismo (come stringa) Meccanismo (come OID)
 Convertire un QOP ASCII in un valore interorpc_gss_qop_to_num() QOP (come stringa) QOP (come intero)

Creazione di un contesto

I contesti vengono creati con la chiamata rpc_gss_seccreate(). Questa funzione accetta come argomenti:

Essa restituisce un handle di autenticazione AUTH. L'Esempio 8-1 mostra come usare rpc_gss_seccreate() per creare un contesto usando il meccanismo di sicurezza Kerberos V5 e il servizio di integrità:


Esempio 8-1 rpc_gss_seccreate()

CLIENT *clnt;                    /* handle client */
char server_host[] = "foo";
char service_name[] = "nfs@euro.spa.it";
char mech[] = "kerberos_v5";

clnt = clnt_create(server_host, SERVER_PROG, SERV_VERS, "netpath");
clnt->clnt_auth = rpc_gss_seccreate(clnt, service_name, mech,
                          rpc_gss_svc_integrity, NULL, NULL, NULL);

. . .

Alcuni aspetti da notare nell'Esempio 8-1 sono i seguenti:

Per maggiori informazioni, vedere la pagina man rpc_gss_seccreate(3N).

Modifica dei valori e distruzione dei contesti

Una volta impostato un contesto, l'applicazione può richiedere la modifica del QOP e dei valori dei servizi per le singole unità di dati da trasmettere. (Ad esempio, si può volere che un programma cifri una password ma non un nome di login.) rpc_gss_set_defaults() permette di operare queste modifiche:


Esempio 8-2 rpc_gss_set_defaults()

rpc_gss_set_defaults(clnt->clnt_auth, rpc_gss_svc_privacy, qop);

. . .

In questo caso, il servizio di sicurezza impostato è quello di riservatezza (vedere "Creazione di un contesto"). qop è un puntatore a una stringa che assegna un nome al nuovo QOP.

I contesti vengono distrutti con la procedura normale, con auth_destroy().

Per maggiori informazioni sulla modifica dei servizi e dei QOP, vedere la pagina man rpc_gss_set_defaults(3N).

Nomi principali

Per stabilire e mantenere un contesto di sicurezza sono necessari due tipi di nomi principali:

Impostazione dei nomi principali dei server

All'avvio, un server deve essere informato dei nomi principali che dovrà rappresentare. (Ogni server può assumere l'identità di più nomi principali.) I nomi principali vengono impostati da rpc_gss_set_svc_name():


Esempio 8-3 rpc_gss_set_svc_name()

char *principal, *mechanism;
u_int req_time;

principal = "nfs@euro.spa.it";
mechanism = "kerberos_v5";
req_time = 10000;		/* tempo in cui le credenziali dovrebbero essere valide */

rpc_gss_set_svc_name(principal, mechanism, req_time, SERV_PROG, SERV_VERS);

(Kerberos ignora il parametro req_time, ma altri sistemi di autenticazione potrebbero usarlo.)

Per maggiori informazioni, vedere la pagina man rpc_gss_set_svc_name(3N).

Generazione dei nomi principali dei client

I server devono poter operare sui nomi principali dei client, ad esempio per confrontare il nome principale di un client con il contenuto di una lista di controllo degli accessi, o per cercare una credenziale UNIX per quel client. Questi nomi principali sono tenuti in forma di puntatori a una struttura rpc_gss_principal_t. (Per maggiori informazioni su rpc_gss_principal_t, vedere la pagina man rpcsec_gss(3N).) Se un server vuole confrontare il nome principale che ha ricevuto con il nome di un'entità nota, esso deve poter generare un nome principale in quella forma.

La chiamata rpc_gss_get_principal_name() accetta come input diversi parametri che identificano in modo univoco una persona in una rete, e genera un nome principale in forma di puntatore alla struttura rpc_gss_principal_t:


Esempio 8-4 rpc_gss_get_principal_name()

rpc_gss_principal_t *principal;

rpc_gss_get_principal_name(nome_principale, meccanismo, nome, nodo, dominio);
. . .

Gli argomenti accettati da rpc_gss_get_principal_name() sono i seguenti:

Ogni meccanismo di sicurezza richiede parametri di identificazione differenti. Ad esempio, Kerberos V5 richiede un nome utente e, solo opzionalmente, i nomi qualificati del nodo e del dominio (nella terminologia Kerberos, i nomi dell'host e del settore).

Per maggiori informazioni, vedere la pagina man rpc_gss_get_principal_name(3N).

Svincolo dei nomi principali

I nomi principali vengono resi disponibili con la chiamata alla libreria free().

Ricezione delle credenziali sul server

Un server deve poter acquisire le credenziali di un client. La funzione rpc_gss_getcred(), mostrata nell'Esempio 8-5, permette al server di ottenere le credenziali UNIX o le credenziali RPCSEC_GSS (o entrambe). A tale scopo, esso utilizza due argomenti che vengono impostati se la funzione viene eseguita. Uno è un puntatore a una struttura rpc_gss_ucred_t, che contiene le credenziali UNIX del chiamante:

typedef struct {
    uid_t   uid;          /* ID utente */
    gid_t   gid;          /* ID gruppo */
    short   gidlen;       
    git_t   *gidlist;     /* elenco gruppi */
} rpc_gss_ucred_t;

L'altro argomento è un puntatore a una struttura rpc_gss_raw_cred_t, che ha la forma seguente:

typedef struct {
		u_int                  version;               /* versione programma RPCSEC_GSS */
		char                   *mechanism;
		char                   *qop;
		rpc_gss_principal_t    client_principal;      /* nome principale client */
		char                   *svc_principal;        /* nome principale server */
		rpc_gss_service_t			service;               /* enum riservatezza integrità */
} rpc_gss_rawcred_t;
(Per una descrizione della struttura rpc_gss_principal_t e del modo in cui viene creata, vedere "Generazione dei nomi principali dei client".) Poiché rpc_gss_rawcred_t contiene i nomi principali sia del client che del server, rpc_gss_getcred() può restituirli entrambi.

L'Esempio 8-5 mostra una procedura di spedizione semplice sul lato server, in cui il server riceve le credenziali per il chiamante. La procedura ottiene le credenziali UNIX del chiamante e quindi verifica l'identità dell'utente usando il meccanismo, il QOP e il tipo di servizio specificato nell'argomento rpc_gss_rcred_t.


Esempio 8-5 Acquisizione delle credenziali

static void server_prog(struct svc_req *rqstp, SVCXPRT *xprt)
{
		rpc_gss_ucred_t *ucred;
		rpc_gss_rawcred_t *rcred;
 
		if (rqst->rq_proq == NULLPROC) {
			svc_sendreply(xprt, xdr_void, NULL);
			return;
		}
		/*
		 * autentica tutte le altre richieste */
		 */
 
		switch (rqstp->rq_cred.oa_flavor) {
		case RPCSEC_GSS:
			/*
			 * ottiene le informazioni sulle credenziali
			 */
			rpc_gss_getcred(rqstp, &rcred, &ucred, NULL);
			/*
			* verifica che l'utente sia autorizzato all'accesso
			* usando i parametri di sicurezza ricevuti
			* controllando il file di configurazione
			*/
			if (!authenticate_user(ucred->uid, rcred->mechanism,
				rcred->qop, rcred->service)) {
				svcerr_weakauth(xprt);
				return;
			}
			break; 	/* consente l'accesso */
		default:
			svcerr_weakauth(xprt);
			return;
		} /* end switch */
 
		switch (rqstp->rq_proq) {
		case SERV_PROC1:
			. . .
		}
 
		/* elaborazione normale della richiesta; invio risposta ... */
 
		return;
 
}

Per maggiori informazioni, vedere la pagina man rpc_gss_getcred(3N).

Cookie

Nell'Esempio 8-5, l'ultimo argomento per rpc_gss_getcred() (in questo caso, NULL) è un cookie definito dall'utente, che avrà il valore specificato dal server al momento della creazione del contesto. Questo cookie, un valore di quattro byte, può essere usato in qualunque modo appropriato per l'applicazione: RPC non lo interpreta. Ad esempio, il cookie può essere un puntatore o un indice di una struttura che rappresenta l'origine del contesto; invece di calcolare questo valore per ogni richiesta, il server lo calcola al momento della creazione del contesto (risparmiando tempo nell'elaborazione della richiesta).

Callback

Un altro caso in cui è possibile usare i cookie è quello dei callback. Un server può specificare un callback (definito dall'utente) in modo da sapere quando il contesto verrà usato per la prima volta usando la funzione rpc_gss_set_callback(). Il callback viene chiamato la prima volta in cui un contesto viene usato per lo scambio dei dati, dopo che il contesto è stato stabilito per il programma e la versione specificati.

La routine di callback definita dall'utente assume la forma seguente:

Il secondo e il terzo argomento, deleg e contesto_gss, sono tipi di dati GSS-API e attualmente non sono esposti, perciò la funzione di callback può ignorarli. (In breve, deleg è l'identità di un qualsiasi sistema paritetico delegato, mentre contesto_gss è un puntatore al contesto GSS-API, nel caso in cui il programma voglia eseguire operazioni GSS-API sul contesto, ad esempio un test per i criteri di accettazione.) L'argomento cookie è già stato descritto.

L'argomento lock è un puntatore a una struttura rpc_gss_lock_t:

typedef struct {
		bool_t              locked;
		rpc_gss_rawcred_t   *raw_cred;
} rpc_gss_lock_t;
Questo parametro permette a un server di applicare un determinato QOP e un determinato servizio per la sessione. Il QOP e il servizio si trovano nella struttura rpc_gss_rawcred_t descritta nell'Esempio 8-5. (Il server non dovrebbe cambiare i valori per il servizio e il QOP.) Quando viene chiamato il callback definito dall'utente, il campo locked viene impostato su FALSE. Se il server imposta locked su TRUE, verranno accettate solo le richieste con valori per il QOP e il servizio che corrispondono a quelli presenti nella struttura rpc_gss_rawcred_t.

Per maggiori informazioni, vedere la pagina man rpc_gss_set_callback(3N).

Dimensione massima dei dati

Le due funzioni rpc_gss_max_data_length() e rpc_gss_svc_max_data_length() permettono di determinare quale dimensione possa avere un blocco di dati prima di essere trasformato dalle misure di sicurezza ed essere inviato attraverso la rete. Generalmente, infatti, le trasformazioni di sicurezza, come la cifratura, cambiano le dimensioni dei dati trasmessi (quasi sempre aumentandole). Per evitare che le dimensioni dei dati trasformati superino una certa soglia, queste due funzioni (di cui la prima è la versione per il client, e l'altra la versione per il server) restituiscono la dimensione massima pre-trasformazione accettabile per un determinato trasporto.

Per maggiori informazioni, vedere le pagine man rpc_gss_max_data_length(3N) e rpc_gss_svc_max_data_length(3N).

Altre funzioni

Per ottenere informazioni sul sistema di sicurezza installato, sono disponibili le seguenti funzioni:

L'uso di queste funzioni permette ai programmatori di evitare l'uso di parametri di sicurezza hard-coded nelle applicazioni. (Per un elenco completo delle funzioni di RPCSEC_GSS, vedere la Tabella 8-1 e la pagina man rpcsec_gss(3N).)

File associati

Per memorizzare le informazioni, RPCSEC_GSS utilizza alcuni file specifici.

La tabella gsscred

Quando un server richiama le credenziali del client associate a una richiesta, esso può ottenere il nome principale del client (in forma di puntatore a una struttura rpc_gss_principal_t) o le credenziali UNIX locali (UID) per quel client. I servizi come NFS richiedono una credenziale UNIX locale per il controllo degli accessi, mentre altri servizi non la richiedono e possono, ad esempio, memorizzare il nome principale come struttura rpc_gss_principal_t direttamente nelle loro liste di controllo degli accessi.


Nota -

La corrispondenza tra la credenziale di rete di un client (il suo nome principale) e la sua credenziale UNIX locale non è automatica: deve essere impostata esplicitamente dall'amministratore della sicurezza locale.


Il file gsscred contiene sia le credenziali UNIX che le credenziali di rete (ad esempio, Kerberos V5) del client. (Le seconde sono una rappresentazione Hex-ASCII della struttura rpc_gss_principal_t.) Il file è accessibile mediante XFN; in questo modo, questa tabella può essere implementata su files, NIS, NIS+ o su qualunque servizio di denominazione supportato da XFN. Nella gerarchia XFN, questa tabella appare come unità_org_corrente/servizio/gsscred. La tabella gsscred viene amministrata con l'utility gsscred, che permette agli amministratori di aggiungere ed eliminare utenti e meccanismi.

/etc/gss/qop e /etc/gss/mech

Per ragioni di comodità, RPCSEC_GSS utilizza denominazioni di tipo stringa per rappresentare i meccanismi e la qualità della protezione (QOP). I meccanismi sottostanti, tuttavia, richiedono che i meccanismi vengano rappresentati come identificativi di oggetti e QOP in forma di numeri interi di 32 bit. In più, per ogni meccanismo, deve essere specificata la libreria condivisa che implementa i servizi per quel meccanismo.

Il file /etc/gss/mech memorizza le seguenti informazioni su tutti i meccanismi installati su un sistema: il nome del meccanismo, in formato ASCII; l'OID del meccanismo; la libreria condivisa che implementa i servizi forniti da quel meccanismo; e, opzionalmente, il modulo kernel che implementa il servizio. Una riga potrebbe avere questa forma:


kerberos_v5   1.2.840.113554.1.2.2    gl/mech_krb5.so gl_kmech_krb5

Il file /etc/gss/qop memorizza, per tutti i meccanismi installati, tutti i QOP supportati da ogni meccanismo, sia come stringa ASCII che come numero intero di 32 bit.

Sia /etc/gss/mech che /etc/gss/qop vengono creati la prima volta che i meccanismi di sicurezza vengono installati su un certo sistema.

Poiché molte delle routine RPC interne del kernel utilizzano valori non-stringa per rappresentare meccanismi e QOP, le applicazioni che debbano usare queste routine potranno usare le funzioni rpc_gss_mech_to_oid() e rpc_gss_qop_to_num() per ottenere gli equivalenti non-stringa per questi parametri.