bouncedcc: only let the user's own clients connect to an IRC-originated bounce
When the DCC request came from IRC, ZNC connects out to the remote peer and the thing connecting to the bounce listener should be one of the user's clients. In GetSockObj, compare the connecting address with the remote IP of each currently connected client and refuse anyone else, closing the accepted socket straight away while leaving the listener up for the real client. The user-originated path is unchanged, since there it is the remote IRC peer that is expected to connect.
This commit is contained in:
1 parent
8aef9873fc
commit
d5375e58f8
2 files changed
+34
No files matched your search
@@ -163,6 +163,13 @@ class CBounceDCCMod : public CModule {
|
||||
|
||||
bool UseClientIP() { return GetNV("UseClientIP").ToBool(); }
|
||||
|
||||
bool IsClientIP(const CString& sIP) {
|
||||
for (CClient* pClient : GetUser()->GetAllClients()) {
|
||||
if (pClient->GetRemoteIP().Equals(sIP)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// The address in a DCC request from IRC is chosen by the remote peer, and
|
||||
// ZNC is the one connecting to it, so don't let it point at ZNC's own
|
||||
// host or at link-local services.
|
||||
@@ -506,6 +513,24 @@ void CDCCBounce::Shutdown() {
|
||||
}
|
||||
|
||||
Csock* CDCCBounce::GetSockObj(const CString& sHost, unsigned short uPort) {
|
||||
// m_sRemoteIP is only known up front when the request came from IRC. In
|
||||
// that case ZNC connects out to the remote peer itself, so the only thing
|
||||
// that should be connecting here is one of the user's own clients. Hand
|
||||
// Csocket a throwaway socket that is closed straight away and keep
|
||||
// listening for the real client.
|
||||
if (!m_sRemoteIP.empty() && !m_pModule->IsClientIP(sHost)) {
|
||||
DEBUG(GetSockName() << " == GetSockObj(" << sHost << ") refused");
|
||||
CString sType = m_bIsChat ? t_s("Chat", "type") : t_s("Xfer", "type");
|
||||
m_pModule->PutModule(
|
||||
t_f("DCC {1} Bounce ({2}): Refused connection from {3}, which is "
|
||||
"not one of your clients")(sType, m_sRemoteNick, sHost));
|
||||
CDCCBounce* pRefused =
|
||||
new CDCCBounce(m_pModule, sHost, uPort, m_sRemoteNick, m_sRemoteIP,
|
||||
m_sFileName, m_bIsChat);
|
||||
pRefused->Close();
|
||||
return pRefused;
|
||||
}
|
||||
|
||||
Close();
|
||||
|
||||
if (m_sRemoteIP.empty()) {
|
||||
|
||||
@@ -766,6 +766,15 @@ TEST_F(ZNCTest, BounceDCCModule) {
|
||||
client.ReadUntilAndGet(":friend!user@host PRIVMSG nick :\001DCC CHAT chat", line2);
|
||||
EXPECT_THAT(line2.toStdString(), Not(HasSubstr("3232235521 54321")));
|
||||
|
||||
// Only the user's own clients may connect to that bounce port. The test
|
||||
// client is on a unix socket, so a TCP connection from anywhere is refused.
|
||||
QByteArray bouncePort = line2.mid(line2.lastIndexOf(' ') + 1);
|
||||
bouncePort.chop(1); // trailing \001
|
||||
QTcpSocket outsider;
|
||||
outsider.connectToHost("127.0.0.1", bouncePort.toUShort());
|
||||
outsider.waitForConnected();
|
||||
client.ReadUntil("Refused connection from 127.0.0.1");
|
||||
|
||||
// Peer must not be able to make ZNC connect to its own loopback
|
||||
ircd.Write(":friend!user@host PRIVMSG nick :\001DCC CHAT chat 2130706433 6379\001");
|
||||
client.ReadUntil("refused: bad address 2130706433");
|
||||
|
||||
Reference in new issue
Block a user