====== Introduzione a GnuTLS ======
Autore: **//Fabio Di Matteo//** \\ Ultima revisione: **//14/12/2010//** \\ \\
[[http://www.gnu.org/software/gnutls/|GnuTLS]] è una implementazione libera e stabile degli standard SSL e TLS il suo scopo è fornire un comodo set di istruzioni per aprire un canale sicuro tra server e client. Ovvero gestire la crittografia dei dati scambiati. \\
La libreria è scritta in C ed ha le seguenti caratteristiche: \\ \\
* Support for TLS 1.2, TLS 1.1, TLS 1.0 and SSL 3.0 protocols (Since SSL 2.0 is insecure it is not supported);
* Support for TLS extensions: server name indication, max record size, etc;
* Support for authentication using the SRP protocol;
* Support for authentication using both X.509 certificates and OpenPGP keys;
* Support for TLS Pre-Shared-Keys (PSK) extension;
* Support for Inner Application (TLS/IA) extension;
* Support for X.509 and OpenPGP certificate handling;
* Support for X.509 Proxy Certificates (RFC 3820);
* Supports all the strong encryption algorithms, including Camellia (RFC 4132);
* Supports safe renegotiation (RFC 5746);
* Supports compression;
* Runs on most Unix platforms and Windows;
* GPL compatible license.
In questo articolo andremo a vedere come realizzare **un semplice echo server** con gnuTLS che risolva il problema del men in the middle (ovvero un terzo che sniffa il traffico tra client e server) il tutto senza nessun tipo di autenticazione.
Potrebbe essere utile leggere l'articolo [[programmazione:c:server_e_client]] . \\
L'esempio che segue è ispirato dagli esempi che sono contenuti nella [[http://www.gnu.org/software/gnutls/manual/html_node/index.html|documentazione ufficiale ]] .
===== Il server =====
Il server in questione si occupera' di instaurare un canale sicuro con il client per ricevere un messaggio (testo) ed infine si preocccupera' di inviare al client lo steso messaggio , appunto come fosse un eco.
==== Le principali funzioni ====
** initialize_tls_session()** si occupa di inizializzare la libreria GnuTLS e ritorna la variabile della sessione da utilizzare durante l'handshake
static gnutls_session_t initialize_tls_session (void)
**generate_dh_params () ** genera i parametri [[http://it.wikipedia.org/wiki/Scambio_di_chiavi_Diffie-Hellman|Diffie-Hellman]] necessari per la cifratura del traffico
static gnutls_session_t initialize_tls_session (void)
Infine nella **main()** abbiamo la gestione di un comune server tcp che pero' fara' uso delle funzioni **gnutls_record_send** e **gnutls_record_recv ** rispettivamente per inviare e ricevere buffer di dati. \\ \\
**gnutls-server.c **
/* Un echo server per con autenticazione anonima*/
#ifdef HAVE_CONFIG_H
# include
#endif
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define SA struct sockaddr
#define SOCKET_ERR(err,s) if(err==-1) {perror(s);return(1);} /*macro per gli errori*/
#define MAX_BUF 1024
#define PORT 9000 /* in ascolto sulla porta 9000 */
#define DH_BITS 1024
/*Variabile globale che conterra' il tipo di handshake (anonimo con Diffie-Hellman) */
gnutls_anon_server_credentials_t anoncred;
/*Variabile globale che conterra' il parametro Diffie-Hellman per la crittografia */
static gnutls_dh_params_t dh_params;
/*Inizializza la libreria GnuTLS e ritorna la variabile delle sessione da
* utilizzare per l'handshake*/
static gnutls_session_t initialize_tls_session (void)
{
gnutls_session_t session;
gnutls_init (&session, GNUTLS_SERVER);
gnutls_priority_set_direct (session, "NORMAL:+ANON-DH", NULL);
gnutls_credentials_set (session, GNUTLS_CRD_ANON, anoncred);
gnutls_dh_set_prime_bits (session, DH_BITS);
return session;
}
/* Genera i parametri Diffie-Hellman */
static int generate_dh_params (void)
{
gnutls_dh_params_init (&dh_params);
gnutls_dh_params_generate2 (dh_params, DH_BITS);
return 0;
}
int main (int argc, char *argv[])
{
int err, listen_sd;
int sd, ret;
struct sockaddr_in sa_serv;
struct sockaddr_in sa_cli;
int client_len;
char topbuf[512];
gnutls_session_t session;
char buffer[MAX_BUF + 1];
int optval = 1;
/* Queste istruzioni inizializzano gnutls */
gnutls_global_init ();
gnutls_anon_allocate_server_credentials (&anoncred);
generate_dh_params ();
gnutls_anon_set_server_dh_params (anoncred, dh_params);
/* Socket tcp per il server */
listen_sd = socket (AF_INET, SOCK_STREAM, 0);
SOCKET_ERR (listen_sd, "socket");
memset (&sa_serv, '\0', sizeof (sa_serv));
sa_serv.sin_family = AF_INET;
sa_serv.sin_addr.s_addr = INADDR_ANY;
sa_serv.sin_port = htons (PORT); /* Server Port number */
setsockopt (listen_sd, SOL_SOCKET, SO_REUSEADDR, (void *) &optval,
sizeof (int));
err = bind (listen_sd, (SA *) & sa_serv, sizeof (sa_serv));
SOCKET_ERR (err, "bind");/*macro per gli errori*/
err = listen (listen_sd, 1024);
SOCKET_ERR (err, "listen");
printf ("Server pronto. in ascolto sulla porta '%d'.\n\n", PORT);
client_len = sizeof (sa_cli);
while(1)
{
session = initialize_tls_session ();
sd = accept (listen_sd, (SA *) & sa_cli, &client_len);
printf ("- connessione da %s, port %d\n",
inet_ntop (AF_INET, &sa_cli.sin_addr, topbuf,
sizeof (topbuf)), ntohs (sa_cli.sin_port));
gnutls_transport_set_ptr (session, (gnutls_transport_ptr_t) sd);
ret = gnutls_handshake (session);
if (ret < 0)
{
close (sd);
gnutls_deinit (session);
fprintf (stderr, "*** Handshake fallito (%s)\n\n", gnutls_strerror (ret));
continue;
}
printf ("- Handshake completato con successo\n");
for (;;)
{
memset (buffer, 0, MAX_BUF + 1);
ret = gnutls_record_recv (session, buffer, MAX_BUF);
if (ret == 0)
{
printf ("\n- il peer ha chiuso la connessione GnuTLS \n");
break;
}
else if (ret < 0)
{
fprintf (stderr, "\n*** Ricevuti dati corrotti ""data(%d). Chiusura della connessione.\n\n", ret);
break;
}
/*Il client ha mandato qualcosa*/
else if (ret > 0)
{
/* Rimanda al client i dati inviati (quindi fa l'echo server) */
gnutls_record_send (session, buffer, strlen (buffer));
printf("Dati ricevuti dal client: \n%s\n",buffer);
}
}
printf ("\n");
/* Chiude la connessione senza aspettare il peer */
gnutls_bye (session, GNUTLS_SHUT_WR);
/*Chiudo il socket tcp*/
close (sd);
/*Distruggo la sessione gnutls*/
gnutls_deinit (session);
}//fine while del server
/*Chiudo il socket tcp e distruggo le variabili gnutls*/
close (listen_sd);
gnutls_anon_free_server_credentials (anoncred);
gnutls_global_deinit ();
return 0;
}
===== Il client =====
Il client si occupera' di iniziare l'handshake e inviare del testo al server il quale reinviera' al client subito dopo .
==== Le principali funzioni ====
Le principali azioni verranno svolte dal main che si occupera' di instaurare l' handshake e trasmettere i dati e successivamente riceverli. \\
Le funzioni **tcp_connect()** e **tcp_close (); ** si occuperanno rispettivamente dell'apertura e chiusura del socket tcp.
int tcp_connect (char *SERVER ,char *PORT); // ritorna il socket descriptor
void tcp_close (int sd);
La funzione **end()** si occupera' invece di chiudere la connessione tcp e di distruggere le varibili e strutture gnutls alla fine del programma.
int end(int sd, gnutls_session_t session, gnutls_anon_client_credentials_t anoncred )
**gnutls-client.c**
#ifdef HAVE_CONFIG_H
# include
#endif
#include
#include
#include
#include
#include
#include
#include
#include
#include
/* Un client TLS , con autenticazione anonima. */
#define MAX_BUF 1024
#define MSG "Prova FreeMediaLab\n"
#define SA struct sockaddr
/* Client tcp tradizionale */
int tcp_connect (char *SERVER ,char *PORT);
void tcp_close (int sd);
/*Funzione per chiudere il client */
int end(int sd, gnutls_session_t session, gnutls_anon_client_credentials_t anoncred );
int tcp_connect (char *SERVER ,char *PORT)
{
int err, sd;
struct sockaddr_in sa;
/* connessione al server */
sd = socket (AF_INET, SOCK_STREAM, 0);
memset (&sa, '\0', sizeof (sa));
sa.sin_family = AF_INET;
sa.sin_port = htons (atoi (PORT));
inet_pton (AF_INET, SERVER, &sa.sin_addr);
err = connect (sd, (SA *) & sa, sizeof (sa));
if (err < 0)
{
fprintf (stderr, "Errore di connessione\n");
exit (1);
}
return sd;
}
/* Chiusura del socket descriptor. */
void tcp_close (int sd)
{
shutdown (sd, SHUT_RDWR); /* no more receptions */
close (sd);
}
int main (int argc, char* argv[])
{
int ret, sd, ii;
gnutls_session_t session;
char buffer[MAX_BUF + 1];
gnutls_anon_client_credentials_t anoncred;
/* Accesso anonimo al server */
gnutls_global_init ();
gnutls_anon_allocate_client_credentials (&anoncred);
/* Inizializzazione della sessione */
gnutls_init (&session, GNUTLS_CLIENT);
/* Utilizzo opzioni di default per la la sessione */
gnutls_priority_set_direct (session, "PERFORMANCE:+ANON-DH:!ARCFOUR-128", NULL);
/* Abilitiamo l'accesso anonimo per la sessione */
gnutls_credentials_set (session, GNUTLS_CRD_ANON, anoncred);
/* Connessione al socket tcp tradizionale */
sd = tcp_connect ("127.0.0.1", "9000");
/*Agganciamo al socket tradizionale le sessione tls*/
gnutls_transport_set_ptr (session, (gnutls_transport_ptr_t) sd);
/* Avviamo l'handshake */
ret = gnutls_handshake (session);
if (ret < 0)
{
fprintf (stderr, "*** Handshake fallito\n");
gnutls_perror (ret);
end(sd, session, anoncred );
}else{
printf ("- Handshake completato con successo!\n");
}
/*Inviamo qualcosa al server*/
gnutls_record_send (session, MSG, strlen (MSG));
/*Riceviamo la risposta dal server*/
ret = gnutls_record_recv (session, buffer, MAX_BUF);
if (ret == 0)
{
printf ("- Il peer ha chiuso la connessione TLS\n");
end(sd, session, anoncred );
} else if (ret < 0)
{
fprintf (stderr, "*** Errore: %s\n", gnutls_strerror (ret));
end(sd, session, anoncred );
}
/*Stampiamo la risposta del server*/
printf ("- Recevuti %d bytes: ", ret);
for (ii = 0; ii < ret; ii++)
{
fputc (buffer[ii], stdout);
}
fputs ("\n", stdout);
gnutls_bye (session, GNUTLS_SHUT_RDWR);
/*Chiudo il socket tcp e distruggo le variabili gnutls*/
end(sd, session, anoncred );
return 0;
}
/*Chiudo il socket tcp e distruggo le variabili gnutls*/
int end(int sd, gnutls_session_t session, gnutls_anon_client_credentials_t anoncred )
{
tcp_close (sd);
gnutls_deinit (session);
gnutls_anon_free_client_credentials (anoncred);
gnutls_global_deinit ();
return 0 ;
}
===== Il makefile =====
CPP = gcc
all:
$(CPP) gnutls-client.c -o gnutls-client `pkg-config gnutls --cflags --libs`
$(CPP) gnutls-server.c -o gnutls-server `pkg-config gnutls --cflags --libs`
server:
$(CPP) gnutls-server.c -o gnutls-server `pkg-config gnutls --cflags --libs`
client:
$(CPP) gnutls-client.c -o gnutls-client `pkg-config gnutls --cflags --libs`
clean:
rm gnutls-client
rm gnutls-server