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 1.0.6 release
* fixed uTP vulnerability
* make utf8 conversions more lenient * make utf8 conversions more lenient
* fix loading of piece priorities from resume data * fix loading of piece priorities from resume data
* improved seed-mode handling (seed-mode will now automatically be left when * 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_min_timeout, 500, 0),
SET(utp_syn_resends, 2, 0), SET(utp_syn_resends, 2, 0),
SET(utp_fin_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_connect_timeout, 3000, 0),
SET(utp_delayed_ack, 0, 0), SET(utp_delayed_ack, 0, 0),
SET(utp_loss_multiplier, 50, 0), SET(utp_loss_multiplier, 50, 0),

View File

@ -280,6 +280,7 @@ struct utp_socket_impl
, m_deferred_ack(false) , m_deferred_ack(false)
, m_subscribe_drained(false) , m_subscribe_drained(false)
, m_stalled(false) , m_stalled(false)
, m_confirmed(false)
{ {
m_sm->inc_stats_counter(counters::num_utp_idle); m_sm->inc_stats_counter(counters::num_utp_idle);
TORRENT_ASSERT(m_userdata); TORRENT_ASSERT(m_userdata);
@ -405,7 +406,7 @@ public:
// refer to the unwritten portions of the buffers, and the // refer to the unwritten portions of the buffers, and the
// ones that fill up are erased from the vector // ones that fill up are erased from the vector
std::vector<iovec_t> m_read_buffer; std::vector<iovec_t> m_read_buffer;
// packets we've received without a read operation // packets we've received without a read operation
// active. Store them here until the client triggers // active. Store them here until the client triggers
// an async_read_some // an async_read_some
@ -491,7 +492,7 @@ public:
// the sum of all packets stored in m_receive_buffer // the sum of all packets stored in m_receive_buffer
boost::int32_t m_receive_buffer_size; boost::int32_t m_receive_buffer_size;
// the sum of all buffers in m_read_buffer // the sum of all buffers in m_read_buffer
boost::int32_t m_read_buffer_size; boost::int32_t m_read_buffer_size;
@ -675,6 +676,11 @@ public:
// of sockets in the utp_socket_manager to be notified of // of sockets in the utp_socket_manager to be notified of
// the socket being writable again // the socket being writable again
bool m_stalled:1; 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 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) if (m_state == UTP_STATE_SYN_SENT && ph->get_type() == ST_STATE)
cmp_seq_nr = m_seq_nr; cmp_seq_nr = m_seq_nr;
#endif #endif
if (m_state != UTP_STATE_NONE 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(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" UTP_LOG("%8p: ERROR: incoming packet ack_nr:%d our seq_nr:%d our "
, this, int(ph->ack_nr), m_seq_nr); "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); m_sm->inc_stats_counter(counters::utp_redundant_pkts_in);
return true; 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); , this, int(ph->ack_nr), m_seq_nr);
return true; 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); UTP_LOGV("%8p: incoming packet type:RESET\n", this);
m_error = boost::asio::error::connection_reset; m_error = boost::asio::error::connection_reset;
set_state(UTP_STATE_ERROR_WAIT); 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; ptr += len;
extension = next_extension; extension = next_extension;
} }
// the send operation in parse_sack() may have set the socket to an error // the send operation in parse_sack() may have set the socket to an error
// state, in which case we shouldn't continue // state, in which case we shouldn't continue
if (m_state == UTP_STATE_ERROR_WAIT || m_state == UTP_STATE_DELETE) return true; 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; 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; 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 // try to send more data as long as we can
// if send_pkt returns true // if send_pkt returns true
while (send_pkt()); while (send_pkt());
@ -3504,7 +3511,10 @@ void utp_socket_impl::tick(time_point now)
if (m_outbuf.size()) ++m_num_timeouts; 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 // the connection is dead
m_error = boost::asio::error::timed_out; m_error = boost::asio::error::timed_out;
@ -3541,7 +3551,7 @@ void utp_socket_impl::tick(time_point now)
TORRENT_ASSERT(m_cwnd >= 0); TORRENT_ASSERT(m_cwnd >= 0);
m_timeout = now + milliseconds(packet_timeout()); m_timeout = now + milliseconds(packet_timeout());
UTP_LOGV("%8p: timeout resetting cwnd:%d\n" UTP_LOGV("%8p: timeout resetting cwnd:%d\n"
, this, int(m_cwnd >> 16)); , this, int(m_cwnd >> 16));