changeset 807:09f8f0c4f4a4

Several changes to support GnuTLS 3.x in a more efficient way
author Sebastien Decugis <sdecugis@freediameter.net>
date Fri, 24 Aug 2012 00:15:48 +0200
parents 6e47b13e7100
children c0a88c1bcc1e
files libfdcore/cnxctx.c libfdcore/cnxctx.h libfdcore/config.c libfdcore/fdcore-internal.h libfdcore/sctps.c tests/testcnx.c
diffstat 6 files changed, 334 insertions(+), 40 deletions(-) [+]
line wrap: on
line diff
--- a/libfdcore/cnxctx.c	Wed Aug 22 23:04:38 2012 +0200
+++ b/libfdcore/cnxctx.c	Fri Aug 24 00:15:48 2012 +0200
@@ -586,7 +586,7 @@
 	CHECK_PARAMS_DO( conn, goto fatal );
 	
 	TRACE_DEBUG(FULL, "Error flag set for socket %d (%s, %s)", conn->cc_socket, conn->cc_id, conn->cc_remid);
-	
+
 	/* Mark the error */
 	fd_cnx_addstate(conn, CC_STATUS_ERROR);
 	
@@ -596,6 +596,7 @@
 		CHECK_FCT_DO( fd_event_send( fd_cnx_target_queue(conn), FDEVP_CNX_ERROR, 0, NULL), goto fatal);
 		fd_cnx_addstate(conn, CC_STATUS_SIGNALED);
 	}
+	
 	return;
 fatal:
 	/* An unrecoverable error occurred, stop the daemon */
