7.4.2 Echo Server with X.509 Authentication II
The following example is a server which supports X.509
authentication. This server supports the export-grade cipher suites,
the DHE ciphersuites and session resuming.
/* This example code is placed in the public domain. */
#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>
#include <gcrypt.h> /* for gcry_control */
#define KEYFILE "key.pem"
#define CERTFILE "cert.pem"
#define CAFILE "ca.pem"
#define CRLFILE "crl.pem"
/* This is a sample TLS 1.0 echo server.
* Export-grade ciphersuites and session resuming are supported.
*/
#define SA struct sockaddr
#define SOCKET_ERR(err,s) if(err==-1) {perror(s);return(1);}
#define MAX_BUF 1024
#define PORT 5556 /* listen to 5556 port */
#define DH_BITS 1024
/* These are global */
gnutls_certificate_credentials_t cert_cred;
static void wrap_db_init (void);
static void wrap_db_deinit (void);
static int wrap_db_store (void *dbf, gnutls_datum_t key, gnutls_datum_t data);
static gnutls_datum_t wrap_db_fetch (void *dbf, gnutls_datum_t key);
static int wrap_db_delete (void *dbf, gnutls_datum_t key);
#define TLS_SESSION_CACHE 50
static gnutls_session_t
initialize_tls_session (void)
{
gnutls_session_t session;
gnutls_init (&session, GNUTLS_SERVER);
/* Use the default priorities, plus, export cipher suites.
*/
gnutls_priority_set_direct (session, "EXPORT", NULL);
gnutls_credentials_set (session, GNUTLS_CRD_CERTIFICATE, cert_cred);
/* request client certificate if any.
*/
gnutls_certificate_server_set_request (session, GNUTLS_CERT_REQUEST);
gnutls_dh_set_prime_bits (session, DH_BITS);
if (TLS_SESSION_CACHE != 0)
{
gnutls_db_set_retrieve_function (session, wrap_db_fetch);
gnutls_db_set_remove_function (session, wrap_db_delete);
gnutls_db_set_store_function (session, wrap_db_store);
gnutls_db_set_ptr (session, NULL);
}
return session;
}
gnutls_dh_params_t dh_params;
/* Export-grade cipher suites require temporary RSA
* keys.
*/
gnutls_rsa_params_t rsa_params;
static char srp_dh_group2048[] =
"-----BEGIN DH PARAMETERS-----\n"
"MIIBBwKCAQCsa9tBMkqam/Fm3l4TiVgvr3K2ZRmH7gf8MZKUPbVgUKNzKcu0oJnt\n"
"gZPgdXdnoT3VIxKrSwMxDc1/SKnaBP1Q6Ag5ae23Z7DPYJUXmhY6s2YaBfvV+qro\n"
"KRipli8Lk7hV+XmT7Jde6qgNdArb9P90c1nQQdXDPqcdKB5EaxR3O8qXtDoj+4AW\n"
"dr0gekNsZIHx0rkHhxdGGludMuaI+HdIVEUjtSSw1X1ep3onddLs+gMs+9v1L7N4\n"
"YWAnkATleuavh05zA85TKZzMBBx7wwjYKlaY86jQw4JxrjX46dv7tpS1yAPYn3rk\n"
"Nd4jbVJfVHWbZeNy/NaO8g+nER+eSv9zAgEC\n" "-----END DH PARAMETERS-----\n";
static int
generate_dh_params (void)
{
gnutls_datum_t dparams = { srp_dh_group2048, sizeof (srp_dh_group2048) };
/* Here instead of generating Diffie-Hellman parameters (for use with DHE
* kx algorithms) we import them.
*/
gnutls_dh_params_init (&dh_params);
gnutls_dh_params_import_pkcs3 (dh_params, &dparams, GNUTLS_X509_FMT_PEM);
return 0;
}
static int
generate_rsa_params (void)
{
gnutls_rsa_params_init (&rsa_params);
/* Generate RSA parameters - for use with RSA-export
* cipher suites. This is an RSA private key and should be
* discarded and regenerated once a day, once every 500
* transactions etc. Depends on the security requirements.
*/
gnutls_rsa_params_generate2 (rsa_params, 512);
return 0;
}
int
main (void)
{
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;
char name[256];
strcpy (name, "Echo Server");
/* to disallow usage of the blocking /dev/random
*/
gcry_control (GCRYCTL_ENABLE_QUICK_RANDOM, 0);
/* this must be called once in the program
*/
gnutls_global_init ();
gnutls_certificate_allocate_credentials (&cert_cred);
gnutls_certificate_set_x509_trust_file (cert_cred, CAFILE,
GNUTLS_X509_FMT_PEM);
gnutls_certificate_set_x509_crl_file (cert_cred, CRLFILE,
GNUTLS_X509_FMT_PEM);
gnutls_certificate_set_x509_key_file (cert_cred, CERTFILE, KEYFILE,
GNUTLS_X509_FMT_PEM);
generate_dh_params ();
generate_rsa_params ();
if (TLS_SESSION_CACHE != 0)
{
wrap_db_init ();
}
gnutls_certificate_set_dh_params (cert_cred, dh_params);
gnutls_certificate_set_rsa_export_params (cert_cred, rsa_params);
/* Socket operations
*/
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");
err = listen (listen_sd, 1024);
SOCKET_ERR (err, "listen");
printf ("%s ready. Listening to port '%d'.\n\n", name, PORT);
client_len = sizeof (sa_cli);
for (;;)
{
session = initialize_tls_session ();
sd = accept (listen_sd, (SA *) & sa_cli, &client_len);
printf ("- connection from %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 has failed (%s)\n\n",
gnutls_strerror (ret));
continue;
}
printf ("- Handshake was completed\n");
/* print_info(session); */
for (;;)
{
memset (buffer, 0, MAX_BUF + 1);
ret = gnutls_record_recv (session, buffer, MAX_BUF);
if (ret == 0)
{
printf ("\n- Peer has closed the TLS connection\n");
break;
}
else if (ret < 0)
{
fprintf (stderr, "\n*** Received corrupted "
"data(%d). Closing the connection.\n\n", ret);
break;
}
else if (ret > 0)
{
/* echo data back to the client
*/
gnutls_record_send (session, buffer, strlen (buffer));
}
}
printf ("\n");
/* do not wait for the peer to close the connection.
*/
gnutls_bye (session, GNUTLS_SHUT_WR);
close (sd);
gnutls_deinit (session);
}
close (listen_sd);
if (TLS_SESSION_CACHE != 0)
{
wrap_db_deinit ();
}
gnutls_certificate_free_credentials (cert_cred);
gnutls_global_deinit ();
return 0;
}
/* Functions and other stuff needed for session resuming.
* This is done using a very simple list which holds session ids
* and session data.
*/
#define MAX_SESSION_ID_SIZE 32
#define MAX_SESSION_DATA_SIZE 512
typedef struct
{
char session_id[MAX_SESSION_ID_SIZE];
size_t session_id_size;
char session_data[MAX_SESSION_DATA_SIZE];
size_t session_data_size;
} CACHE;
static CACHE *cache_db;
static int cache_db_ptr = 0;
static void
wrap_db_init (void)
{
/* allocate cache_db */
cache_db = calloc (1, TLS_SESSION_CACHE * sizeof (CACHE));
}
static void
wrap_db_deinit (void)
{
free (cache_db);
cache_db = NULL;
return;
}
static int
wrap_db_store (void *dbf, gnutls_datum_t key, gnutls_datum_t data)
{
if (cache_db == NULL)
return -1;
if (key.size > MAX_SESSION_ID_SIZE)
return -1;
if (data.size > MAX_SESSION_DATA_SIZE)
return -1;
memcpy (cache_db[cache_db_ptr].session_id, key.data, key.size);
cache_db[cache_db_ptr].session_id_size = key.size;
memcpy (cache_db[cache_db_ptr].session_data, data.data, data.size);
cache_db[cache_db_ptr].session_data_size = data.size;
cache_db_ptr++;
cache_db_ptr %= TLS_SESSION_CACHE;
return 0;
}
static gnutls_datum_t
wrap_db_fetch (void *dbf, gnutls_datum_t key)
{
gnutls_datum_t res = { NULL, 0 };
int i;
if (cache_db == NULL)
return res;
for (i = 0; i < TLS_SESSION_CACHE; i++)
{
if (key.size == cache_db[i].session_id_size &&
memcmp (key.data, cache_db[i].session_id, key.size) == 0)
{
res.size = cache_db[i].session_data_size;
res.data = gnutls_malloc (res.size);
if (res.data == NULL)
return res;
memcpy (res.data, cache_db[i].session_data, res.size);
return res;
}
}
return res;
}
static int
wrap_db_delete (void *dbf, gnutls_datum_t key)
{
int i;
if (cache_db == NULL)
return -1;
for (i = 0; i < TLS_SESSION_CACHE; i++)
{
if (key.size == cache_db[i].session_id_size &&
memcmp (key.data, cache_db[i].session_id, key.size) == 0)
{
cache_db[i].session_id_size = 0;
cache_db[i].session_data_size = 0;
return 0;
}
}
return -1;
}