+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:
lm-internals.h \
lm-sha.c \
lm-sha.h \
+ lm-ssl.c \
lm-utils.c \
lm-proxy.c \
lm-queue.c \
lm-message-node.h \
lm-utils.h \
lm-proxy.h \
+ lm-ssl.h \
loudmouth.h \
$(NULL)
#include <config.h>
-#ifdef HAVE_GNUTLS
-#include <gnutls/gnutls.h>
-#endif
-
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
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;
LmCallback *disconnect_cb;
- LmSSLFunction ssl_func;
- gpointer ssl_func_data;
- gchar *expected_fingerprint;
-
LmQueue *incoming_messages;
GSource *incoming_source;
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);
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)
{
* 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);
(GIOFunc) connection_hup_event,
connection);
-
if (!connection_send (connection,
"<?xml version='1.0' encoding='UTF-8'?>", -1,
NULL)) {
}
}
-
- connection_initilize_gnutls (connection);
+ if (connection->ssl) {
+ _lm_ssl_initialize (connection->ssl);
+ }
/* Prepare and do the nonblocking connection */
data = g_new (LmConnectData, 1);
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
}
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;
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;
}
}
}
-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.
}
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 ();
}
/**
- * 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;
+ }
}
/**
#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;
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,
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,
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);
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,
--- /dev/null
+/* -*- 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
+
--- /dev/null
+/* -*- 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__ */
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
}
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) {
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);
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);
}
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,