changeset 30037:4e532eedcab4

Save remote nonce, and verify we get the same data when initiating the direct connection. Refs #247.
author Elliott Sales de Andrade <qulogic@pidgin.im>
date Fri, 23 Apr 2010 22:38:13 +0000
parents 31f20c9c7674
children 1664d74b2e69
files libpurple/protocols/msn/directconn.c libpurple/protocols/msn/directconn.h libpurple/protocols/msn/slp.c
diffstat 3 files changed, 70 insertions(+), 7 deletions(-) [+]
line wrap: on
line diff
--- a/libpurple/protocols/msn/directconn.c	Fri Apr 23 22:19:23 2010 +0000
+++ b/libpurple/protocols/msn/directconn.c	Fri Apr 23 22:38:13 2010 +0000
@@ -76,6 +76,9 @@
 	          GUINT32_FROM_BE(*((guint32 *)(digest + 10))),
 	          GUINT16_FROM_BE(*((guint16 *)(digest + 14)))
 	);
+
+	if (purple_debug_is_verbose())
+		purple_debug_info("msn", "DC %p generated nonce %s\n", dc, dc->nonce_hash);
 }
 
 static MsnDirectConnPacket *
@@ -682,6 +685,53 @@
 	msn_dc_enqueue_packet(dc, p);
 }
 
