diff --git a/src/Client.cpp b/src/Client.cpp index aa9c0896..dca31b39 100644 --- a/src/Client.cpp +++ b/src/Client.cpp @@ -420,6 +420,13 @@ void CAuthBase::Invalidate() { m_pSock = nullptr; } void CAuthBase::AcceptLogin(CUser& User) { if (m_pSock) { + // Auth modules call this directly, so check allowed hosts here rather + // than only in CZNC::AuthUser() + CString sHost = GetRemoteIP(); + if (!User.IsHostAllowed(sHost)) { + RefuseLogin("Your host [" + sHost + "] is not allowed"); + return; + } AcceptedLogin(User); } Invalidate(); @@ -1340,6 +1347,11 @@ void CClient::RefuseSASLLogin(const CString& sReason) { } void CClient::AcceptSASLLogin(CUser& User) { + if (!User.IsHostAllowed(GetRemoteIP())) { + RefuseSASLLogin("Your host [" + GetRemoteIP() + "] is not allowed"); + return; + } + PutClient(":irc.znc.in 900 " + GetNick() + " " + GetNick() + "!" + User.GetIdent() + "@" + GetRemoteIP() + " " + User.GetUsername() + " :You are now logged in as " + User.GetUsername()); diff --git a/src/znc.cpp b/src/znc.cpp index 0b3265b9..d9dc887c 100644 --- a/src/znc.cpp +++ b/src/znc.cpp @@ -1978,8 +1978,6 @@ CZNC::TrafficStatsMap CZNC::GetNetworkTrafficStats(const CString& sUsername, } void CZNC::AuthUser(std::shared_ptr AuthClass) { - // TODO unless the auth module calls it, CUser::IsHostAllowed() is not - // honoured bool bReturn = false; GLOBALMODULECALL(OnLoginAttempt(AuthClass), &bReturn); if (bReturn) return; @@ -1991,13 +1989,7 @@ void CZNC::AuthUser(std::shared_ptr AuthClass) { return; } - CString sHost = AuthClass->GetRemoteIP(); - - if (!pUser->IsHostAllowed(sHost)) { - AuthClass->RefuseLogin("Your host [" + sHost + "] is not allowed"); - return; - } - + // CAuthBase::AcceptLogin() checks allowed hosts AuthClass->AcceptLogin(*pUser); } diff --git a/test/ClientTest.cpp b/test/ClientTest.cpp index d60f88c9..5b134b0f 100644 --- a/test/ClientTest.cpp +++ b/test/ClientTest.cpp @@ -390,3 +390,11 @@ TEST_F(ClientTest, OnSendToClientMessage) { EXPECT_THAT(m_pTestSock->vsLines, ElementsAre(msg.ToString())); m_pTestModule->bSendHooks = false; } + +TEST_F(ClientTest, AcceptSASLLoginAllowedHosts) { + TestClient client; + m_pTestUser->AddAllowedHost("203.0.113.0/24"); + client.AcceptSASLLogin(*m_pTestUser); + EXPECT_THAT(client.vsLines, + ElementsAre(":irc.znc.in 904 me :Your host [] is not allowed")); +} diff --git a/test/integration/tests/modules.cpp b/test/integration/tests/modules.cpp index 54732252..2ec09b34 100644 --- a/test/integration/tests/modules.cpp +++ b/test/integration/tests/modules.cpp @@ -589,6 +589,41 @@ TEST_F(ZNCTest, SaslAuthPlainImapAuthLineBreak) { EXPECT_FALSE(imap.hasPendingConnections()); } +TEST_F(ZNCTest, ImapAuthAllowedHosts) { + QTcpServer imap; + ASSERT_TRUE(imap.listen(QHostAddress::LocalHost)) << imap.errorString().toStdString(); + QFile conf(m_dir.path() + "/configs/znc.conf"); + ASSERT_TRUE(conf.open(QIODevice::Append | QIODevice::Text)); + QTextStream(&conf) << R"( + + Pass = plain#hunter2 + Allow = 203.0.113.0/24 + + )"; + conf.close(); + auto znc = Run(); + auto ircd = ConnectIRCd(); + auto client = LoginClient(); + client.Write(QStringLiteral("znc loadmod imapauth 127.0.0.1 %1") + .arg(imap.serverPort()) + .toUtf8()); + client.ReadUntil("Loaded"); + + auto client2 = ConnectClient(); + client2.Write("PASS foo:hunter2"); + client2.Write("NICK foo"); + client2.Write("USER foo x x :x"); + + ASSERT_TRUE(imap.waitForNewConnection(30000 /* msec */)); + auto imapsock = WrapIO(imap.nextPendingConnection()); + imapsock.Write("* OK IMAP4rev1 Service Ready"); + imapsock.ReadUntil("AUTH LOGIN \"foo\" \"hunter2\""); + imapsock.Write("AUTH OK"); + + client2.ReadUntil(":irc.znc.in 464 foo :Your host ["); + client2.ReadUntil("] is not allowed"); +} + TEST_F(ZNCTest, SaslAuthExternal) { #ifndef HAVE_LIBSSL GTEST_SKIP() << "SSL is disabled";