Adding StartTLS support.
authorSenko Rasic <senko@phyrexia.(none)>
Thu, 06 Sep 2007 15:20:04 +0200
changeset 274 c32a7011e435
parent 273 6be9056c1fb3
child 275 37937c612f62
Adding StartTLS support.
loudmouth/lm-connection.c
loudmouth/lm-connection.h
loudmouth/lm-message.c
loudmouth/lm-message.h
loudmouth/lm-socket.c
loudmouth/lm-socket.h
loudmouth/lm-ssl-openssl.c
--- 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");
 	}