2004-01-15 Mikael Hallendal <micke@imendio.com>
authorhallski <hallski>
Thu, 15 Jan 2004 07:57:29 +0000 (07:57 +0000)
committerhallski <hallski>
Thu, 15 Jan 2004 07:57:29 +0000 (07:57 +0000)
* 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.

ChangeLog
loudmouth/Makefile.am
loudmouth/lm-connection.c
loudmouth/lm-connection.h
loudmouth/lm-internals.h
loudmouth/lm-ssl.c [new file with mode: 0644]
loudmouth/lm-ssl.h [new file with mode: 0644]
loudmouth/test-lm.c

index d16b7574b4a070109fc83234c726f0f6c0b05906..5182ea844fbd71be6039d09b837c8a4bc292f5d8 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,21 @@
+2004-01-15  Mikael Hallendal  <micke@imendio.com>
+
+       * 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  <micke@imendio.com>
 
        * loudmouth/lm-connection.c:
index 21b543effe49fdea571b03d03368b17dfa78908f..d3ef71746706f108f35abe1e467eb1ab212286b3 100644 (file)
@@ -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 75c063804f448ed41d42d8d7f07ac11861084b83..892ee08c6408e996bff081d4c17f9b1be11a947d 100644 (file)
 
 #include <config.h>
 
-#ifdef HAVE_GNUTLS
-#include <gnutls/gnutls.h>
-#endif
-
 #include <string.h>
 #include <unistd.h>
 #include <sys/types.h>
@@ -71,14 +67,10 @@ typedef struct {
 
 struct _LmConnection {
        /* Parameters */
-       gchar          *server;
-       guint           port;
-       char            fingerprint[20];
+       gchar      *server;
+       guint       port;
 
-#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_source       (LmConnection *connection);
 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     *parser,
        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,11 +294,16 @@ connection_succeeded (LmConnectData *connect_data)
         * like that */
        g_io_channel_set_flags (connection->io_channel, flags, NULL);
 
-       /* FIXME: Handle error */
-       if (!connection_begin_ssl (connection, NULL)) {
-               connection->fd = -1;
-               g_io_channel_unref(connection->io_channel);
-               return FALSE;
+       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);
@@ -418,7 +327,6 @@ connection_succeeded (LmConnectData *connect_data)
                                                   (GIOFunc) connection_hup_event,
                                                   connection);
 
-
        if (!connection_send (connection, 
                              "<?xml version='1.0' encoding='UTF-8'?>", -1,
                              NULL)) {
@@ -634,8 +542,9 @@ connection_do_open (LmConnection *connection, GError **error)
                }
        }
 
-
-       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 *connection)
 
        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   *source,
                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
+               status = g_io_channel_read_chars (connection->io_channel,
+                                                 buf, IN_BUFFER_SIZE - 1,
+                                                 &bytes_read,
+                                                 NULL);
        }
-#endif
 
        if (status != G_IO_STATUS_NORMAL) {
                gint reason;
@@ -820,24 +708,18 @@ connection_send (LmConnection  *connection,
        g_log (LM_LOG_DOMAIN, LM_LOG_LEVEL_NET, "%s\n", str);
        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,
-                                                       connection);
-                       }
-                   
+
+       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 (LmConnection       *connection,
        }
 }
 
-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 *connection, guint port)
 }
 
 /**
- * lm_connection_supports_ssl:
- *
- * Checks whether Loudmouth supports SSL or not.
+ * lm_connection_get_ssl: 
+ * @connection: an #LmConnection
  *
- * Return value: #TRUE if this installation of Loudmouth supports SSL, otherwise returns #FALSE.
+ * Returns the SSL struct if the connection is using one.
+ * 
+ * Return value: The ssl struct or %NULL if no proxy is used.
  **/
-gboolean
-lm_connection_supports_ssl (void)
+LmSSL *
+lm_connection_get_ssl (LmConnection *connection)
 {
-#ifdef HAVE_GNUTLS
-       return TRUE;
-#else
-       return FALSE;
-#endif
+       g_return_val_if_fail (connection != NULL, NULL);
+       
+       return connection->ssl;
 }
