Barra laterale

programmazione:gnutls:introduzione

Introduzione a GnuTLS

Autore: Fabio Di Matteo
Ultima revisione: 14/12/2010

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 Server e client .

L'esempio che segue è ispirato dagli esempi che sono contenuti nella 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 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 <config.h>
#endif
 
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <string.h>
#include <unistd.h>
#include <gnutls/gnutls.h>
 
 
 
 
#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 <config.h>
#endif
 
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <gnutls/gnutls.h>
#include <netinet/in.h>
 
 
/* 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


programmazione/gnutls/introduzione.txt · Ultima modifica: 18/04/2018 - 15:48 (modifica esterna)