# HG changeset patch # User Elliott Sales de Andrade # Date 1272062293 0 # Node ID 4e532eedcab47ca81a8c7cdb30b926f191974303 # Parent 31f20c9c7674c4f1a38e984e39355568b7d795a2 Save remote nonce, and verify we get the same data when initiating the direct connection. Refs #247. diff -r 31f20c9c7674 -r 4e532eedcab4 libpurple/protocols/msn/directconn.c --- 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); diff -r 31f20c9c7674 -r 4e532eedcab4 libpurple/protocols/msn/directconn.h --- 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 */ diff -r 31f20c9c7674 -r 4e532eedcab4 libpurple/protocols/msn/slp.c --- 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"))