Adding StartTLS support.
--- a/loudmouth/lm-connection.c Wed Sep 05 16:03:24 2007 +0100
+++ b/loudmouth/lm-connection.c Thu Sep 06 15:20:04 2007 +0200
@@ -71,11 +71,16 @@
GHashTable *id_handlers;
GSList *handlers[LM_MESSAGE_TYPE_UNKNOWN];
- /* XMPP1.0 stuff (SASL, resource binding) */
+ /* XMPP1.0 stuff (SASL, resource binding, StartTLS) */
gboolean use_xmpp;
LmSASL *sasl;
gchar *resource;
LmMessageHandler *features_cb;
+ gboolean use_starttls;
+ LmMessageHandler *starttls_cb;
+ LmSSLFunction ssl_fail_cb;
+ gpointer ssl_fail_cb_data;
+ gboolean require_starttls;
/* Communication */
guint open_id;
@@ -107,6 +112,7 @@
#define XMPP_NS_BIND "urn:ietf:params:xml:ns:xmpp-bind"
#define XMPP_NS_SESSION "urn:ietf:params:xml:ns:xmpp-session"
+#define XMPP_NS_STARTTLS "urn:ietf:params:xml:ns:xmpp-tls"
static void connection_free (LmConnection *connection);
@@ -699,7 +705,10 @@
connection->use_xmpp = TRUE;
- connection->sasl = lm_sasl_new (connection);
+ /* stream is started multiple times, but we only want
+ * one sasl mechanism */
+ if (!connection->sasl)
+ connection->sasl = lm_sasl_new(connection);
} else {
lm_verbose ("Old Jabber stream received: %s\n",
connection->stream_id);
@@ -885,6 +894,16 @@
}
}
+static LmSSLResponse
+connection_tls_error (LmSSL *ssl,
+ LmSSLStatus status,
+ gpointer user_data)
+{
+ LmConnection *conn = (LmConnection *) user_data;
+
+ return conn->ssl_fail_cb (ssl, status, conn->ssl_fail_cb_data);
+}
+
static LmHandlerResult
connection_bind_reply (LmMessageHandler *handler,
LmConnection *connection,
@@ -927,13 +946,57 @@
}
static LmHandlerResult
+_lm_connection_starttls_cb (LmMessageHandler *handler,
+ LmConnection *connection,
+ LmMessage *message,
+ gpointer user_data)
+{
+ connection->ssl = lm_ssl_new (NULL, connection_tls_error, connection, NULL);
+ lm_ssl_ref (connection->ssl);
+ if (lm_socket_starttls (connection->socket, connection->ssl)) {
+ connection_send_stream_header (connection);
+ } else {
+ connection_do_close (connection);
+ connection_signal_disconnect (connection,
+ LM_DISCONNECT_REASON_ERROR);
+ }
+
+ return LM_HANDLER_RESULT_REMOVE_MESSAGE;
+}
+
+static LmHandlerResult
connection_features_cb (LmMessageHandler *handler,
LmConnection *connection,
LmMessage *message,
gpointer user_data)
{
LmMessageNode *bind_node;
+ LmMessageNode *starttls_node;
+ starttls_node = lm_message_node_find_child (message->node, "starttls");
+ if (connection->use_starttls && starttls_node) {
+ LmMessage *msg;
+
+ msg = lm_message_new (NULL, LM_MESSAGE_TYPE_STARTTLS);
+
+ lm_message_node_set_attributes (msg->node,
+ "xmlns", XMPP_NS_STARTTLS,
+ NULL);
+
+ lm_connection_send (connection, msg, NULL);
+ lm_message_unref (msg);
+
+ return LM_HANDLER_RESULT_REMOVE_MESSAGE;
+ } else if (!connection->ssl && connection->require_starttls) {
+ /* If we don't have ssl set up already, and there
+ * were no starttls features present, and we do
+ * require it, this is the place to scream */
+
+ g_debug ("%s: required StartTLS feature not supported by server", G_STRFUNC);
+ connection_do_close (connection);
+ return LM_HANDLER_RESULT_REMOVE_MESSAGE;
+ }
+
bind_node = lm_message_node_find_child (message->node, "bind");
if (bind_node) {
LmMessageHandler *bind_handler;
@@ -1287,6 +1350,16 @@
LM_MESSAGE_TYPE_STREAM_FEATURES,
LM_HANDLER_PRIORITY_FIRST);
+ if (connection->use_starttls) {
+ connection->starttls_cb =
+ lm_message_handler_new (_lm_connection_starttls_cb,
+ NULL, NULL);
+ lm_connection_register_message_handler (connection,
+ connection->starttls_cb,
+ LM_MESSAGE_TYPE_PROCEED,
+ LM_HANDLER_PRIORITY_FIRST);
+ }
+
return TRUE;
}
@@ -1963,3 +2036,19 @@
}
}
+/*
+ * lm_connection_use_startls:
+ * @connection: Connection to use STARTTLS on
+ */
+void
+lm_connection_use_starttls (LmConnection *connection,
+ gboolean require,
+ LmSSLFunction ssl_fail_cb,
+ gpointer user_data)
+{
+ connection->use_starttls = TRUE;
+ connection->ssl_fail_cb = ssl_fail_cb;
+ connection->ssl_fail_cb_data = user_data;
+ connection->require_starttls = require;
+}
+
--- a/loudmouth/lm-connection.h Wed Sep 05 16:03:24 2007 +0100
+++ b/loudmouth/lm-connection.h Thu Sep 06 15:20:04 2007 +0200
@@ -159,6 +159,10 @@
LmConnectionState lm_connection_get_state (LmConnection *connection);
LmConnection* lm_connection_ref (LmConnection *connection);
void lm_connection_unref (LmConnection *connection);
+void lm_connection_use_starttls (LmConnection *connection,
+ gboolean require,
+ LmSSLFunction ssl_fail_cb,
+ gpointer user_data);
G_END_DECLS
--- a/loudmouth/lm-message.c Wed Sep 05 16:03:24 2007 +0100
+++ b/loudmouth/lm-message.c Thu Sep 06 15:20:04 2007 +0200
@@ -41,7 +41,9 @@
{ LM_MESSAGE_TYPE_RESPONSE, "response" },
{ LM_MESSAGE_TYPE_SUCCESS, "success" },
{ LM_MESSAGE_TYPE_FAILURE, "failure" },
- { LM_MESSAGE_TYPE_STREAM_ERROR, "stream:error" }
+ { LM_MESSAGE_TYPE_STREAM_ERROR, "stream:error" },
+ { LM_MESSAGE_TYPE_PROCEED, "proceed" },
+ { LM_MESSAGE_TYPE_STARTTLS, "starttls" }
};
static struct SubTypeNames
@@ -81,7 +83,7 @@
}
for (i = LM_MESSAGE_TYPE_MESSAGE;
- i <= LM_MESSAGE_TYPE_STREAM_ERROR;
+ i <= LM_MESSAGE_TYPE_STARTTLS;
++i) {
if (strcmp (type_str, type_names[i].name) == 0) {
return i;
@@ -96,7 +98,7 @@
_lm_message_type_to_string (LmMessageType type)
{
if (type < LM_MESSAGE_TYPE_MESSAGE ||
- type > LM_MESSAGE_TYPE_STREAM_ERROR) {
+ type > LM_MESSAGE_TYPE_STARTTLS) {
type = LM_MESSAGE_TYPE_UNKNOWN;
}
--- a/loudmouth/lm-message.h Wed Sep 05 16:03:24 2007 +0100
+++ b/loudmouth/lm-message.h Thu Sep 06 15:20:04 2007 +0200
@@ -49,6 +49,8 @@
LM_MESSAGE_TYPE_SUCCESS,
LM_MESSAGE_TYPE_FAILURE,
LM_MESSAGE_TYPE_STREAM_ERROR,
+ LM_MESSAGE_TYPE_PROCEED,
+ LM_MESSAGE_TYPE_STARTTLS,
LM_MESSAGE_TYPE_UNKNOWN
} LmMessageType;
--- a/loudmouth/lm-socket.c Wed Sep 05 16:03:24 2007 +0100
+++ b/loudmouth/lm-socket.c Thu Sep 06 15:20:04 2007 +0200
@@ -51,6 +51,7 @@
LmConnection *connection;
GMainContext *context;
+ gchar *domain;
gchar *server;
guint port;
@@ -118,6 +119,7 @@
socket_free (LmSocket *socket)
{
g_free (socket->server);
+ g_free (socket->domain);
if (socket->ssl) {
lm_ssl_unref (socket->ssl);
@@ -291,6 +293,58 @@
return TRUE;
}
+static gboolean
+_lm_socket_ssl_init (LmSocket *socket, gboolean delayed)
+{
+ GError *error = NULL;
+
+ lm_verbose ("Setting up SSL...\n");
+
+ _lm_ssl_initialize (socket->ssl);
+
+#ifdef HAVE_GNUTLS
+ /* GNU TLS requires the socket to be blocking */
+ _lm_sock_set_blocking (socket->fd, TRUE);
+#endif
+
+ if (!_lm_ssl_begin (socket->ssl, socket->fd, socket->domain, &error)) {
+ lm_verbose ("Could not begin SSL\n");
+
+ if (error) {
+ g_log (LM_LOG_DOMAIN, LM_LOG_LEVEL_NET,
+ "%s\n", error->message);
+ g_error_free (error);
+ }
+
+ _lm_sock_shutdown (socket->fd);
+ _lm_sock_close (socket->fd);
+
+ if (!delayed)
+ (socket->connect_func) (socket, FALSE, socket->user_data);
+
+ return FALSE;
+ }
+
+#ifdef HAVE_GNUTLS
+ _lm_sock_set_blocking (socket->fd, FALSE);
+#endif
+
+ return TRUE;
+}
+
+gboolean
+lm_socket_starttls (LmSocket *socket, LmSSL *ssl)
+{
+ /* no-op if we're using old-style ssl (so ssl is set up already) */
+ if (socket->ssl)
+ return TRUE;
+
+ socket->ssl = ssl;
+ return _lm_socket_ssl_init (socket, TRUE);
+}
+
+
+
void
_lm_socket_succeeded (LmConnectData *connect_data)
{
@@ -317,38 +371,10 @@
socket->connect_data = NULL;
g_free (connect_data);
+ /* old-style ssl should be started immediately */
if (socket->ssl) {
- GError *error = NULL;
-
- lm_verbose ("Setting up SSL...\n");
-
-#ifdef HAVE_GNUTLS
- /* GNU TLS requires the socket to be blocking */
- _lm_sock_set_blocking (socket->fd, TRUE);
-#endif
-
- if (!_lm_ssl_begin (socket->ssl, socket->fd,
- socket->server,
- &error)) {
- lm_verbose ("Could not begin SSL\n");
-
- if (error) {
- g_log (LM_LOG_DOMAIN, LM_LOG_LEVEL_NET,
- "%s\n", error->message);
- g_error_free (error);
- }
-
- _lm_sock_shutdown (socket->fd);
- _lm_sock_close (socket->fd);
-
- (socket->connect_func) (socket, FALSE,
- socket->user_data);
+ if (!_lm_socket_ssl_init (socket, FALSE))
return;
- }
-
-#ifdef HAVE_GNUTLS
- _lm_sock_set_blocking (socket->fd, FALSE);
-#endif
}
socket->watch_in =
@@ -936,7 +962,7 @@
#ifndef HAVE_ASYNCNS
unsigned char srv_ans[SRV_LEN];
- int err;
+ int len;
#endif
g_return_val_if_fail (server != NULL, NULL);
@@ -950,6 +976,7 @@
socket->ref_count = 1;
socket->connection = connection;
+ socket->domain = g_strdup (server);
socket->server = g_strdup (server);
socket->port = port;
socket->use_srv = use_srv;
@@ -991,8 +1018,8 @@
#else
res_init ();
- err = res_query (srv, C_IN, T_SRV, srv_ans, SRV_LEN);
- _lm_socket_create_phase1 (socket, (err) ? NULL : srv_ans, err);
+ len = res_query (srv, C_IN, T_SRV, srv_ans, SRV_LEN);
+ _lm_socket_create_phase1 (socket, (len < 1) ? NULL : srv_ans, len);
g_free (srv);
#endif
} else {
--- a/loudmouth/lm-socket.h Wed Sep 05 16:03:24 2007 +0100
+++ b/loudmouth/lm-socket.h Thu Sep 06 15:20:04 2007 +0200
@@ -69,6 +69,7 @@
#ifdef HAVE_ASYNCNS
void _asyncns_cancel (LmSocket *socket);
#endif
+gboolean lm_socket_starttls (LmSocket *socket, LmSSL *ssl);
#endif /* __LM_SOCKET_H__ */
--- a/loudmouth/lm-ssl-openssl.c Wed Sep 05 16:03:24 2007 +0100
+++ b/loudmouth/lm-ssl-openssl.c Thu Sep 06 15:20:04 2007 +0200
@@ -206,17 +206,23 @@
cn = (gchar *) g_malloc0(LM_SSL_CN_MAX + 1);
if (X509_NAME_get_text_by_NID(crt_subj, NID_commonName, cn, LM_SSL_CN_MAX) > 0) {
+ gchar *domain = cn;
+
g_log (LM_LOG_DOMAIN, LM_LOG_LEVEL_SSL,
"%s: server = '%s', cn = '%s'\n",
__FILE__, server, cn);
- if (strncmp (server, cn, LM_SSL_CN_MAX) != 0) {
+ if ((cn[0] == '*') && (cn[1] == '.')) {
+ domain = strstr (cn, server);
+ }
+
+ if ((domain == NULL) || (strncmp (server, domain, LM_SSL_CN_MAX) != 0)) {
if (base->func (ssl,
LM_SSL_STATUS_CERT_HOSTNAME_MISMATCH,
base->func_data) != LM_SSL_RESPONSE_CONTINUE) {
retval = FALSE;
}
- }
+ }
} else {
g_warning ("X509_NAME_get_text_by_NID() failed");
}