Hi!

Continuing to clean LmConnection up I splitted out the SSL parts. These
are now put into lm-ssl.[ch]. Instead of calling 

lm_connection_set_use_ssl (...);

you will now first create an SSL structure and then pass it to
lm_connection_set_ssl (similar to how you set a proxy).

>From test-lm.c:
---------------
LmSSL *ssl;
ssl  = lm_ssl_new (expected_fingerprint,
                   (LmSSLFunction) ssl_cb,
                   info,
                   (GDestroyNotify) free_user_info);
                 
lm_connection_set_ssl (connection, ssl);
 
lm_ssl_unref (ssl);
-------------------

Tried it out and it seems to be working fine. Attaching patch if anyone
wants to try out before the CVS (really should move this to imendio svn)
has synced.

Regards,
  Mikael Hallendal
-- 
Mikael Hallendal               [email protected]
Imendio HB                     http://www.imendio.com
Phone: +46 (0)709 718 918

Index: ChangeLog
===================================================================
RCS file: /cvs/gnome/loudmouth/ChangeLog,v
retrieving revision 1.58
diff -u -b -B -p -r1.58 ChangeLog
--- ChangeLog	15 Jan 2004 06:18:13 -0000	1.58
+++ ChangeLog	15 Jan 2004 07:57:10 -0000
@@ -1,5 +1,23 @@
 2004-01-15  Mikael Hallendal  <[email protected]>
 
+	* loudmouth/Makefile.am:
+	- Added lm-ssl.[ch]
+	* loudmouth/lm-connection.c:
+	* loudmouth/lm-connection.h:
+	- Splitted out the SSL parts
+	- No longer contains loads of #ifdefs around tls parts
+	- Added lm_connection_[set|get]_ssl instead.
+	* loudmouth/lm-internals.h:
+	- Added _lm_ssl*
+	* loudmouth/lm-ssl.c:
+	* loudmouth/lm-ssl.h:
+	- New files, the SSL parts from LmConnection.
+	- Declares no-ops for SSL functions if compiled without support for it.
+	* loudmouth/test-lm.c:
+	- Updated for new SSL API.
+
+2004-01-15  Mikael Hallendal  <[email protected]>
+
 	* loudmouth/lm-connection.c:
 	- Added Sjoerd Simons to copyright list.
 
Index: loudmouth/Makefile.am
===================================================================
RCS file: /cvs/gnome/loudmouth/loudmouth/Makefile.am,v
retrieving revision 1.4
diff -u -b -B -p -r1.4 Makefile.am
--- loudmouth/Makefile.am	15 Jan 2004 05:50:26 -0000	1.4
+++ loudmouth/Makefile.am	15 Jan 2004 07:57:10 -0000
@@ -23,6 +23,7 @@ libloudmouth_la_SOURCES =		\
 	lm-internals.h			\
 	lm-sha.c			\
 	lm-sha.h			\
+	lm-ssl.c                        \
 	lm-utils.c			\
 	lm-proxy.c                      \
 	lm-queue.c                      \
@@ -37,6 +38,7 @@ libloudmouthinclude_HEADERS =		\
 	lm-message-node.h		\
 	lm-utils.h			\
 	lm-proxy.h                      \
+	lm-ssl.h                        \
 	loudmouth.h			\
 	$(NULL)
 
Index: loudmouth/lm-connection.c
===================================================================
RCS file: /cvs/gnome/loudmouth/loudmouth/lm-connection.c,v
retrieving revision 1.28
diff -u -b -B -p -r1.28 lm-connection.c
--- loudmouth/lm-connection.c	15 Jan 2004 06:18:14 -0000	1.28
+++ loudmouth/lm-connection.c	15 Jan 2004 07:57:10 -0000
@@ -21,10 +21,6 @@
 
 #include <config.h>
 
-#ifdef HAVE_GNUTLS
-#include <gnutls/gnutls.h>
-#endif
-
 #include <string.h>
 #include <unistd.h>
 #include <sys/types.h>
