From 7e5ab23cbb7b2d1e4023a8016abf4793e9606bbb Mon Sep 17 00:00:00 2001 From: Jakub Adam Date: Wed, 29 Jun 2016 06:40:12 +0000 Subject: [PATCH] ms-ice: legacy FINGERPRINT mode In order to preserve compatibility with clients which use custom CRC lookup table from [MS-ICE2], whenever a connectivity check request or reply is sent, an additional message is sent along. These two messages differ only in FINGERPRINT attribute - one uses regular CRC lookup table for calculation, the other uses the modified table. When a message is received and FINGERPRINT doesn't pass validation using regular CRC table, the receiver also tries to verify using the modified table. [MS-ICE2] 3.1.4.8.2 describes this procedure. The commit fixes compatibility with older MSOC and Lync clients. Differential Revision: https://phabricator.freedesktop.org/D1138 --- agent/conncheck.c | 55 +++++++++++++++++++++++++++++++++++++++++++++++++++---- stun/stunagent.c | 52 ++++++++++++++++++++++++++++++++++++++-------------- 2 files changed, 89 insertions(+), 18 deletions(-) diff --git a/agent/conncheck.c b/agent/conncheck.c index e913153..b7ae90a 100644 --- a/agent/conncheck.c +++ b/agent/conncheck.c @@ -57,6 +57,7 @@ #include "agent-priv.h" #include "conncheck.h" #include "discovery.h" +#include "stun/stun5389.h" #include "stun/usages/ice.h" #include "stun/usages/bind.h" #include "stun/usages/turn.h" @@ -778,7 +779,37 @@ static guint32 peer_reflexive_candidate_priority (NiceAgent *agent, return priority; } +static void ms_ice2_legacy_conncheck_send(StunMessage *msg, NiceSocket *sock, + const NiceAddress *remote_addr) +{ + uint32_t *fingerprint_attr; + uint32_t fingerprint_orig; + uint16_t fingerprint_len; + size_t buffer_len; + + fingerprint_attr = (uint32_t *)stun_message_find (msg, + STUN_ATTRIBUTE_FINGERPRINT, &fingerprint_len); + + if (fingerprint_attr == NULL) { + nice_debug ("FINGERPRINT not found."); + return; + } + + if (fingerprint_len != sizeof (fingerprint_orig)) { + nice_debug ("Unexpected FINGERPRINT length %u.", fingerprint_len); + return; + } + + memcpy (&fingerprint_orig, fingerprint_attr, sizeof (fingerprint_orig)); + buffer_len = stun_message_length (msg); + + *fingerprint_attr = stun_fingerprint (msg->buffer, buffer_len, TRUE); + + agent_socket_send (sock, remote_addr, buffer_len, (gchar *)msg->buffer); + + memcpy (fingerprint_attr, &fingerprint_orig, sizeof (fingerprint_orig)); +} /* * Timer callback that handles initiating and managing connectivity @@ -883,6 +914,11 @@ static gboolean priv_conn_keepalive_tick_unlocked (NiceAgent *agent) agent_socket_send (p->local->sockptr, &p->remote->addr, buf_len, (gchar *)p->keepalive.stun_buffer); + if (agent->compatibility == NICE_COMPATIBILITY_OC2007R2) { + ms_ice2_legacy_conncheck_send (&p->keepalive.stun_message, + p->local->sockptr, &p->remote->addr); + } + nice_debug ("Agent %p : stun_bind_keepalive for pair %p res %d.", agent, p, (int) buf_len); } else { @@ -2167,6 +2203,11 @@ int conn_check_send (NiceAgent *agent, CandidateCheckPair *pair) agent_socket_send (pair->sockptr, &pair->remote->addr, buffer_len, (gchar *)pair->stun_buffer); + if (agent->compatibility == NICE_COMPATIBILITY_OC2007R2) { + ms_ice2_legacy_conncheck_send (&pair->stun_message, pair->sockptr, + &pair->remote->addr); + } + timeout = stun_timer_remainder (&pair->timer); /* note: convert from milli to microseconds for g_time_val_add() */ g_get_current_time (&pair->next_tick); @@ -2372,12 +2413,15 @@ static gboolean priv_schedule_triggered_check (NiceAgent *agent, NiceStream *str * @param toaddr address to which reply is sent * @param socket the socket over which the request came * @param rbuf_len length of STUN message to send - * @param rbuf buffer containing the STUN message to send + * @param msg the STUN message to send * @param use_candidate whether the request had USE_CANDIDATE attribute * * @pre (rcand == NULL || nice_address_equal(rcand->addr, toaddr) == TRUE) */ -static void priv_reply_to_conn_check (NiceAgent *agent, NiceStream *stream, NiceComponent *component, NiceCandidate *lcand, NiceCandidate *rcand, const NiceAddress *toaddr, NiceSocket *sockptr, size_t rbuf_len, uint8_t *rbuf, gboolean use_candidate) +static void priv_reply_to_conn_check (NiceAgent *agent, NiceStream *stream, + NiceComponent *component, NiceCandidate *lcand, NiceCandidate *rcand, + const NiceAddress *toaddr, NiceSocket *sockptr, size_t rbuf_len, + StunMessage *msg, gboolean use_candidate) { g_assert (rcand == NULL || nice_address_equal(&rcand->addr, toaddr) == TRUE); @@ -2393,7 +2437,10 @@ static void priv_reply_to_conn_check (NiceAgent *agent, NiceStream *stream, Nice (int)use_candidate); } - agent_socket_send (sockptr, toaddr, rbuf_len, (const gchar*)rbuf); + agent_socket_send (sockptr, toaddr, rbuf_len, (const gchar*)msg->buffer); + if (agent->compatibility == NICE_COMPATIBILITY_OC2007R2) { + ms_ice2_legacy_conncheck_send(msg, sockptr, toaddr); + } if (rcand) { /* note: upon successful check, make the reserve check immediately */ @@ -3601,7 +3648,7 @@ gboolean conn_check_handle_inbound_stun (NiceAgent *agent, NiceStream *stream, } priv_reply_to_conn_check (agent, stream, component, local_candidate, - remote_candidate, from, nicesock, rbuf_len, rbuf, use_candidate); + remote_candidate, from, nicesock, rbuf_len, &msg, use_candidate); if (component->remote_candidates == NULL) { /* case: We've got a valid binding request to a local candidate diff --git a/stun/stunagent.c b/stun/stunagent.c index 461a8e3..cd97684 100644 --- a/stun/stunagent.c +++ b/stun/stunagent.c @@ -98,13 +98,48 @@ bool stun_agent_default_validater (StunAgent *agent, } +static bool stun_agent_check_fingerprint(StunAgent *agent, StunMessage *msg) +{ + uint32_t fpr; + uint32_t crc32; + uint16_t msg_len; + + /* Looks for FINGERPRINT */ + if (stun_message_find32 (msg, STUN_ATTRIBUTE_FINGERPRINT, &fpr) != + STUN_MESSAGE_RETURN_SUCCESS) { + stun_debug ("STUN demux error: no FINGERPRINT attribute!"); + return FALSE; + } + + msg_len = stun_message_length (msg); + + /* Checks FINGERPRINT */ + crc32 = stun_fingerprint (msg->buffer, msg_len, FALSE); + fpr = ntohl (fpr); + if (fpr != crc32) { + uint16_t palen; + + /* [MS-ICE2] 3.1.4.8.2 Connectivity Checks Phase - legacy compatibility */ + if (agent->compatibility == STUN_COMPATIBILITY_MSICE2 && + stun_message_find (msg, STUN_ATTRIBUTE_MS_IMPLEMENTATION_VERSION, + &palen) == NULL && + fpr == stun_fingerprint (msg->buffer, msg_len, TRUE)) { + return TRUE; + } + + stun_debug ("STUN demux error: bad fingerprint: 0x%08x, expected: 0x%08x!", + fpr, crc32); + return FALSE; + } + + return TRUE; +} + StunValidationStatus stun_agent_validate (StunAgent *agent, StunMessage *msg, const uint8_t *buffer, size_t buffer_len, StunMessageIntegrityValidate validater, void * validater_data) { StunTransactionId msg_id; - uint32_t fpr; - uint32_t crc32; int len; uint8_t *username = NULL; uint16_t username_len; @@ -148,18 +183,7 @@ StunValidationStatus stun_agent_validate (StunAgent *agent, StunMessage *msg, if ((agent->compatibility == STUN_COMPATIBILITY_RFC5389 || agent->compatibility == STUN_COMPATIBILITY_MSICE2) && agent->usage_flags & STUN_AGENT_USAGE_USE_FINGERPRINT) { - /* Looks for FINGERPRINT */ - if (stun_message_find32 (msg, STUN_ATTRIBUTE_FINGERPRINT, &fpr) != - STUN_MESSAGE_RETURN_SUCCESS) { - stun_debug ("STUN demux error: no FINGERPRINT attribute!"); - return STUN_VALIDATION_BAD_REQUEST; - } - /* Checks FINGERPRINT */ - crc32 = stun_fingerprint (msg->buffer, stun_message_length (msg), FALSE); - fpr = ntohl (fpr); - if (fpr != crc32) { - stun_debug ("STUN demux error: bad fingerprint: 0x%08x," - " expected: 0x%08x!", fpr, crc32); + if (stun_agent_check_fingerprint(agent, msg) == FALSE) { return STUN_VALIDATION_BAD_REQUEST; } -- 2.7.4