-/*
-* @fingerprint: the expected fingerprint of the remote cert, or %NULL 
- * @ssl_function: Callback function used when an authentication error occurs.
+
+/**
+ * lm_connection_set_ssl:
+ * @connection: An #LmConnection
+ * @ssl: An #LmSSL
+ *
+ * Sets SSL struct or unset if @ssl is %NULL. If set @connection will use SSL to for the connection.
  */
 void
-lm_connection_set_use_ssl (LmConnection  *connection, 
-                          const gchar   *expected_fingerprint,
-                          LmSSLFunction  ssl_function,
-                          gpointer       user_data)
+lm_connection_set_ssl (LmConnection *connection, LmSSL *ssl)
 {
        g_return_if_fail (connection != NULL);
 
-       g_free (connection->expected_fingerprint);
-       
-       if (expected_fingerprint) {
-               connection->expected_fingerprint = 
-                       g_strdup (expected_fingerprint);
+       if (connection->ssl) {
+               lm_ssl_unref (connection->ssl);
        }
 
-       connection->ssl_func = ssl_function;
-       connection->ssl_func_data = user_data;
-}
-
-/**
- * lm_connection_get_use_ssl:
- * @connection: an #LmConnection
- * 
- * Returns if @connection is using SSL or not
- * 
- * Return value: #TRUE if @connection is using SSL, #FALSE otherwise.
- **/
-gboolean
-lm_connection_get_use_ssl (LmConnection *connection)
-{
-       g_return_val_if_fail (connection != NULL, FALSE);
-
-       return connection->ssl_func != NULL;
-}
-
-/**
- * lm_connection_get_fingerprint: 
- * @connection: an #LmConnection
- *
- * 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)
-{
-       g_return_val_if_fail (connection != NULL, NULL);
-       
-       return (unsigned char*) connection->fingerprint;
+       if (ssl) {
+               connection->ssl = lm_ssl_ref (ssl);
+       } else {
+               connection->ssl = NULL;
+       }
 }
 
 /**
index 9efd4b66dddecc96996dc96f3725b2594aead7f1..709cec4dd03a6d7f43baafe4ae90fc9f114fb75b 100644 (file)
@@ -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;
 
@@ -57,27 +58,6 @@ typedef enum {
        LM_DISCONNECT_REASON_UNKNOWN
 } 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,
@@ -94,11 +74,6 @@ typedef void          (* LmDisconnectFunction) (LmConnection       *connection,
                                                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,16 +113,11 @@ void          lm_connection_set_server        (LmConnection       *connection,
 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,
                                               LmProxy            *proxy);
index 1a9628b141ec69442f9ee41d44bafd7c9ac6f83a..d284201f0b18fa3e85610485d08f1f5d06cccd74 100644 (file)
@@ -52,6 +52,19 @@ gboolean         _lm_proxy_negotiate                (LmProxy          *proxy,
                                                     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,
diff --git a/loudmouth/lm-ssl.c b/loudmouth/lm-ssl.c
new file mode 100644 (file)
index 0000000..aca7771
--- /dev/null
@@ -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 <walters@gnome.org>
+ *
+ * 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
+
diff --git a/loudmouth/lm-ssl.h b/loudmouth/lm-ssl.h
new file mode 100644 (file)
index 0000000..b4c8b78
--- /dev/null
@@ -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 <sjoerd@luon.net>
+ *
+ * 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 91a0efa0e10b33fc8c67f8778a8733cc0541ba11..5480ebf0aed560a45c15d3194b8761ceaa73f35b 100644 (file)
@@ -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, unsigned int size)
 }
 
 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, LmSSLStatus status, gpointer ud)
                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);
        }
@@ -190,18 +199,24 @@ main (int argc, char **argv)
        info->passwd = g_strdup (argv[3]);
        
        if (argc > 4) {
-               int i;
-               char *p;
+               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);
+               
+               ssl = lm_ssl_new (expected_fingerprint,
+                                 (LmSSLFunction) ssl_cb,
+                                 info, 
+                                 (GDestroyNotify) free_user_info);
+               
+               lm_connection_set_ssl (connection, ssl);
 
-               lm_connection_set_use_ssl (connection,
-                                          expected_fingerprint,
-                                          (LmSSLFunction) ssl_cb,
-                                          info);
+               lm_ssl_unref (ssl);
        }
 
        result = lm_connection_open (connection,