Skip to content

Commit

Permalink
[core] Added handshake data check to prevent rogue handshakes (#1781)
Browse files Browse the repository at this point in the history
  • Loading branch information
ethouris authored Feb 3, 2021
1 parent 524565f commit 7d99b80
Show file tree
Hide file tree
Showing 5 changed files with 111 additions and 64 deletions.
5 changes: 3 additions & 2 deletions srtcore/api.h
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,9 @@ friend class CRendezvousQueue;
CUDTUnited();
~CUDTUnited();

// Public constants
static const int32_t MAX_SOCKET_VAL = 1 << 29; // maximum value for a regular socket

public:

enum ErrorHandling { ERH_RETURN, ERH_THROW, ERH_ABORT };
Expand Down Expand Up @@ -346,8 +349,6 @@ friend class CRendezvousQueue;

srt::sync::Mutex m_IDLock; // used to synchronize ID generation

static const int32_t MAX_SOCKET_VAL = 1 << 29; // maximum value for a regular socket

SRTSOCKET m_SocketIDGenerator; // seed to generate a new unique socket ID
SRTSOCKET m_SocketIDGenerator_init; // Keeps track of the very first one

Expand Down
52 changes: 42 additions & 10 deletions srtcore/core.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,14 @@ CUDT::CUDT(CUDTSocket* parent): m_parent(parent)

m_pCache = NULL;

// This is in order to set it ANY kind of initial value, however
// this value should not be used when not connected and should be
// updated in the handshake. When this value is 0, it means that
// packets shall not be sent, as the other party doesn't have a
// room to receive and store it. Therefore this value should be
// overridden before any sending happens.
m_iFlowWindowSize = 0;

// Default congctl is "live".
// Available builtin congctl: "file".
// Other congctls can be registerred.
Expand Down Expand Up @@ -4522,7 +4530,11 @@ EConnectStatus CUDT::processRendezvous(
m_ConnReq.m_extension = needs_extension;

// This must be done before prepareConnectionObjects().
applyResponseSettings();
if (!applyResponseSettings())
{
LOGC(cnlog.Error, log << "processRendezvous: rogue peer");
return CONN_REJECT;
}

// This must be done before interpreting and creating HSv5 extensions.
if (!prepareConnectionObjects(m_ConnRes, m_SrtHsSide, 0))
Expand Down Expand Up @@ -4966,8 +4978,15 @@ EConnectStatus CUDT::processConnectResponse(const CPacket& response, CUDTExcepti
return postConnect(response, false, eout);
}

void CUDT::applyResponseSettings() ATR_NOEXCEPT
bool CUDT::applyResponseSettings() ATR_NOEXCEPT
{
if (!m_ConnRes.valid())
{
LOGC(cnlog.Error, log << "applyResponseSettings: ROGUE HANDSHAKE - rejecting");
m_RejectReason = SRT_REJ_ROGUE;
return false;
}

// Re-configure according to the negotiated values.
m_iMSS = m_ConnRes.m_iMSS;
m_iFlowWindowSize = m_ConnRes.m_iFlightFlagSize;
Expand All @@ -4977,14 +4996,16 @@ void CUDT::applyResponseSettings() ATR_NOEXCEPT

setInitialRcvSeq(m_iPeerISN);

m_iRcvCurrPhySeqNo = m_ConnRes.m_iISN - 1;
m_iRcvCurrPhySeqNo = CSeqNo::decseq(m_ConnRes.m_iISN);
m_PeerID = m_ConnRes.m_iID;
memcpy((m_piSelfIP), m_ConnRes.m_piPeerIP, sizeof m_piSelfIP);

HLOGC(cnlog.Debug,
log << CONID() << "applyResponseSettings: HANSHAKE CONCLUDED. SETTING: payload-size=" << m_iMaxSRTPayloadSize
<< " mss=" << m_ConnRes.m_iMSS << " flw=" << m_ConnRes.m_iFlightFlagSize << " isn=" << m_ConnRes.m_iISN
<< " peerID=" << m_ConnRes.m_iID);

return true;
}

EConnectStatus CUDT::postConnect(const CPacket &response, bool rendezvous, CUDTException *eout) ATR_NOEXCEPT
Expand All @@ -5007,24 +5028,28 @@ EConnectStatus CUDT::postConnect(const CPacket &response, bool rendezvous, CUDTE
//
// Currently just this function must be called always BEFORE prepareConnectionObjects
// everywhere except acceptAndRespond().
applyResponseSettings();
bool ok = applyResponseSettings();

// This will actually be done also in rendezvous HSv4,
// however in this case the HSREQ extension will not be attached,
// so it will simply go the "old way".
bool ok = prepareConnectionObjects(m_ConnRes, m_SrtHsSide, eout);
// (&&: skip if failed already)
ok = ok && prepareConnectionObjects(m_ConnRes, m_SrtHsSide, eout);

// May happen that 'response' contains a data packet that was sent in rendezvous mode.
// In this situation the interpretation of handshake was already done earlier.
if (ok && response.isControl())
ok = ok && response.isControl();
ok = ok && interpretSrtHandshake(m_ConnRes, response, 0, 0);

if (!ok)
{
ok = interpretSrtHandshake(m_ConnRes, response, 0, 0);
if (!ok && eout)
if (eout)
{
*eout = CUDTException(MJ_SETUP, MN_REJECTED, 0);
}
}
if (!ok) // m_RejectReason already set
// m_RejectReason already set
return CONN_REJECT;
}
}

bool have_group = false;
Expand Down Expand Up @@ -10758,6 +10783,13 @@ int CUDT::processConnectRequest(const sockaddr_any& addr, CPacket& packet)
// set in the above INDUCTION, in the HS_VERSION_SRT1
// should also contain extra data.

if (!hs.valid())
{
LOGC(cnlog.Error, log << "processConnectRequest: ROGUE HS RECEIVED. Rejecting");
m_RejectReason = SRT_REJ_ROGUE;
return SRT_REJ_ROGUE;
}

HLOGC(cnlog.Debug,
log << "processConnectRequest: received type=" << RequestTypeStr(hs.m_iReqType) << " - checking cookie...");
if (hs.m_iCookie != cookie_val)
Expand Down
2 changes: 1 addition & 1 deletion srtcore/core.h
Original file line number Diff line number Diff line change
Expand Up @@ -546,7 +546,7 @@ class CUDT
SRT_ATR_NODISCARD EConnectStatus processRendezvous(const CPacket &response, const sockaddr_any& serv_addr, EReadStatus, CPacket& reqpkt);
SRT_ATR_NODISCARD bool prepareConnectionObjects(const CHandShake &hs, HandshakeSide hsd, CUDTException *eout);
SRT_ATR_NODISCARD EConnectStatus postConnect(const CPacket& response, bool rendezvous, CUDTException* eout) ATR_NOEXCEPT;
void applyResponseSettings() ATR_NOEXCEPT;
SRT_ATR_NODISCARD bool applyResponseSettings() ATR_NOEXCEPT;
SRT_ATR_NODISCARD EConnectStatus processAsyncConnectResponse(const CPacket& pkt) ATR_NOEXCEPT;
SRT_ATR_NODISCARD bool processAsyncConnectRequest(EReadStatus rst, EConnectStatus cst, const CPacket& response, const sockaddr_any& serv_addr);
SRT_ATR_NODISCARD EConnectStatus craftKmResponse(uint32_t* aw_kmdata, size_t& w_kmdatasize);
Expand Down
13 changes: 13 additions & 0 deletions srtcore/handshake.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <algorithm>

#include "udt.h"
#include "api.h"
#include "core.h"
#include "handshake.h"
#include "utilities.h"
Expand Down Expand Up @@ -186,6 +187,18 @@ string CHandShake::RdvStateStr(CHandShake::RendezvousState s)
}
#endif

