====== 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