@@ -73,12 +69,8 @@ struct _LmConnection {
 	/* Parameters */
 	gchar          *server;
 	guint           port;
-	char	        fingerprint[20];
 
-#ifdef HAVE_GNUTLS
-	gnutls_session  gnutls_session;
-	gnutls_certificate_client_credentials gnutls_xcred;
-#endif
+	LmSSL      *ssl;
 
 	LmProxy    *proxy;
 	
@@ -105,10 +97,6 @@ struct _LmConnection {
 
 	LmCallback *disconnect_cb;
 
-	LmSSLFunction  ssl_func;
-	gpointer       ssl_func_data;
-	gchar         *expected_fingerprint;
-
 	LmQueue    *incoming_messages;
 	GSource    *incoming_source;
 
@@ -181,9 +169,6 @@ static GSource * connection_create_sourc
 static void      connection_signal_disconnect   (LmConnection *connection,
 						 LmDisconnectReason reason);
 
-static void     connection_initilize_gnutls     (LmConnection *connection);
-static gboolean connection_begin_ssl            (LmConnection *connection,
-						 GError       **error);
 static void     connection_do_connect           (LmConnectData *connect_data);
 
 
@@ -280,87 +265,6 @@ connection_new_message_cb (LmParser     
 	lm_queue_push_tail (connection->incoming_messages, m);
 }
 
-#ifdef HAVE_GNUTLS
-static gboolean
-connection_verify_certificate (LmConnection *connection)
-{
-	int status;
-	LmSSLFunction ssl_function = connection->ssl_func;
-
-	/* This verification function uses the trusted CAs in the credentials
-	 * structure. So you must have installed one or more CA certificates.
-	 */
-	status = gnutls_certificate_verify_peers (connection->gnutls_session);
-
-	if (status == GNUTLS_E_NO_CERTIFICATE_FOUND)
-		if (ssl_function (connection,
-				   LM_SSL_STATUS_NO_CERT_FOUND,
-				   connection->ssl_func_data) != LM_SSL_RESPONSE_CONTINUE)
-			return FALSE;
-
-	if (status & GNUTLS_CERT_INVALID
-	    || status & GNUTLS_CERT_NOT_TRUSTED
-	    || status & GNUTLS_CERT_CORRUPTED
-	    || status & GNUTLS_CERT_REVOKED)
-		if (ssl_function (connection,
-				   LM_SSL_STATUS_UNTRUSTED_CERT,
-				   connection->ssl_func_data) != LM_SSL_RESPONSE_CONTINUE)
-
-			return FALSE;
-
-	if (gnutls_certificate_expiration_time_peers (connection->gnutls_session) < time (0)) {
-		if (ssl_function (connection,
-				   LM_SSL_STATUS_CERT_EXPIRED,
-				   connection->ssl_func_data) != LM_SSL_RESPONSE_CONTINUE)
-			return FALSE;
-	}
-	
-	if (gnutls_certificate_activation_time_peers (connection->gnutls_session) > time (0)) {
-		if (ssl_function (connection,
-				  LM_SSL_STATUS_CERT_NOT_ACTIVATED,
-				  connection->ssl_func_data) != LM_SSL_RESPONSE_CONTINUE)
-			return FALSE;
-	}
-	
-	if (gnutls_certificate_type_get (connection->gnutls_session) == GNUTLS_CRT_X509) {
-		const gnutls_datum* cert_list;
-		int cert_list_size;
-		int digest_size;
-		
-		cert_list = gnutls_certificate_get_peers (connection->gnutls_session, &cert_list_size);
-		if (cert_list == NULL) {
-			if (ssl_function (connection,
-					   LM_SSL_STATUS_NO_CERT_FOUND,
-					   connection->ssl_func_data) != LM_SSL_RESPONSE_CONTINUE)
-				return FALSE;
-		}
-		if (!gnutls_x509_check_certificates_hostname (&cert_list[0],
-							      connection->server)) {
-			if (ssl_function (connection,
-					  LM_SSL_STATUS_CERT_HOSTNAME_MISMATCH,
-					  connection->ssl_func_data) != LM_SSL_RESPONSE_CONTINUE)
-				return FALSE;
-		}
-		if (gnutls_x509_fingerprint (GNUTLS_DIG_MD5, &cert_list[0],
-					     connection->fingerprint,
-					     &digest_size) >= 0) {
-			if (connection->expected_fingerprint &&
-			    memcmp (connection->expected_fingerprint, connection->fingerprint,
-				    digest_size) &&
-			    ssl_function (connection,
-					   LM_SSL_STATUS_CERT_FINGERPRINT_MISMATCH,
-					   connection->ssl_func_data) != LM_SSL_RESPONSE_CONTINUE)
-				return FALSE;
-		} else if (ssl_function (connection,
-					  LM_SSL_STATUS_GENERIC_ERROR,
-					  connection->ssl_func_data) != LM_SSL_RESPONSE_CONTINUE)
-			return FALSE;
-	}
-
-	return TRUE;
-}
-#endif
-
 static gboolean
 connection_succeeded (LmConnectData *connect_data)
 {
@@ -390,12 +294,17 @@ connection_succeeded (LmConnectData *con
 	 * like that */
 	g_io_channel_set_flags (connection->io_channel, flags, NULL);
 
-	/* FIXME: Handle error */
-	if (!connection_begin_ssl (connection, NULL)) {
+	if (connection->ssl) {
+		if (!_lm_ssl_begin (connection->ssl, connection->fd, 
+				    connection->server, NULL)) {
+			shutdown (connection->fd, SHUT_RDWR);
+			close (connection->fd);
+			connection_do_close (connection);
 		connection->fd = -1;
 		g_io_channel_unref(connection->io_channel);
 		return FALSE;
 	}
+	}
 	
 	g_io_channel_set_close_on_unref (connection->io_channel, TRUE);
 	g_io_channel_set_encoding (connection->io_channel, NULL, NULL);
@@ -634,8 +542,9 @@ connection_do_open (LmConnection *connec
 		}
 	}
 
-
-	connection_initilize_gnutls (connection);
+	if (connection->ssl) {
+		_lm_ssl_initialize (connection->ssl);
+	}
 
 	/* Prepare and do the nonblocking connection */
 	data = g_new (LmConnectData, 1);
@@ -671,13 +580,9 @@ connection_do_close (LmConnection *conne
 
 	connection->state = LM_CONNECTION_STATE_DISCONNECTED;
 
-#ifdef HAVE_GNUTLS
-	if (lm_connection_get_use_ssl (connection)) {
-		gnutls_deinit (connection->gnutls_session);
-		gnutls_certificate_free_credentials (connection->gnutls_xcred);
-		gnutls_global_deinit ();
+	if (connection->ssl) {
+		_lm_ssl_close (connection->ssl);
 	}
-#endif
 }
 
 
@@ -694,32 +599,15 @@ connection_in_event (GIOChannel   *sourc
 		return FALSE;
 	}
 
-#ifdef HAVE_GNUTLS
-	if (lm_connection_get_use_ssl (connection)) {
-		bytes_read = gnutls_record_recv (connection->gnutls_session,
-						 buf,IN_BUFFER_SIZE - 1);
-		if (bytes_read == GNUTLS_E_AGAIN) {
-			status = G_IO_STATUS_AGAIN;
-		}
-		else if (bytes_read <= 0) {
-			status = G_IO_STATUS_ERROR;
-			
-			//connection_error_event (connection->io_channel, 
-			//			G_IO_HUP,
-			//			connection);
-		}
-		else {
-			status = G_IO_STATUS_NORMAL;
-		}
+	if (connection->ssl) {
+		status = _lm_ssl_read (connection->ssl, 
+				       buf, IN_BUFFER_SIZE - 1, &bytes_read);
 	} else {
-#endif
 	    status = g_io_channel_read_chars (connection->io_channel,
 					      buf, IN_BUFFER_SIZE - 1,
 					      &bytes_read,
 					      NULL);
-#ifdef HAVE_GNUTLS
 	}
-#endif
 
 	if (status != G_IO_STATUS_NORMAL) {
 		gint reason;
@@ -821,23 +709,17 @@ connection_send (LmConnection  *connecti
 	g_log (LM_LOG_DOMAIN, LM_LOG_LEVEL_NET, 
 	       "-----------------------------------\n");
 	
-#ifdef HAVE_GNUTLS
-	if (lm_connection_get_use_ssl (connection)) {
-		while ((bytes_written = gnutls_record_send (connection->gnutls_session, str, len)) < 0)
-			if (bytes_written != GNUTLS_E_INTERRUPTED &&
-			    bytes_written != GNUTLS_E_AGAIN)
-			{
-				connection_error_event (connection->io_channel, G_IO_HUP,
+	if (connection->ssl) {
+		if (!_lm_ssl_send (connection->ssl, str, len)) {
+			
+			connection_error_event (connection->io_channel, 
+						G_IO_HUP,
 							connection);
 			}
-		    
 	} else {
-#endif
 		g_io_channel_write_chars (connection->io_channel, str, len, 
 					  &bytes_written, NULL);
-#ifdef HAVE_GNUTLS
 	}
-#endif
 
 	return TRUE;
 }
@@ -1113,70 +995,6 @@ connection_signal_disconnect (LmConnecti
 	}
 }
 
-static void
-connection_initilize_gnutls (LmConnection *connection)
-{
-#ifdef HAVE_GNUTLS
-	if (lm_connection_get_use_ssl (connection)) {
-		gnutls_global_init ();
-		gnutls_certificate_allocate_credentials (&connection->gnutls_xcred);
-	}
-#endif
-}
-
-static gboolean 
-connection_begin_ssl (LmConnection *connection, GError **error)
-{
-#ifdef HAVE_GNUTLS
-	if (lm_connection_get_use_ssl (connection)) {
-		int ret;
-		gboolean auth_ok = TRUE;
-		const int cert_type_priority[2] =
-		{ GNUTLS_CRT_X509, GNUTLS_CRT_OPENPGP };
-
-		gnutls_init (&connection->gnutls_session, GNUTLS_CLIENT);
-		gnutls_set_default_priority (connection->gnutls_session);
-		gnutls_certificate_type_set_priority (connection->gnutls_session,
-						      cert_type_priority);
-		gnutls_credentials_set (connection->gnutls_session,
-					GNUTLS_CRD_CERTIFICATE,
-					connection->gnutls_xcred);
-		
-		gnutls_transport_set_ptr (connection->gnutls_session, 
-					  (gnutls_transport_ptr) connection->fd);
-
-		ret = gnutls_handshake (connection->gnutls_session);
-
-		if (ret >= 0) {
-			auth_ok = connection_verify_certificate (connection);
-		}
-		
-		if (ret < 0 || !auth_ok) {
-			char *errmsg;
-			
-			gnutls_perror (ret);
-			shutdown (connection->fd, SHUT_RDWR);
-			close (connection->fd);
-			connection_do_close (connection);
-			
-			if (!auth_ok) {
-				errmsg = "*** GNUTLS authentication error";
-			} else {
-				errmsg = "*** GNUTLS handshake failed";
-			}
-			
-			g_set_error (error, 
-				     LM_ERROR, LM_ERROR_CONNECTION_OPEN,
-				     errmsg);			
-			
-			return FALSE;
-		}
-		return TRUE;
-	}
-#endif
-	return TRUE;
-}
-
 /**
  * lm_connection_new:
  * @server: The hostname to the server for the connection.
@@ -1203,9 +1021,7 @@ lm_connection_new (const gchar *server)
 	}
 	
 	connection->port              = LM_CONNECTION_DEFAULT_PORT;
-	connection->ssl_func          = NULL;
-	connection->expected_fingerprint = NULL;
-	connection->fingerprint[0]    = '\0';
+	connection->ssl               = NULL;
 	connection->proxy             = NULL;
 	connection->disconnect_cb     = NULL;
 	connection->incoming_messages = lm_queue_new ();
@@ -1609,74 +1425,42 @@ lm_connection_set_port (LmConnection *co
 }
 
 /**
- * lm_connection_supports_ssl:
- *
- * Checks whether Loudmouth supports SSL or not.
- *
- * Return value: #TRUE if this installation of Loudmouth supports SSL, otherwise returns #FALSE.
- **/
-gboolean
-lm_connection_supports_ssl (void)
-{
-#ifdef HAVE_GNUTLS
-	return TRUE;
-#else
-	return FALSE;
-#endif
-}
-/*
-* @fingerprint: the expected fingerprint of the remote cert, or %NULL 
- * @ssl_function: Callback function used when an authentication error occurs.
- */
-void
-lm_connection_set_use_ssl (LmConnection  *connection, 
-			   const gchar   *expected_fingerprint,
-			   LmSSLFunction  ssl_function,
-			   gpointer       user_data)
-{
-	g_return_if_fail (connection != NULL);
-
-	g_free (connection->expected_fingerprint);
-	
-	if (expected_fingerprint) {
-		connection->expected_fingerprint = 
-			g_strdup (expected_fingerprint);
-	}
-
-	connection->ssl_func = ssl_function;
-	connection->ssl_func_data = user_data;
-}
-
-/**
- * lm_connection_get_use_ssl:
+ * lm_connection_get_ssl: 
  * @connection: an #LmConnection
  * 
- * Returns if @connection is using SSL or not
+ * Returns the SSL struct if the connection is using one.
  * 
- * Return value: #TRUE if @connection is using SSL, #FALSE otherwise.
+ * Return value: The ssl struct or %NULL if no proxy is used.
  **/
-gboolean
-lm_connection_get_use_ssl (LmConnection *connection)
+LmSSL *
+lm_connection_get_ssl (LmConnection *connection)
 {
-	g_return_val_if_fail (connection != NULL, FALSE);
+	g_return_val_if_fail (connection != NULL, NULL);
 
-	return connection->ssl_func != NULL;
+	return connection->ssl;
 }
 
 /**
- * lm_connection_get_fingerprint: 
- * @connection: an #LmConnection
+ * lm_connection_set_ssl:
+ * @connection: An #LmConnection
+ * @ssl: An #LmSSL
  *
- * Returns the MD5 fingerprint of the remote server's certificate.
- * 
- * Return value: A 16-byte array representing the fingerprint or %NULL if unknown.
- **/
-const unsigned char *
-lm_connection_get_fingerprint (LmConnection *connection)
+ * Sets SSL struct or unset if @ssl is %NULL. If set @connection will use SSL to for the connection.
+ */
+void
+lm_connection_set_ssl (LmConnection *connection, LmSSL *ssl)
 {
-	g_return_val_if_fail (connection != NULL, NULL);
+	g_return_if_fail (connection != NULL);
 	
-	return (unsigned char*) connection->fingerprint;
+	if (connection->ssl) {
+		lm_ssl_unref (connection->ssl);
+	}
+
+	if (ssl) {
+		connection->ssl = lm_ssl_ref (ssl);
+	} else {
+		connection->ssl = NULL;
+	}
 }
 
 /**
Index: loudmouth/lm-connection.h
===================================================================
RCS file: /cvs/gnome/loudmouth/loudmouth/lm-connection.h,v
retrieving revision 1.10
diff -u -b -B -p -r1.10 lm-connection.h
--- loudmouth/lm-connection.h	15 Jan 2004 05:50:26 -0000	1.10
+++ loudmouth/lm-connection.h	15 Jan 2004 07:57:10 -0000
@@ -26,8 +26,9 @@
 #error "Only <loudmouth/loudmouth.h> can be included directly, this file may di\sappear or change contents."
 #endif
 
-#include <loudmouth/lm-proxy.h>
 #include <loudmouth/lm-message.h>
+#include <loudmouth/lm-proxy.h>
+#include <loudmouth/lm-ssl.h>
 
 #define LM_CONNECTION(o) (LmConnection *) o;
 
@@ -58,27 +59,6 @@ typedef enum {
 } LmDisconnectReason;
 
 typedef enum {
-	LM_CERT_INVALID,
-	LM_CERT_ISSUER_NOT_FOUND,
-	LM_CERT_REVOKED,
-} LmCertificateStatus;
-
-typedef enum {
-	LM_SSL_STATUS_NO_CERT_FOUND,	
-	LM_SSL_STATUS_UNTRUSTED_CERT,
-	LM_SSL_STATUS_CERT_EXPIRED,
-	LM_SSL_STATUS_CERT_NOT_ACTIVATED,
-	LM_SSL_STATUS_CERT_HOSTNAME_MISMATCH,			
-	LM_SSL_STATUS_CERT_FINGERPRINT_MISMATCH,			
-	LM_SSL_STATUS_GENERIC_ERROR,	
-} LmSSLStatus;
-
-typedef enum {
-	LM_SSL_RESPONSE_CONTINUE,
-	LM_SSL_RESPONSE_STOP,
-} LmSSLResponse;
-
-typedef enum {
 	LM_CONNECTION_STATE_DISCONNECTED,
 	LM_CONNECTION_STATE_CONNECTING,
 	LM_CONNECTION_STATE_CONNECTED,
@@ -94,11 +74,6 @@ typedef void          (* LmDisconnectFun
 						LmDisconnectReason  reason,
 						gpointer            user_data);
 
-typedef LmSSLResponse (* LmSSLFunction)        (LmConnection *connection,
-						LmSSLStatus   status,
-						gpointer      user_data);
-
-
 LmConnection *lm_connection_new               (const gchar        *server);
 gboolean      lm_connection_open              (LmConnection       *connection,
 					       LmResultFunction    function,
@@ -138,15 +113,10 @@ void          lm_connection_set_server  
 guint         lm_connection_get_port          (LmConnection       *connection);
 void          lm_connection_set_port          (LmConnection       *connection,
 					       guint               port);
-gboolean      lm_connection_supports_ssl      (void);
-void          lm_connection_set_use_ssl       (LmConnection       *connection,
-					       const gchar        *expected_fingerprint,
-					       LmSSLFunction       ssl_function,
-					       gpointer            user_data);
-gboolean      lm_connection_get_use_ssl       (LmConnection       *connection);
 
-const unsigned char * 
-lm_connection_get_fingerprint                 (LmConnection       *connection);
+LmSSL *       lm_connection_get_ssl           (LmConnection       *connection);
+void          lm_connection_set_ssl           (LmConnection       *connection,
+					       LmSSL              *ssl);
 					       
 LmProxy *     lm_connection_get_proxy         (LmConnection       *connection);
 void          lm_connection_set_proxy         (LmConnection       *connection,
Index: loudmouth/lm-internals.h
===================================================================
RCS file: /cvs/gnome/loudmouth/loudmouth/lm-internals.h,v
retrieving revision 1.6
diff -u -b -B -p -r1.6 lm-internals.h
--- loudmouth/lm-internals.h	15 Jan 2004 05:50:26 -0000	1.6
+++ loudmouth/lm-internals.h	15 Jan 2004 07:57:10 -0000
@@ -52,6 +52,19 @@ gboolean         _lm_proxy_negotiate    
 						     gint              fd,
 						     const gchar      *server,
 						     guint             port);
+void             _lm_ssl_initialize                 (LmSSL            *ssl);
+gboolean         _lm_ssl_begin                      (LmSSL            *ssl,
+						     gint              fd,
+						     const gchar      *server,
+						     GError          **error);
+GIOStatus        _lm_ssl_read                       (LmSSL            *ssl,
+						     gchar            *buf,
+						     gint              len,
+						     gint             *bytes_read);
+gboolean         _lm_ssl_send                       (LmSSL            *ssl,
+						     const gchar      *str,
+						     gint              len);
+void             _lm_ssl_close                      (LmSSL            *ssl);
 
 LmHandlerResult    
 _lm_message_handler_handle_message                (LmMessageHandler *handler,
Index: loudmouth/lm-ssl.c
===================================================================
RCS file: loudmouth/lm-ssl.c
diff -N loudmouth/lm-ssl.c
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ loudmouth/lm-ssl.c	15 Jan 2004 07:57:10 -0000
@@ -0,0 +1,375 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2003-2004 Imendio HB
+ * Copyright (C) 2003      Colin Walters <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <config.h>
+
+#include "lm-internals.h"
+
+#include <string.h>
+#include <glib.h>
+
+#include "lm-error.h"
+
+#ifdef HAVE_GNUTLS
+#include <gnutls/gnutls.h>
+#endif
+
+struct _LmSSL {
+	LmSSLFunction   func;
+	gpointer        func_data;
+	GDestroyNotify  data_notify;
+	gchar          *expected_fingerprint;
+	char            fingerprint[20];
+
+	gint            ref_count;
+#ifdef HAVE_GNUTLS
+	gnutls_session  gnutls_session;
+	gnutls_certificate_client_credentials gnutls_xcred;
+#endif
+};
+
+static void      ssl_free                (LmSSL       *ssl);
+
+#ifdef HAVE_GNUTLS
+static gboolean  ssl_verify_certificate  (LmSSL       *ssl,
+					  const gchar *server);
+
+static gboolean
+ssl_verify_certificate (LmSSL *ssl, const gchar *server)
+{
+	int           status;
+
+	/* This verification function uses the trusted CAs in the credentials
+	 * structure. So you must have installed one or more CA certificates.
+	 */
+	status = gnutls_certificate_verify_peers (ssl->gnutls_session);
+
+	if (status == GNUTLS_E_NO_CERTIFICATE_FOUND) {
+		if (ssl->func (ssl,
+			       LM_SSL_STATUS_NO_CERT_FOUND,
+			       ssl->func_data) != LM_SSL_RESPONSE_CONTINUE) {
+			return FALSE;
+		}
+	}
+	
+	if (status & GNUTLS_CERT_INVALID
+	    || status & GNUTLS_CERT_NOT_TRUSTED
+	    || status & GNUTLS_CERT_CORRUPTED
+	    || status & GNUTLS_CERT_REVOKED) {
+		if (ssl->func (ssl, LM_SSL_STATUS_UNTRUSTED_CERT,
+			       ssl->func_data) != LM_SSL_RESPONSE_CONTINUE) {
+			return FALSE;
+		}
+	}
+	
+	if (gnutls_certificate_expiration_time_peers (ssl->gnutls_session) < time (0)) {
+		if (ssl->func (ssl, LM_SSL_STATUS_CERT_EXPIRED,
+			       ssl->func_data) != LM_SSL_RESPONSE_CONTINUE) {
+			return FALSE;
+		}
+	}
+	
+	if (gnutls_certificate_activation_time_peers (ssl->gnutls_session) > time (0)) {
+		if (ssl->func (ssl, LM_SSL_STATUS_CERT_NOT_ACTIVATED,
+			       ssl->func_data) != LM_SSL_RESPONSE_CONTINUE) {
+			return FALSE;
+		}
+	}
+	
+	if (gnutls_certificate_type_get (ssl->gnutls_session) == GNUTLS_CRT_X509) {
+		const gnutls_datum* cert_list;
+		int cert_list_size;
+		int digest_size;
+		
+		cert_list = gnutls_certificate_get_peers (ssl->gnutls_session, &cert_list_size);
+		if (cert_list == NULL) {
+			if (ssl->func (ssl, LM_SSL_STATUS_NO_CERT_FOUND,
+				       ssl->func_data) != LM_SSL_RESPONSE_CONTINUE) {
+				return FALSE;
+			}
+		}
+		
+		if (!gnutls_x509_check_certificates_hostname (&cert_list[0],
+							      server)) {
+			if (ssl->func (ssl, LM_SSL_STATUS_CERT_HOSTNAME_MISMATCH,
+				       ssl->func_data) != LM_SSL_RESPONSE_CONTINUE) {
+				return FALSE;
+			}
+		}
+
+		if (gnutls_x509_fingerprint (GNUTLS_DIG_MD5, &cert_list[0],
+					     ssl->fingerprint,
+					     &digest_size) >= 0) {
+			if (ssl->expected_fingerprint &&
+			    memcmp (ssl->expected_fingerprint, ssl->fingerprint,
+				    digest_size) &&
+			    ssl->func (ssl,
+				       LM_SSL_STATUS_CERT_FINGERPRINT_MISMATCH,
+				       ssl->func_data) != LM_SSL_RESPONSE_CONTINUE) {
+				return FALSE;
+			}
+		} 
+		else if (ssl->func (ssl, LM_SSL_STATUS_GENERIC_ERROR,
+				    ssl->func_data) != LM_SSL_RESPONSE_CONTINUE) {
+			return FALSE; 
+		} 
+	}
+
+	return TRUE;
+}
+
+void
+_lm_ssl_initialize (LmSSL *ssl) 
+{
+	gnutls_global_init ();
+	gnutls_certificate_allocate_credentials (&ssl->gnutls_xcred);
+}
+
+gboolean
+_lm_ssl_begin (LmSSL *ssl, gint fd, const gchar *server, GError **error)
+{
+	int ret;
+	gboolean auth_ok = TRUE;
+	const int cert_type_priority[2] =
+	{ GNUTLS_CRT_X509, GNUTLS_CRT_OPENPGP };
+
+	gnutls_init (&ssl->gnutls_session, GNUTLS_CLIENT);
+	gnutls_set_default_priority (ssl->gnutls_session);
+	gnutls_certificate_type_set_priority (ssl->gnutls_session,
+					      cert_type_priority);
+	gnutls_credentials_set (ssl->gnutls_session,
+				GNUTLS_CRD_CERTIFICATE,
+				ssl->gnutls_xcred);
+
+	gnutls_transport_set_ptr (ssl->gnutls_session,
+				  (gnutls_transport_ptr) fd);
+
+	ret = gnutls_handshake (ssl->gnutls_session);
+
+	if (ret >= 0) {
+		auth_ok = ssl_verify_certificate (ssl, server);
+	}
+
+	if (ret < 0 || !auth_ok) {
+		char *errmsg;
+
+		gnutls_perror (ret);
+	
+		if (!auth_ok) {
+			errmsg = "*** GNUTLS authentication error";
+		} else {
+			errmsg = "*** GNUTLS handshake failed";
+		}
+
+		g_set_error (error, 
+			     LM_ERROR, LM_ERROR_CONNECTION_OPEN,
+			     errmsg);			
+
+		return FALSE;
+	}
+	
+	return TRUE;
+}
+
+GIOStatus
+_lm_ssl_read (LmSSL *ssl, gchar *buf, gint len, gint *bytes_read)
+{
+	GIOStatus status;
+	
+	*bytes_read = gnutls_record_recv (ssl->gnutls_session, buf, len);
+	
+	if (*bytes_read == GNUTLS_E_AGAIN) {
+		status = G_IO_STATUS_AGAIN;
+	}
+	else if (*bytes_read <= 0) {
+		status = G_IO_STATUS_ERROR;
+	} else {
+		status = G_IO_STATUS_NORMAL;
+	}
+
+	return status;
+}
+
+gboolean
+_lm_ssl_send (LmSSL *ssl, const gchar *str, gint len)
+{
+	gint bytes_written;
+
+	bytes_written = gnutls_record_send (ssl->gnutls_session, str, len);
+
+	while (bytes_written < 0) {
+		if (bytes_written != GNUTLS_E_INTERRUPTED &&
+		    bytes_written != GNUTLS_E_AGAIN) {
+			return FALSE;
+		}
+	
+		bytes_written = gnutls_record_send (ssl->gnutls_session, 
+						    str, len);
+	}
+
+	return TRUE;
+}
+
+void 
+_lm_ssl_close (LmSSL *ssl)
+{
+	gnutls_deinit (ssl->gnutls_session);
+	gnutls_certificate_free_credentials (ssl->gnutls_xcred);
+	gnutls_global_deinit ();
+}
+#endif 
+
+
+static void
+ssl_free (LmSSL *ssl)
+{
+	g_free (ssl->expected_fingerprint);
+	g_free (ssl);
+}
+
+/**
+ * lm_ssl_is_supported:
+ *
+ * Checks whether Loudmouth supports SSL or not.
+ *
+ * Return value: #TRUE if this installation of Loudmouth supports SSL, otherwise returns #FALSE.
+ **/
+gboolean
+lm_ssl_is_supported (void)
+{
+#ifdef HAVE_GNUTLS
+	return TRUE;
+#else
+	return FALSE;
+#endif
+}
+
+LmSSL *
+lm_ssl_new (const gchar    *expected_fingerprint,
+	    LmSSLFunction   ssl_function,
+	    gpointer        user_data,
+	    GDestroyNotify  notify)
+{
+	LmSSL *ssl;
+
+	ssl = g_new0 (LmSSL, 1);
+	
+	ssl->ref_count      = 1;
+	ssl->func           = ssl_function;
+	ssl->func_data      = user_data;
+	ssl->data_notify    = notify;
+	ssl->fingerprint[0] = '\0';
+
+	if (expected_fingerprint) {
+		ssl->expected_fingerprint = g_strdup (expected_fingerprint);
+	} else {
+		ssl->expected_fingerprint = NULL;
+	}
+
+	return ssl;
+}
+
+/**
+ * lm_ssl_get_fingerprint: 
+ * @ssl: an #LmSSL
+ *
+ * Returns the MD5 fingerprint of the remote server's certificate.
+ * 
+ * Return value: A 16-byte array representing the fingerprint or %NULL if unknown.
+ **/
+const unsigned char *
+lm_ssl_get_fingerprint (LmSSL *ssl)
+{
+	g_return_val_if_fail (ssl != NULL, NULL);
+	
+	return (unsigned char*) ssl->fingerprint;
+}
+
+LmSSL *
+lm_ssl_ref (LmSSL *ssl)
+{
+	g_return_val_if_fail (ssl != NULL, NULL);
+
+	ssl->ref_count++;
+
+	return ssl;
+}
+
+void 
+lm_ssl_unref (LmSSL *ssl)
+{
+	g_return_if_fail (ssl != NULL);
+        
+        ssl->ref_count --;
+        
+        if (ssl->ref_count == 0) {
+		if (ssl->data_notify) {
+			(* ssl->data_notify) (ssl->func_data);
+		}
+               
+		ssl_free (ssl);
+        }
+}
+
+/* Define the GnuTLS functions as noops if we compile without support */
+#ifndef HAVE_GNUTLS
+
+void
+_lm_ssl_initialize (LmSSL *ssl)
+{
+	/* NOOP */
+}
+
+gboolean
+_lm_ssl_begin (LmSSL        *ssl,
+	       gint          fd,
+	       const gchar  *server,
+	       GError      **error)
+{
+	return TRUE;
+}
+
+GIOStatus
+_lm_ssl_read (LmSSL *ssl,
+	      gchar *buf,
+	      gint   len,
+	      gint  *bytes_read)
+{
+	/* NOOP */
+	*bytes_read = 0;
+
+	return G_IO_STATUS_EOF;
+}
+
+gboolean 
+_lm_ssl_send (LmSSL *ssl, const gchar *str, gint len)
+{
+	/* NOOP */
+	return TRUE;
+}
+void 
+_lm_ssl_close (LmSSL *ssl)
+{
+	/* NOOP */
+}
+
+#endif
+
Index: loudmouth/lm-ssl.h
===================================================================
RCS file: loudmouth/lm-ssl.h
diff -N loudmouth/lm-ssl.h
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ loudmouth/lm-ssl.h	15 Jan 2004 07:57:11 -0000
@@ -0,0 +1,69 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2003-2004 Imendio HB
+ * Copyright (C) 2003-2004 Sjoerd Simons <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __LM_SSL_H__
+#define __LM_SSL_H__
+
+#if !defined (LM_INSIDE_LOUDMOUTH_H) && !defined (LM_COMPILATION)
+#error "Only <loudmouth/loudmouth.h> can be included directly, this file may disappear or change contents."
+#endif
+
+typedef struct _LmSSL LmSSL;
+typedef enum {
+	LM_CERT_INVALID,
+	LM_CERT_ISSUER_NOT_FOUND,
+	LM_CERT_REVOKED,
+} LmCertificateStatus;
+
+typedef enum {
+	LM_SSL_STATUS_NO_CERT_FOUND,	
+	LM_SSL_STATUS_UNTRUSTED_CERT,
+	LM_SSL_STATUS_CERT_EXPIRED,
+	LM_SSL_STATUS_CERT_NOT_ACTIVATED,
+	LM_SSL_STATUS_CERT_HOSTNAME_MISMATCH,			
+	LM_SSL_STATUS_CERT_FINGERPRINT_MISMATCH,			
+	LM_SSL_STATUS_GENERIC_ERROR,	
+} LmSSLStatus;
+
+typedef enum {
+	LM_SSL_RESPONSE_CONTINUE,
+	LM_SSL_RESPONSE_STOP,
+} LmSSLResponse;
+
+typedef LmSSLResponse (* LmSSLFunction)        (LmSSL        *ssl,
+						LmSSLStatus   status,
+						gpointer      user_data);
+
+LmSSL *
+lm_ssl_new (const gchar    *expected_fingerprint,
+	    LmSSLFunction   ssl_function,
+	    gpointer        user_data,
+	    GDestroyNotify  notify);
+
+gboolean  lm_ssl_is_supported (void);
+
+const unsigned char *  lm_ssl_get_fingerprint (LmSSL *ssl);
+
+
+LmSSL *   lm_ssl_ref          (LmSSL *ssl);
+void      lm_ssl_unref        (LmSSL *ssl);
+
+#endif /* __LM_SSL_H__ */
Index: loudmouth/test-lm.c
===================================================================
RCS file: /cvs/gnome/loudmouth/loudmouth/test-lm.c,v
retrieving revision 1.9
diff -u -b -B -p -r1.9 test-lm.c
--- loudmouth/test-lm.c	27 Dec 2003 23:01:19 -0000	1.9
+++ loudmouth/test-lm.c	15 Jan 2004 07:57:11 -0000
@@ -35,6 +35,15 @@ typedef struct {
 	gchar *passwd;
 } UserInfo;
 
+static void 
+free_user_info (UserInfo *info)
+{
+	g_free (info->name);
+	g_free (info->passwd);
+
+	g_free (info);
+}
+
 static unsigned char expected_fingerprint[20];
 
 static void
@@ -47,7 +56,7 @@ print_finger (const unsigned char *fpr, 
 }
 
 static LmSSLResponse
-ssl_cb (LmConnection *connection, LmSSLStatus status, gpointer ud)
+ssl_cb (LmSSL *ssl, LmSSLStatus status, gpointer ud)
 {
 	g_print ("SSL status: %d\n", status);
 	switch (status) {
@@ -67,7 +76,7 @@ ssl_cb (LmConnection *connection, LmSSLS
 		g_print ("Certificate hostname does not match expected hostname!\n"); 
 		break;
 	case LM_SSL_STATUS_CERT_FINGERPRINT_MISMATCH: {
-		const unsigned char *fpr = lm_connection_get_fingerprint (connection);
+		const unsigned char *fpr = lm_ssl_get_fingerprint (ssl);
 		g_print ("Certificate fingerprint does not match expected fingerprint!\n"); 
 		g_print ("Remote fingerprint: ");
 		print_finger (fpr, 16);
@@ -172,7 +181,7 @@ main (int argc, char **argv)
 
         connection = lm_connection_new (argv[1]);
 
-	if (argc > 4 && !lm_connection_supports_ssl ()) {
+	if (argc > 4 && !lm_ssl_is_supported ()) {
 		g_error ("No SSL support!");
 		exit (1);
 	}
@@ -192,16 +201,22 @@ main (int argc, char **argv)
 	if (argc > 4) {
 		int i;
 		char *p;
+		LmSSL *ssl;
+		
 		lm_connection_set_port (connection,
 					LM_CONNECTION_DEFAULT_PORT_SSL);
 		
 		for (i = 0, p = argv[4]; *p && *(p+1); i++, p += 3)
 			expected_fingerprint[i] = (unsigned char) g_ascii_strtoull (p, NULL, 16);
 
-		lm_connection_set_use_ssl (connection,
-					   expected_fingerprint,
+		ssl = lm_ssl_new (expected_fingerprint,
 					   (LmSSLFunction) ssl_cb,
-					   info);
+				  info, 
+				  (GDestroyNotify) free_user_info);
+		
+		lm_connection_set_ssl (connection, ssl);
+
+		lm_ssl_unref (ssl);
 	}
 
 	result = lm_connection_open (connection,

Reply via email to