bool CHandShake::valid()
{
if (m_iVersion < CUDT::HS_VERSION_UDT4
|| m_iISN < 0 || m_iISN >= CSeqNo::m_iMaxSeqNo
|| m_iMSS < 32
|| m_iFlightFlagSize < 2
|| m_iID >= CUDTUnited::MAX_SOCKET_VAL)
return false;

return true;
}

string CHandShake::show()
{
ostringstream so;
Expand Down
103 changes: 52 additions & 51 deletions srtcore/handshake.h
Original file line number Diff line number Diff line change
Expand Up @@ -285,20 +285,20 @@ inline std::string RequestTypeStr(UDTRequestType) { return ""; }
class CHandShake
{
public:
CHandShake();
CHandShake();

int store_to(char* buf, size_t& size);
int load_from(const char* buf, size_t size);
int store_to(char* buf, size_t& size);
int load_from(const char* buf, size_t size);

public:
// This is the size of SERIALIZED handshake.
// Might be defined as simply sizeof(CHandShake), but the
// enum values would have to be forced as int32_t, which is only
// available in C++11. Theoretically they are all 32-bit, but
// such a statement is not reliable and not portable.
static const size_t m_iContentSize = 48; // Size of hand shake data
// This is the size of SERIALIZED handshake.
// Might be defined as simply sizeof(CHandShake), but the
// enum values would have to be forced as int32_t, which is only
// available in C++11. Theoretically they are all 32-bit, but
// such a statement is not reliable and not portable.
static const size_t m_iContentSize = 48; // Size of hand shake data

// Extension flags
// Extension flags

static const int32_t HS_EXT_HSREQ = BIT(0);
static const int32_t HS_EXT_KMREQ = BIT(1);
Expand All @@ -310,50 +310,51 @@ class CHandShake
int32_t flags() { return m_iType; }

public:
int32_t m_iVersion; // UDT version (HS_VERSION_* symbols)
int32_t m_iType; // UDT4: socket type (only UDT_DGRAM is valid); SRT1: extension flags
int32_t m_iISN; // random initial sequence number
int32_t m_iMSS; // maximum segment size
int32_t m_iFlightFlagSize; // flow control window size
UDTRequestType m_iReqType; // handshake stage
int32_t m_iID; // socket ID
int32_t m_iCookie; // cookie
uint32_t m_piPeerIP[4]; // The IP address that the peer's UDP port is bound to

bool m_extension;

std::string show();

// The rendezvous state machine used in HSv5 only (in HSv4 everything is happening the old way).
//
// The WAVING state is the very initial state of the rendezvous connection and restored after the
// connection is closed.
// The ATTENTION and FINE are two alternative states that are transited to from WAVING. The possible
// situations are:
// - "serial arrangement": one party transits to ATTENTION and the other party transits to FINE
// - "parallel arrangement" both parties transit to ATTENTION
//
// Parallel arrangement is a "virtually impossible" case, in which both parties must send the first
// URQ_WAVEAHAND message in a perfect time synchronization, when they are started at exactly the same
// time, on machines with exactly the same performance and all things preceding the message sending
// have taken perfectly identical amount of time. This isn't anyhow possible otherwise because if
// the clients have started at different times, the one who started first sends a message and the
// system of the receiver buffers this message even before the client binds the port for enough long
// time so that it outlasts also the possible second, repeated waveahand.
enum RendezvousState
{
RDV_INVALID, //< This socket wasn't prepared for rendezvous process. Reject any events.
RDV_WAVING, //< Initial state for rendezvous. No contact seen from the peer.
RDV_ATTENTION, //< When received URQ_WAVEAHAND. [WAVING]:URQ_WAVEAHAND --> [ATTENTION].
RDV_FINE, //< When received URQ_CONCLUSION. [WAVING]:URQ_CONCLUSION --> [FINE].
RDV_INITIATED, //< When received URQ_CONCLUSION+HSREQ extension in ATTENTION state.
RDV_CONNECTED //< Final connected state. [ATTENTION]:URQ_CONCLUSION --> [CONNECTED] <-- [FINE]:URQ_AGREEMENT.
};
int32_t m_iVersion; // UDT version (HS_VERSION_* symbols)
int32_t m_iType; // UDT4: socket type (only UDT_DGRAM is valid); SRT1: extension flags
int32_t m_iISN; // random initial sequence number
int32_t m_iMSS; // maximum segment size
int32_t m_iFlightFlagSize; // flow control window size
UDTRequestType m_iReqType; // handshake stage
int32_t m_iID; // socket ID
int32_t m_iCookie; // cookie
uint32_t m_piPeerIP[4]; // The IP address that the peer's UDP port is bound to

bool m_extension;

bool valid();
std::string show();

// The rendezvous state machine used in HSv5 only (in HSv4 everything is happening the old way).
//
// The WAVING state is the very initial state of the rendezvous connection and restored after the
// connection is closed.
// The ATTENTION and FINE are two alternative states that are transited to from WAVING. The possible
// situations are:
// - "serial arrangement": one party transits to ATTENTION and the other party transits to FINE
// - "parallel arrangement" both parties transit to ATTENTION
//
// Parallel arrangement is a "virtually impossible" case, in which both parties must send the first
// URQ_WAVEAHAND message in a perfect time synchronization, when they are started at exactly the same
// time, on machines with exactly the same performance and all things preceding the message sending
// have taken perfectly identical amount of time. This isn't anyhow possible otherwise because if
// the clients have started at different times, the one who started first sends a message and the
// system of the receiver buffers this message even before the client binds the port for enough long
// time so that it outlasts also the possible second, repeated waveahand.
enum RendezvousState
{
RDV_INVALID, //< This socket wasn't prepared for rendezvous process. Reject any events.
RDV_WAVING, //< Initial state for rendezvous. No contact seen from the peer.
RDV_ATTENTION, //< When received URQ_WAVEAHAND. [WAVING]:URQ_WAVEAHAND --> [ATTENTION].
RDV_FINE, //< When received URQ_CONCLUSION. [WAVING]:URQ_CONCLUSION --> [FINE].
RDV_INITIATED, //< When received URQ_CONCLUSION+HSREQ extension in ATTENTION state.
RDV_CONNECTED //< Final connected state. [ATTENTION]:URQ_CONCLUSION --> [CONNECTED] <-- [FINE]:URQ_AGREEMENT.
};

#if ENABLE_LOGGING
static std::string RdvStateStr(RendezvousState s);
static std::string RdvStateStr(RendezvousState s);
#else
static std::string RdvStateStr(RendezvousState) { return ""; }
static std::string RdvStateStr(RendezvousState) { return ""; }
#endif

};
Expand Down

0 comments on commit 7d99b80

Please sign in to comment.