+static gboolean
+msn_dc_verify_handshake(MsnDirectConn *dc, guint32 packet_length)
+{
+	PurpleCipherContext *context;
+	PurpleCipher *cipher;
+	guchar nonce[16];
+	guchar digest[20];
+	gchar  nonce_hash[37];
+
+	if (packet_length != DC_PACKET_HEADER_SIZE)
+		return FALSE;
+
+	memcpy(nonce, dc->in_buffer + 4 + offsetof(MsnDcContext, ack_id), 16);
+
+	cipher = purple_ciphers_find_cipher("sha1");
+	context = purple_cipher_context_new(cipher, NULL);
+	purple_cipher_context_append(context, nonce, sizeof(nonce));
+	purple_cipher_context_digest(context, sizeof(digest), digest, NULL);
+	purple_cipher_context_destroy(context);
+
+	g_sprintf(nonce_hash,
+	          "%08X-%04X-%04X-%04X-%08X%04X",
+	          GUINT32_FROM_LE(*((guint32 *)(digest + 0))),
+	          GUINT16_FROM_LE(*((guint16 *)(digest + 4))),
+	          GUINT16_FROM_LE(*((guint16 *)(digest + 6))),
+	          GUINT16_FROM_BE(*((guint16 *)(digest + 8))),
+	          GUINT32_FROM_BE(*((guint32 *)(digest + 10))),
+	          GUINT16_FROM_BE(*((guint16 *)(digest + 14)))
+	);
+
+	if (g_str_equal(dc->remote_nonce, nonce_hash)) {
+		purple_debug_info("msn",
+				"Received nonce %s from buddy request "
+				"and calculated nonce %s from DC attempt. "
+				"Nonces match, allowing direct connection\n",
+				dc->remote_nonce, nonce_hash);
+		return TRUE;
+	} else {
+		purple_debug_warning("msn",
+				"Received nonce %s from buddy request "
+				"and calculated nonce %s from DC attempt. "
+				"Nonces don't match, ignoring direct connection\n",
+				dc->remote_nonce, nonce_hash);
+		return TRUE;
+	}
+}
+
 static void
 msn_dc_send_packet_cb(MsnDirectConnPacket *p)
 {
@@ -727,10 +777,9 @@
 		break;
 
 	case DC_STATE_HANDSHAKE:
-		if (packet_length != DC_PACKET_HEADER_SIZE)
+		if (!msn_dc_verify_handshake(dc, packet_length))
 			return DC_PROCESS_FALLBACK;
 
-		/* TODO: Check! */
 		msn_dc_send_handshake_reply(dc);
 		dc->state = DC_STATE_ESTABLISHED;
 
@@ -739,7 +788,9 @@
 		break;
 
 	case DC_STATE_HANDSHAKE_REPLY:
-		/* TODO: Check! */
+		if (!msn_dc_verify_handshake(dc, packet_length))
+			return DC_PROCESS_FALLBACK;
+
 		dc->state = DC_STATE_ESTABLISHED;
 
 		msn_slpcall_session_init(dc->slpcall);
--- a/libpurple/protocols/msn/directconn.h	Fri Apr 23 22:19:23 2010 +0000
+++ b/libpurple/protocols/msn/directconn.h	Fri Apr 23 22:38:13 2010 +0000
@@ -71,8 +71,9 @@
 	char                *msg_body;  /**< The body of message sent by send_connection_info_msg_cb */
 	MsnSlpMessage       *prev_ack;  /**< The saved SLP ACK message */
 
-	guchar  nonce[16];      /**< The nonce used for direct connection handshake */
-	gchar   nonce_hash[37]; /**< The hash of nonce */
+	guchar  nonce[16];          /**< The nonce used for direct connection handshake */
+	gchar   nonce_hash[37];     /**< The hash of nonce */
+	gchar   remote_nonce[37];   /**< The remote side's nonce */
 
 	PurpleNetworkListenData *listen_data;           /**< The pending socket creation request */
 	PurpleProxyConnectData  *connect_data;          /**< The pending connection attempt */
--- a/libpurple/protocols/msn/slp.c	Fri Apr 23 22:19:23 2010 +0000
+++ b/libpurple/protocols/msn/slp.c	Fri Apr 23 22:38:13 2010 +0000
@@ -281,6 +281,7 @@
 {
 	/* A direct connection negotiation response */
 	char *bridge;
+	char *nonce;
 	MsnDirectConn *dc = slpcall->slplink->dc;
 	gboolean result = FALSE;
 
@@ -290,9 +291,13 @@
 	g_return_val_if_fail(dc->state == DC_STATE_CLOSED, FALSE);
 
 	bridge = get_token(content, "Bridge: ", "\r\n");
-	if (bridge && !strcmp(bridge, "TCPv1")) {
+	nonce = get_token(content, "Hashed-Nonce: {", "}\r\n");
+	if (nonce && bridge && !strcmp(bridge, "TCPv1")) {
 		/* Ok, the client supports direct TCP connection */
 
+		strncpy(dc->remote_nonce, nonce, 36);
+		dc->remote_nonce[36] = '\0';
+
 		if (dc->listen_data != NULL || dc->listenfd != -1) {
 			if (dc->listen_data != NULL) {
 				/*
@@ -377,6 +382,7 @@
 		 */
 	}
 
+	g_free(nonce);
 	g_free(bridge);
 
 	return result;
@@ -624,6 +630,7 @@
 	{
 		/* A direct connection negotiation request */
 		char *bridges;
+		char *nonce;
 
 		purple_debug_info("msn", "got_invite: transreqbody received\n");
 
@@ -632,7 +639,8 @@
 			return;
 
 		bridges = get_token(content, "Bridges: ", "\r\n");
-		if (bridges && strstr(bridges, "TCPv1") != NULL) {
+		nonce = get_token(content, "Hashed-Nonce: {", "}\r\n");
+		if (nonce && bridges && strstr(bridges, "TCPv1") != NULL) {
 			/*
 			 * Ok, the client supports direct TCP connection
 			 * Try to create a listening port
@@ -640,6 +648,8 @@
 			MsnDirectConn *dc;
 
 			dc = msn_dc_new(slpcall);
+			strncpy(dc->remote_nonce, nonce, 36);
+			dc->remote_nonce[36] = '\0';
 
 			dc->listen_data = purple_network_listen_range(
 				0, 0,
@@ -680,6 +690,7 @@
 			 */
 		}
 
+		g_free(nonce);
 		g_free(bridges);
 	}
 	else if (!strcmp(type, "application/x-msnmsgr-transrespbody"))