@@ -842,9 +843,6 @@
 			switch (ret) {
 				case GNUTLS_E_REHANDSHAKE: 
 					if (!fd_cnx_teststate(conn, CC_STATUS_CLOSING)) {
-						#ifdef GNUTLS_VERSION_310
-						GNUTLS_TRACE( gnutls_handshake_set_timeout( session, GNUTLS_DEFAULT_HANDSHAKE_TIMEOUT));
-						#endif /* GNUTLS_VERSION_310 */
 						CHECK_GNUTLS_DO( ret = gnutls_handshake(session),
 							{
 								if (TRACE_BOOL(INFO)) {
@@ -890,10 +888,6 @@
 			switch (ret) {
 				case GNUTLS_E_REHANDSHAKE: 
 					if (!fd_cnx_teststate(conn, CC_STATUS_CLOSING)) {
-						#ifdef GNUTLS_VERSION_310
-						GNUTLS_TRACE( gnutls_handshake_set_timeout( session, GNUTLS_DEFAULT_HANDSHAKE_TIMEOUT));
-						#endif /* GNUTLS_VERSION_310 */
-
 						CHECK_GNUTLS_DO( ret = gnutls_handshake(session),
 							{
 								if (TRACE_BOOL(INFO)) {
@@ -1030,10 +1024,12 @@
 	if (mode == GNUTLS_SERVER) {
 		gnutls_certificate_server_set_request (*session, GNUTLS_CERT_REQUIRE);
 	}
-	
+		
 	return 0;
 }
 
+#ifndef GNUTLS_VERSION_300
+
 /* Verify remote credentials after successful handshake (return 0 if OK, EINVAL otherwise) */
 int fd_tls_verify_credentials(gnutls_session_t session, struct cnxctx * conn, int verbose)
 {
@@ -1253,6 +1249,240 @@
 	return ret;
 }
 
+#else /* GNUTLS_VERSION_300 */
+
+/* Verify remote credentials DURING handshake (return gnutls status) */
+int fd_tls_verify_credentials_2(gnutls_session_t session)
+{
+	/* inspired from gnutls 3.x guidelines */
+	unsigned int status;
+	const gnutls_datum_t *cert_list = NULL;
+	unsigned int cert_list_size;
+	gnutls_x509_crt_t cert;
+	struct cnxctx * conn;
+	int hostname_verified = 0;
+
+	TRACE_ENTRY("%p", session);
+	
+	/* get the associated connection */
+	conn = gnutls_session_get_ptr (session);
+	
+	/* Trace the session information -- http://www.gnu.org/software/gnutls/manual/gnutls.html#Obtaining-session-information */
+	if (TRACE_BOOL(FULL)) {
+		const char *tmp;
+		gnutls_credentials_type_t cred;
+		gnutls_kx_algorithm_t kx;
+		int dhe, ecdh;
+
+		dhe = ecdh = 0;
+
+		fd_log_debug("TLS Session information for connection '%s':\n", conn->cc_id);
+		
+		/* print the key exchange's algorithm name
+		*/
+		GNUTLS_TRACE( kx = gnutls_kx_get (session) );
+		GNUTLS_TRACE( tmp = gnutls_kx_get_name (kx) );
+		fd_log_debug("\t- Key Exchange: %s\n", tmp);
+
+		/* Check the authentication type used and switch
+		* to the appropriate.
+		*/
+		GNUTLS_TRACE( cred = gnutls_auth_get_type (session) );
+		switch (cred)
+		{
+			case GNUTLS_CRD_IA:
+				fd_log_debug("\t - TLS/IA session\n");
+				break;
+
+
+			#ifdef ENABLE_SRP
+			case GNUTLS_CRD_SRP:
+				fd_log_debug("\t - SRP session with username %s\n",
+					gnutls_srp_server_get_username (session));
+				break;
+			#endif
+
+			case GNUTLS_CRD_PSK:
+				/* This returns NULL in server side.
+				*/
+				if (gnutls_psk_client_get_hint (session) != NULL)
+					fd_log_debug("\t - PSK authentication. PSK hint '%s'\n",
+						gnutls_psk_client_get_hint (session));
+				/* This returns NULL in client side.
+				*/
+				if (gnutls_psk_server_get_username (session) != NULL)
+					fd_log_debug("\t - PSK authentication. Connected as '%s'\n",
+						gnutls_psk_server_get_username (session));
+
+				if (kx == GNUTLS_KX_ECDHE_PSK)
+					ecdh = 1;
+				else if (kx == GNUTLS_KX_DHE_PSK)
+					dhe = 1;
+				break;
+
+			case GNUTLS_CRD_ANON:      /* anonymous authentication */
+				fd_log_debug("\t - Anonymous DH using prime of %d bits\n",
+					gnutls_dh_get_prime_bits (session));
+				if (kx == GNUTLS_KX_ANON_ECDH)
+					ecdh = 1;
+				else if (kx == GNUTLS_KX_ANON_DH)
+					dhe = 1;
+				break;
+
+			case GNUTLS_CRD_CERTIFICATE:       /* certificate authentication */
+
+				/* Check if we have been using ephemeral Diffie-Hellman.
+				*/
+				if (kx == GNUTLS_KX_DHE_RSA || kx == GNUTLS_KX_DHE_DSS)
+					dhe = 1;
+				else if (kx == GNUTLS_KX_ECDHE_RSA || kx == GNUTLS_KX_ECDHE_ECDSA)
+					ecdh = 1;
+				
+				/* Now print some info on the remote certificate */
+				if (gnutls_certificate_type_get (session) == GNUTLS_CRT_X509) {
+					gnutls_datum_t cinfo;
+
+					cert_list = gnutls_certificate_get_peers (session, &cert_list_size);
+
+					fd_log_debug("\t Peer provided %d certificates.\n", cert_list_size);
+
+					if (cert_list_size > 0)
+					{
+						int ret;
+
+						/* we only print information about the first certificate.
+						*/
+						gnutls_x509_crt_init (&cert);
+
+						gnutls_x509_crt_import (cert, &cert_list[0], GNUTLS_X509_FMT_DER);
+
+						fd_log_debug("\t Certificate info:\n");
+
+						/* This is the preferred way of printing short information about
+						 a certificate. */
+
+						ret = gnutls_x509_crt_print (cert, GNUTLS_CRT_PRINT_ONELINE, &cinfo);
+						if (ret == 0)
+						{
+						  fd_log_debug("\t\t%s\n", cinfo.data);
+						  gnutls_free (cinfo.data);
+						}
+						
+						if (conn->cc_tls_para.cn) {
+							if (!gnutls_x509_crt_check_hostname (cert, conn->cc_tls_para.cn)) {
+								fd_log_debug("\tTLS: Remote certificate invalid on socket %d (Remote: '%s')(Connection: '%s') :\n", conn->cc_socket, conn->cc_remid, conn->cc_id);
+								fd_log_debug("\t - The certificate hostname does not match '%s'\n", conn->cc_tls_para.cn);
+								gnutls_x509_crt_deinit (cert);
+								return GNUTLS_E_CERTIFICATE_ERROR;
+							}
+							
+						}
+
+						hostname_verified = 1;
+
+						gnutls_x509_crt_deinit (cert);
+
+					}
+    				}
+				break;
+
+		}                           /* switch */
+
+		if (ecdh != 0)
+			fd_log_debug("\t - Ephemeral ECDH using curve %s\n",
+				gnutls_ecc_curve_get_name (gnutls_ecc_curve_get (session)));
+		else if (dhe != 0)
+			fd_log_debug("\t - Ephemeral DH using prime of %d bits\n",
+				gnutls_dh_get_prime_bits (session));
+
+		/* print the protocol's name (ie TLS 1.0) 
+		*/
+		tmp = gnutls_protocol_get_name (gnutls_protocol_get_version (session));
+		fd_log_debug("\t - Protocol: %s\n", tmp);
+
+		/* print the certificate type of the peer.
+		* ie X.509
+		*/
+		tmp = gnutls_certificate_type_get_name (gnutls_certificate_type_get (session));
+		fd_log_debug("\t - Certificate Type: %s\n", tmp);
+
+		/* print the compression algorithm (if any)
+		*/
+		tmp = gnutls_compression_get_name (gnutls_compression_get (session));
+		fd_log_debug("\t - Compression: %s\n", tmp);
+
+		/* print the name of the cipher used.
+		* ie 3DES.
+		*/
+		tmp = gnutls_cipher_get_name (gnutls_cipher_get (session));
+		fd_log_debug("\t - Cipher: %s\n", tmp);
+
+		/* Print the MAC algorithms name.
+		* ie SHA1
+		*/
+		tmp = gnutls_mac_get_name (gnutls_mac_get (session));
+		fd_log_debug("\t - MAC: %s\n", tmp);
+	
+	}
+
+	/* This verification function uses the trusted CAs in the credentials
+	* structure. So you must have installed one or more CA certificates.
+	*/
+	CHECK_GNUTLS_DO( gnutls_certificate_verify_peers2 (session, &status), return GNUTLS_E_CERTIFICATE_ERROR );
+	if (TRACE_BOOL(INFO) && (status & GNUTLS_CERT_INVALID)) {
+		fd_log_debug("TLS: Remote certificate invalid on socket %d (Remote: '%s')(Connection: '%s') :\n", conn->cc_socket, conn->cc_remid, conn->cc_id);
+		if (status & GNUTLS_CERT_SIGNER_NOT_FOUND)
+			fd_log_debug(" - The certificate hasn't got a known issuer.\n");
+
+		if (status & GNUTLS_CERT_REVOKED)
+			fd_log_debug(" - The certificate has been revoked.\n");
+
+		if (status & GNUTLS_CERT_EXPIRED)
+			fd_log_debug(" - The certificate has expired.\n");
+
+		if (status & GNUTLS_CERT_NOT_ACTIVATED)
+			fd_log_debug(" - The certificate is not yet activated.\n");
+	}	
+	if (status & GNUTLS_CERT_INVALID)
+	{
+		return GNUTLS_E_CERTIFICATE_ERROR;
+	}
+	
+	/* Up to here the process is the same for X.509 certificates and
+	* OpenPGP keys. From now on X.509 certificates are assumed. This can
+	* be easily extended to work with openpgp keys as well.
+	*/
+	if ((!hostname_verified) && (conn->cc_tls_para.cn)) {
+		if (gnutls_certificate_type_get (session) != GNUTLS_CRT_X509) {
+			TRACE_DEBUG(INFO, "TLS: Remote credentials are not x509, rejected on socket %d (Remote: '%s')(Connection: '%s') :\n", conn->cc_socket, conn->cc_remid, conn->cc_id);
+			return GNUTLS_E_CERTIFICATE_ERROR;
+		}
+
+		CHECK_GNUTLS_DO( gnutls_x509_crt_init (&cert), return GNUTLS_E_CERTIFICATE_ERROR );
+
+		cert_list = gnutls_certificate_get_peers (session, &cert_list_size);
+		CHECK_PARAMS_DO( cert_list, return GNUTLS_E_CERTIFICATE_ERROR );
+
+		CHECK_GNUTLS_DO( gnutls_x509_crt_import (cert, &cert_list[0], GNUTLS_X509_FMT_DER), return GNUTLS_E_CERTIFICATE_ERROR );
+
+		if (!gnutls_x509_crt_check_hostname (cert, conn->cc_tls_para.cn)) {
+			if (TRACE_BOOL(INFO)) {
+				fd_log_debug("TLS: Remote certificate invalid on socket %d (Remote: '%s')(Connection: '%s') :\n", conn->cc_socket, conn->cc_remid, conn->cc_id);
+				fd_log_debug(" - The certificate hostname does not match '%s'\n", conn->cc_tls_para.cn);
+			}
+			gnutls_x509_crt_deinit (cert);
+			return GNUTLS_E_CERTIFICATE_ERROR;
+		}
+
+		gnutls_x509_crt_deinit (cert);
+	}
+
+	/* notify gnutls to continue handshake normally */
+	return 0;
+}
+
+#endif /* GNUTLS_VERSION_300 */
+
 /* TLS handshake a connection; no need to have called start_clear before. Reception is active if handhsake is successful */
 int fd_cnx_handshake(struct cnxctx * conn, int mode, char * priority, void * alt_creds)
 {
@@ -1288,19 +1518,32 @@
 		GNUTLS_TRACE( gnutls_transport_set_pull_function(conn->cc_tls_para.session, (void *)fd_cnx_s_recv) );
 		GNUTLS_TRACE( gnutls_transport_set_push_function(conn->cc_tls_para.session, (void *)fd_cnx_s_send) );
 	}
+	
+	/* additional initialization for gnutls 3.x */
+	#ifdef GNUTLS_VERSION_300
+		/* the verify function has already been set in the global initialization in config.c */
+	
+	/* fd_tls_verify_credentials_2 uses the connection */
+	gnutls_session_set_ptr (conn->cc_tls_para.session, (void *) conn);
+	
+	if ((conn->cc_tls_para.cn != NULL) && (mode == GNUTLS_CLIENT)) {
+		/* this might allow virtual hosting on the remote peer */
+		CHECK_GNUTLS_DO( gnutls_server_name_set (conn->cc_tls_para.session, GNUTLS_NAME_DNS, conn->cc_tls_para.cn, strlen(conn->cc_tls_para.cn)), /* ignore failure */);
+	}
+	
+	#endif /* GNUTLS_VERSION_300 */
 
+	#ifdef GNUTLS_VERSION_310
+	GNUTLS_TRACE( gnutls_handshake_set_timeout( conn->cc_tls_para.session, GNUTLS_DEFAULT_HANDSHAKE_TIMEOUT));
+	#endif /* GNUTLS_VERSION_310 */
+	
 	/* Mark the connection as protected from here, so that the gnutls credentials will be freed */
 	fd_cnx_addstate(conn, CC_STATUS_TLS);
 	
 	/* Handshake master session */
 	{
 		int ret;
-		#ifdef GNUTLS_VERSION_310
-		GNUTLS_TRACE( gnutls_handshake_set_timeout( conn->cc_tls_para.session, GNUTLS_DEFAULT_HANDSHAKE_TIMEOUT));
-		#endif /* GNUTLS_VERSION_310 */
-
-		/* When gnutls 2.10.1 is around, we should use gnutls_certificate_set_verify_function and fd_tls_verify_credentials, so that handshake fails directly. */
-		
+	
 		CHECK_GNUTLS_DO( ret = gnutls_handshake(conn->cc_tls_para.session),
 			{
 				if (TRACE_BOOL(INFO)) {
@@ -1310,6 +1553,7 @@
 				return EINVAL;
 			} );
 
+		#ifndef GNUTLS_VERSION_300
 		/* Now verify the remote credentials are valid -- only simple tests here */
 		CHECK_FCT_DO( fd_tls_verify_credentials(conn->cc_tls_para.session, conn, 1), 
 			{  
@@ -1317,6 +1561,7 @@
 				fd_cnx_markerror(conn);
 				return EINVAL;
 			});
+		#endif /* GNUTLS_VERSION_300 */
 	}
 
 	/* Multi-stream TLS: handshake other streams as well */
@@ -1324,7 +1569,7 @@
 #ifndef DISABLE_SCTP
 		/* Start reading the messages from the master session. That way, if the remote peer closed, we are not stuck inside handshake */
 		CHECK_FCT(fd_sctps_startthreads(conn, 0));
-		
+
 		/* Resume all additional sessions from the master one. */
 		CHECK_FCT(fd_sctps_handshake_others(conn, priority, alt_creds));
 
--- a/libfdcore/cnxctx.h	Wed Aug 22 23:04:38 2012 +0200
+++ b/libfdcore/cnxctx.h	Fri Aug 24 00:15:48 2012 +0200
@@ -97,7 +97,9 @@
 /* TLS */
 int fd_tls_rcvthr_core(struct cnxctx * conn, gnutls_session_t session);
 int fd_tls_prepare(gnutls_session_t * session, int mode, char * priority, void * alt_creds);
+#ifndef GNUTLS_VERSION_300
 int fd_tls_verify_credentials(gnutls_session_t session, struct cnxctx * conn, int verbose);
+#endif /* GNUTLS_VERSION_300 */
 
 /* TCP */
 int fd_tcp_create_bind_server( int * sock, sSA * sa, socklen_t salen );
@@ -142,6 +144,5 @@
 
 #endif /* DISABLE_SCTP */
 
-
 #endif /* _CNXCTX_H */
 
--- a/libfdcore/config.c	Wed Aug 22 23:04:38 2012 +0200
+++ b/libfdcore/config.c	Fri Aug 24 00:15:48 2012 +0200
@@ -537,6 +537,11 @@
 	
 	/* gnutls_certificate_set_verify_limits -- so far the default values are fine... */
 	
+	#ifdef GNUTLS_VERSION_300
+	/* Use certificate verification during the handshake */
+	gnutls_certificate_set_verify_function (fd_g_config->cnf_sec_data.credentials, fd_tls_verify_credentials_2);
+	#endif /* GNUTLS_VERSION_300 */
+	
 	/* DH */
 	if (fd_g_config->cnf_sec_data.dh_file) {
 		gnutls_datum_t dhparams = { NULL, 0 };
--- a/libfdcore/fdcore-internal.h	Wed Aug 22 23:04:38 2012 +0200
+++ b/libfdcore/fdcore-internal.h	Fri Aug 24 00:15:48 2012 +0200
@@ -351,6 +351,9 @@
 int             fd_cnx_recv_setaltfifo(struct cnxctx * conn, struct fifo * alt_fifo); /* send FDEVP_CNX_MSG_RECV event to the fifo list */
 int             fd_cnx_send(struct cnxctx * conn, unsigned char * buf, size_t len, uint32_t flags);
 void            fd_cnx_destroy(struct cnxctx * conn);
+#ifdef GNUTLS_VERSION_300
+int             fd_tls_verify_credentials_2(gnutls_session_t session);
+#endif /* GNUTLS_VERSION_300 */
 
 /* Flags for the fd_cnx_send function : */
 #define FD_CNX_ORDERED		(1 << 0)	/* All messages sent with this flag set will be delivered in the same order. No guarantee on other messages */
--- a/libfdcore/sctps.c	Wed Aug 22 23:04:38 2012 +0200
+++ b/libfdcore/sctps.c	Fri Aug 24 00:15:48 2012 +0200
@@ -104,7 +104,6 @@
 				break;
 			
 			case FDEVP_CNX_ERROR:
-				fd_cnx_markerror(conn);
 				goto out;
 				
 			case FDEVP_CNX_SHUTDOWN:
@@ -120,9 +119,11 @@
 out:
 	/* Signal termination of the connection to all decipher threads */
 	for (strid = 0; strid < conn->cc_sctp_para.pairs; strid++) {
-		if (conn->cc_sctps_data.array[strid].raw_recv)
+		if (conn->cc_sctps_data.array[strid].raw_recv) {
 			CHECK_FCT_DO(fd_event_send(conn->cc_sctps_data.array[strid].raw_recv, FDEVP_CNX_ERROR, 0, NULL), goto fatal );
+		}
 	}
+	fd_cnx_markerror(conn);
 	TRACE_DEBUG(FULL, "Thread terminated");	
 	return NULL;
 	
@@ -170,7 +171,7 @@
 	TRACE_ENTRY("%p %p %zd", tr, data, len);
 	CHECK_PARAMS_DO( tr && data, { errno = EINVAL; return -1; } );
 	
-	CHECK_FCT_DO( fd_sctp_sendstr(ctx->parent, ctx->strid, (uint8_t *)data, len), /* errno is already set */ return -1 );
+	CHECK_FCT_DO( fd_sctp_sendstr(ctx->parent, ctx->strid, (uint8_t *)data, len), return -1 );
 	
 	return len;
 }
@@ -183,14 +184,16 @@
 	int emptied;
 	
 	TRACE_ENTRY("%p %p %zd", tr, buf, len);
-	CHECK_PARAMS_DO( tr && buf, { errno = EINVAL; return -1; } );
+	CHECK_PARAMS_DO( tr && buf, { errno = EINVAL; goto error; } );
 	
 	/* If we don't have data available now, pull new message from the fifo -- this is blocking (until the queue is destroyed) */
 	if (!ctx->partial.buf) {
 		int ev;
-		CHECK_FCT_DO( errno = fd_event_get(ctx->raw_recv, &ev, &ctx->partial.bufsz, (void *)&ctx->partial.buf), return -1 );
-		if (ev == FDEVP_CNX_ERROR) 
-			return 0; /* connection closed */
+		CHECK_FCT_DO( errno = fd_event_get(ctx->raw_recv, &ev, &ctx->partial.bufsz, (void *)&ctx->partial.buf), goto error );
+		if (ev == FDEVP_CNX_ERROR) {
+			/* Documentations says to return 0 on connection closed, but it does hang within gnutls_handshake */
+			return -1;
+		}
 	}
 		
 	pulled = ctx->partial.bufsz - ctx->partial.offset;
@@ -215,6 +218,10 @@
 
 	/* We are done */
 	return pulled;
+	
+error:
+	gnutls_transport_set_errno (ctx->session, errno);
+	return -1;
 }
 
 /* Set the parameters of a session to use the appropriate fifo and stream information */
@@ -462,21 +469,21 @@
 	}
 	
 	TRACE_DEBUG(FULL, "Starting TLS resumed handshake on stream %hu", ctx->strid);
-#ifdef GNUTLS_VERSION_310
-	GNUTLS_TRACE( gnutls_handshake_set_timeout( ctx->session, GNUTLS_DEFAULT_HANDSHAKE_TIMEOUT));
-#endif /* GNUTLS_VERSION_310 */
+
 	CHECK_GNUTLS_DO( gnutls_handshake( ctx->session ), return NULL);
 			
 	GNUTLS_TRACE( resumed = gnutls_session_is_resumed(ctx->session) );
+	#ifndef GNUTLS_VERSION_300
 	if (!resumed) {
 		/* Check the credentials here also */
 		CHECK_FCT_DO( fd_tls_verify_credentials(ctx->session, ctx->parent, 0), return NULL );
 	}
+	#endif /* GNUTLS_VERSION_300 */
 	if (TRACE_BOOL(FULL)) {
 		if (resumed) {
 			fd_log_debug("Session was resumed successfully on stream %hu (conn: '%s')\n", ctx->strid, fd_cnx_getid(ctx->parent));
 		} else {
-			fd_log_debug("Session was NOT resumed on stream %hu  (full handshake + verif) (conn: '%s')\n", ctx->strid, fd_cnx_getid(ctx->parent));
+			fd_log_debug("Session was NOT resumed on stream %hu  (full handshake) (conn: '%s')\n", ctx->strid, fd_cnx_getid(ctx->parent));
 		}
 	}
 			
@@ -554,6 +561,24 @@
 		/* Set credentials and priority */
 		CHECK_FCT( fd_tls_prepare(&conn->cc_sctps_data.array[i].session, conn->cc_tls_para.mode, priority, alt_creds) );
 		
+		/* additional initialization for gnutls 3.x */
+		#ifdef GNUTLS_VERSION_300
+			/* the verify function has already been set in the global initialization in config.c */
+
+		/* fd_tls_verify_credentials_2 uses the connection */
+		gnutls_session_set_ptr (conn->cc_sctps_data.array[i].session, (void *) conn);
+
+		if ((conn->cc_tls_para.cn != NULL) && (conn->cc_tls_para.mode == GNUTLS_CLIENT)) {
+			/* this might allow virtual hosting on the remote peer */
+			CHECK_GNUTLS_DO( gnutls_server_name_set (conn->cc_sctps_data.array[i].session, GNUTLS_NAME_DNS, conn->cc_tls_para.cn, strlen(conn->cc_tls_para.cn)), /* ignore failure */);
+		}
+
+		#endif /* GNUTLS_VERSION_300 */
+
+		#ifdef GNUTLS_VERSION_310
+		GNUTLS_TRACE( gnutls_handshake_set_timeout( conn->cc_sctps_data.array[i].session, GNUTLS_DEFAULT_HANDSHAKE_TIMEOUT));
+		#endif /* GNUTLS_VERSION_310 */
+
 		/* For the client, copy data from master session; for the server, set session resuming pointers */
 		if (conn->cc_tls_para.mode == GNUTLS_CLIENT) {
 			CHECK_GNUTLS_DO( gnutls_session_set_data(conn->cc_sctps_data.array[i].session, master_data.data, master_data.size), return ENOMEM );
--- a/tests/testcnx.c	Wed Aug 22 23:04:38 2012 +0200
+++ b/tests/testcnx.c	Fri Aug 24 00:15:48 2012 +0200
@@ -613,6 +613,26 @@
 									 GNUTLS_X509_FMT_PEM), );
 	CHECK( 1, ret );
 	
+	#ifdef GNUTLS_VERSION_300
+	{
+		/* We import these CA in the trust list */
+		gnutls_x509_crt_t * calist;
+		unsigned int cacount = 0;
+		
+		CHECK_GNUTLS_DO( ret = gnutls_x509_crt_list_import2(&calist, &cacount, &ca, GNUTLS_X509_FMT_PEM, 
+							GNUTLS_X509_CRT_LIST_FAIL_IF_UNSORTED), );
+		CHECK( 1, cacount );
+
+		CHECK_GNUTLS_DO( ret = gnutls_x509_trust_list_add_cas (fd_g_config->cnf_sec_data.trustlist, calist, cacount, 0), );
+		CHECK( 1, ret );
+	}
+		
+	/* Use certificate verification during the handshake */
+	gnutls_certificate_set_verify_function (fd_g_config->cnf_sec_data.credentials, fd_tls_verify_credentials_2);
+	
+	#endif /* GNUTLS_VERSION_300 */
+							
+	
 	/* Set the server credentials (in config) */
 	CHECK_GNUTLS_DO( ret = gnutls_certificate_set_x509_key_mem( fd_g_config->cnf_sec_data.credentials,
 									&server_cert,
@@ -1200,7 +1220,7 @@
 	
 	
 	/* Basic operation tested successfully, now test we detect error conditions */
-	
+
 	/* Untrusted certificate, TCP */
 	{
 		struct connect_flags cf;
@@ -1237,11 +1257,12 @@
 		/* Start the handshake directly */
 		CHECK( 0, pthread_create(&thr, NULL, handshake_thr, &hf) );
 		CHECK( EINVAL, fd_cnx_handshake(server_side, GNUTLS_SERVER, NULL, NULL) );
+		fd_cnx_destroy(server_side);
+		
 		CHECK( 0, pthread_join(thr, NULL) );
 		
 		/* Now close the connection */
 		CHECK( 0, pthread_create(&thr, NULL, destroy_thr, client_side) );
-		fd_cnx_destroy(server_side);
 		CHECK( 0, pthread_join(thr, NULL) );
 		
 		/* Free the credentials */
@@ -1266,13 +1287,7 @@
 		CHECK( GNUTLS_E_SUCCESS, ret );
 		/* Set the CA */
 		CHECK_GNUTLS_DO( ret = gnutls_certificate_set_x509_trust_mem( hf.creds, &notrust_ca, GNUTLS_X509_FMT_PEM), );
-		/* TODO: fix me.
-		 We should not get stuck when the server fails the handshake but the client succeeds.
-		 However, at the moment we do get stuck.
-		 FFS, is this a test problem or a problem in the code?
-		 
-		 CHECK_GNUTLS_DO( ret = gnutls_certificate_set_x509_trust_mem( hf.creds, &ca, GNUTLS_X509_FMT_PEM), );
-		 */
+		CHECK_GNUTLS_DO( ret = gnutls_certificate_set_x509_trust_mem( hf.creds, &ca, GNUTLS_X509_FMT_PEM), );
 		CHECK( 1, ret );
 		/* Set the key */
 		CHECK_GNUTLS_DO( ret = gnutls_certificate_set_x509_key_mem( hf.creds, &notrust_cert, &notrust_priv, GNUTLS_X509_FMT_PEM), );
@@ -1293,11 +1308,11 @@
 		/* Start the handshake directly */
 		CHECK( 0, pthread_create(&thr, NULL, handshake_thr, &hf) );
 		CHECK( EINVAL, fd_cnx_handshake(server_side, GNUTLS_SERVER, NULL, NULL) );
+		fd_cnx_destroy(server_side);
 		CHECK( 0, pthread_join(thr, NULL) );
 		
 		/* Now close the connection */
 		CHECK( 0, pthread_create(&thr, NULL, destroy_thr, client_side) );
-		fd_cnx_destroy(server_side);
 		CHECK( 0, pthread_join(thr, NULL) );
 		
 		/* Free the credentials */
@@ -1342,11 +1357,11 @@
 		/* Start the handshake directly */
 		CHECK( 0, pthread_create(&thr, NULL, handshake_thr, &hf) );
 		CHECK( EINVAL, fd_cnx_handshake(server_side, GNUTLS_SERVER, NULL, NULL) );
+		fd_cnx_destroy(server_side);
 		CHECK( 0, pthread_join(thr, NULL) );
 		
 		/* Now close the connection */
 		CHECK( 0, pthread_create(&thr, NULL, destroy_thr, client_side) );
-		fd_cnx_destroy(server_side);
 		CHECK( 0, pthread_join(thr, NULL) );
 		
 		/* Free the credentials */
@@ -1420,11 +1435,11 @@
 		/* Start the handshake, check it is successful */
 		CHECK( 0, pthread_create(&thr, NULL, handshake_thr, &hf) );
 		CHECK( EINVAL, fd_cnx_handshake(server_side, GNUTLS_SERVER, NULL, NULL) );
+		fd_cnx_destroy(server_side);
 		CHECK( 0, pthread_join(thr, NULL) );
 		
 		/* Now close the connection */
 		CHECK( 0, pthread_create(&thr, NULL, destroy_thr, client_side) );
-		fd_cnx_destroy(server_side);
 		CHECK( 0, pthread_join(thr, NULL) );
 		
 		/* Free the credentials */
"Welcome to our mercurial repository"