fixed uTP vulnerability

This commit is contained in:
arvidn 2015-07-18 21:55:26 -04:00
parent 2fee484edd
commit 4d89c7da97
3 changed files with 27 additions and 16 deletions

View File

@ -74,6 +74,7 @@
1.0.6 release
* fixed uTP vulnerability
* make utf8 conversions more lenient
* fix loading of piece priorities from resume data
* improved seed-mode handling (seed-mode will now automatically be left when

View File

@ -308,7 +308,7 @@ namespace libtorrent
SET(utp_min_timeout, 500, 0),
SET(utp_syn_resends, 2, 0),
SET(utp_fin_resends, 2, 0),
SET(utp_num_resends, 6, 0),
SET(utp_num_resends, 3, 0),
SET(utp_connect_timeout, 3000, 0),
SET(utp_delayed_ack, 0, 0),
SET(utp_loss_multiplier, 50, 0),

View File

@ -280,6 +280,7 @@ struct utp_socket_impl
, m_deferred_ack(false)
, m_subscribe_drained(false)
, m_stalled(false)
, m_confirmed(false)
{
m_sm->inc_stats_counter(counters::num_utp_idle);
TORRENT_ASSERT(m_userdata);
@ -405,7 +406,7 @@ public:
// refer to the unwritten portions of the buffers, and the
// ones that fill up are erased from the vector
std::vector<iovec_t> m_read_buffer;
// packets we've received without a read operation
// active. Store them here until the client triggers
// an async_read_some
@ -491,7 +492,7 @@ public:
// the sum of all packets stored in m_receive_buffer
boost::int32_t m_receive_buffer_size;
// the sum of all buffers in m_read_buffer
boost::int32_t m_read_buffer_size;
@ -675,6 +676,11 @@ public:
// of sockets in the utp_socket_manager to be notified of
// the socket being writable again
bool m_stalled:1;
// this is false by default and set to true once we've received a non-SYN
// packet for this connection with a correct ack_nr, confirming that the
// other end is not spoofing its source IP
bool m_confirmed:1;
};
utp_socket_impl* construct_utp_impl(boost::uint16_t recv_id
@ -2736,11 +2742,14 @@ bool utp_socket_impl::incoming_packet(boost::uint8_t const* buf, int size
if (m_state == UTP_STATE_SYN_SENT && ph->get_type() == ST_STATE)
cmp_seq_nr = m_seq_nr;
#endif
if (m_state != UTP_STATE_NONE
&& compare_less_wrap(cmp_seq_nr, ph->ack_nr, ACK_MASK))
if ((m_state != UTP_STATE_NONE || ph->get_type() != ST_SYN)
&& (compare_less_wrap(cmp_seq_nr, ph->ack_nr, ACK_MASK)
|| compare_less_wrap(ph->ack_nr, m_acked_seq_nr
- dup_ack_limit, ACK_MASK)))
{
UTP_LOG("%8p: ERROR: incoming packet ack_nr:%d our seq_nr:%d (ignored)\n"
, this, int(ph->ack_nr), m_seq_nr);
UTP_LOG("%8p: ERROR: incoming packet ack_nr:%d our seq_nr:%d our "
"acked_seq_nr:%d (ignored)\n"
, this, int(ph->ack_nr), m_seq_nr, m_acked_seq_nr);
m_sm->inc_stats_counter(counters::utp_redundant_pkts_in);
return true;
}
@ -2805,12 +2814,6 @@ bool utp_socket_impl::incoming_packet(boost::uint8_t const* buf, int size
, this, int(ph->ack_nr), m_seq_nr);
return true;
}
if (compare_less_wrap(ph->ack_nr, m_acked_seq_nr , ACK_MASK))
{
UTP_LOG("%8p: ERROR: invalid RESET packet, ack_nr:%d our acked_seq_nr:%d (ignored)\n"
, this, int(ph->ack_nr), m_acked_seq_nr);
return true;
}
UTP_LOGV("%8p: incoming packet type:RESET\n", this);
m_error = boost::asio::error::connection_reset;
set_state(UTP_STATE_ERROR_WAIT);
@ -2927,7 +2930,7 @@ bool utp_socket_impl::incoming_packet(boost::uint8_t const* buf, int size
ptr += len;
extension = next_extension;
}
// the send operation in parse_sack() may have set the socket to an error
// state, in which case we shouldn't continue
if (m_state == UTP_STATE_ERROR_WAIT || m_state == UTP_STATE_DELETE) return true;
@ -3118,6 +3121,10 @@ bool utp_socket_impl::incoming_packet(boost::uint8_t const* buf, int size
bool has_ack = ph->get_type() == ST_DATA || ph->get_type() == ST_FIN || ph->get_type() == ST_SYN;
boost::uint32_t prev_out_packets = m_out_packets;
// the connection is connected and this packet made it past all the
// checks. We can now assume the other end is not spoofing it's IP.
if (ph->get_type() != ST_SYN) m_confirmed = true;
// try to send more data as long as we can
// if send_pkt returns true
while (send_pkt());
@ -3504,7 +3511,10 @@ void utp_socket_impl::tick(time_point now)
if (m_outbuf.size()) ++m_num_timeouts;
if (m_num_timeouts > m_sm->num_resends())
// a socket that has not been confirmed to actually have a live remote end
// (the IP may have been spoofed) fail on the first timeout. If we had
// heard anything from this peer, it would have been confirmed.
if (m_num_timeouts > m_sm->num_resends() || !m_confirmed)
{
// the connection is dead
m_error = boost::asio::error::timed_out;
@ -3541,7 +3551,7 @@ void utp_socket_impl::tick(time_point now)
TORRENT_ASSERT(m_cwnd >= 0);
m_timeout = now + milliseconds(packet_timeout());
UTP_LOGV("%8p: timeout resetting cwnd:%d\n"
, this, int(m_cwnd >> 16));