From af5cdd01922bd8225852c7de91f48ff2443c2f9b Mon Sep 17 00:00:00 2001 From: RealKindOne Date: Sun, 23 Aug 2026 15:26:03 -0400 Subject: [PATCH] crypt: Modernize and add test. Fix OnNumericMessage 332 issue. It was possible to trigger null pointer dereference during OnNumericMessage The affected versions are 1.7.0+ CVE-2026-82374 --- modules/crypt.cpp | 53 ++++++++++++++------- test/integration/tests/modules.cpp | 74 +++++++++++++++++++++++++++++- 2 files changed, 110 insertions(+), 17 deletions(-) diff --git a/modules/crypt.cpp b/modules/crypt.cpp index 94ec3be1..2b202fa0 100644 --- a/modules/crypt.cpp +++ b/modules/crypt.cpp @@ -248,13 +248,19 @@ class CCryptMod : public CModule { FilterOutgoing(Message); return CONTINUE; } + EModRet OnPrivTextMessage(CTextMessage& Message) override { + CNick& Nick = Message.GetNick(); + CString sMessage = Message.GetText(); + CString sHostmask = Nick.GetHostMask(); - EModRet OnPrivMsg(CNick& Nick, CString& sMessage) override { FilterIncoming(Nick.GetNick(), Nick, sMessage); + Message.SetText(sMessage); return CONTINUE; } - EModRet OnPrivNotice(CNick& Nick, CString& sMessage) override { + EModRet OnPrivNoticeMessage(CNoticeMessage& Message) override { + CNick& Nick = Message.GetNick(); + CString sMessage = Message.GetText(); CString sCommand = sMessage.Token(0); CString sOtherPubKey = sMessage.Token(1); @@ -305,33 +311,42 @@ class CCryptMod : public CModule { } FilterIncoming(Nick.GetNick(), Nick, sMessage); + Message.SetText(sMessage); return CONTINUE; } - EModRet OnPrivAction(CNick& Nick, CString& sMessage) override { - FilterIncoming(Nick.GetNick(), Nick, sMessage); + EModRet OnPrivActionMessage(CActionMessage& Message) override { + CString sText = Message.GetText(); + FilterIncoming(Message.GetNick().GetNick(), Message.GetNick(), sText); + Message.SetText(sText); return CONTINUE; } - EModRet OnChanMsg(CNick& Nick, CChan& Channel, CString& sMessage) override { - FilterIncoming(Channel.GetName(), Nick, sMessage); + EModRet OnChanTextMessage(CTextMessage& Message) override { + CString sText = Message.GetText(); + FilterIncoming(Message.GetTarget(), Message.GetNick(), sText); + Message.SetText(sText); return CONTINUE; } - EModRet OnChanNotice(CNick& Nick, CChan& Channel, - CString& sMessage) override { - FilterIncoming(Channel.GetName(), Nick, sMessage); + EModRet OnChanNoticeMessage(CNoticeMessage& Message) override { + CString sText = Message.GetText(); + FilterIncoming(Message.GetTarget(), Message.GetNick(), sText); + Message.SetText(sText); return CONTINUE; } - EModRet OnChanAction(CNick& Nick, CChan& Channel, - CString& sMessage) override { - FilterIncoming(Channel.GetName(), Nick, sMessage); + EModRet OnChanActionMessage(CActionMessage& Message) override { + CString sText = Message.GetText(); + FilterIncoming(Message.GetTarget(), Message.GetNick(), sText); + Message.SetText(sText); return CONTINUE; } - EModRet OnTopic(CNick& Nick, CChan& Channel, CString& sMessage) override { - FilterIncoming(Channel.GetName(), Nick, sMessage); + EModRet OnTopicMessage(CTopicMessage& Message) override { + CString sText = Message.GetText(); + FilterIncoming(Message.GetTarget(), Message.GetNick(), sText); + Message.SetText(sText); return CONTINUE; } @@ -342,10 +357,16 @@ class CCryptMod : public CModule { CChan* pChan = GetNetwork()->FindChan(Message.GetParam(1)); if (pChan) { - CNick* Nick = pChan->FindNick(Message.GetParam(0)); + CNick* pNick = pChan->FindNick(Message.GetParam(0)); CString sTopic = Message.GetParam(2); - FilterIncoming(pChan->GetName(), *Nick, sTopic); + if (pNick) { + FilterIncoming(pChan->GetName(), *pNick, sTopic); + } else { + // 332 can arrive before ZNC has this nick in the nicklist. + CNick TempNick(Message.GetParam(0)); + FilterIncoming(pChan->GetName(), TempNick, sTopic); + } Message.SetParam(2, sTopic); } diff --git a/test/integration/tests/modules.cpp b/test/integration/tests/modules.cpp index 929a8e9b..2e0a1290 100644 --- a/test/integration/tests/modules.cpp +++ b/test/integration/tests/modules.cpp @@ -138,7 +138,11 @@ TEST_F(ZNCTest, WatchModule) { ":*spaces!watch@znc.in PRIVMSG nick : SOMETHING word1 word2 SOMETHING"); } -TEST_F(ZNCTest, ModuleCrypt) { +TEST_F(ZNCTest, CryptModule) { +#ifndef HAVE_LIBSSL + GTEST_SKIP() << "SSL is disabled"; +#endif + QFile conf(m_dir.path() + "/configs/znc.conf"); ASSERT_TRUE(conf.open(QIODevice::Append | QIODevice::Text)); QTextStream(&conf) << "ServerThrottle = 1\n"; @@ -184,6 +188,8 @@ TEST_F(ZNCTest, ModuleCrypt) { QByteArray key2(""); client2.ReadUntilAndGet("\002user\017: ", key2); ASSERT_EQ(key1.mid(9), key2.mid(8)); + + // OnPrivTextMessage client1.Write("CAP REQ :echo-message"); client1.Write("PRIVMSG .nick2 :Hello"); QByteArray secretmsg; @@ -191,6 +197,72 @@ TEST_F(ZNCTest, ModuleCrypt) { ircd2.Write(":user!user@user/test " + secretmsg); client2.ReadUntil("Hello"); client1.ReadUntil(secretmsg); // by echo-message + + client1.Write("PRIVMSG *crypt :SetNickPrefix ."); + client1.ReadUntil("Setting Nick Prefix to ."); + client2.Write("PRIVMSG *crypt :SetNickPrefix ."); + client2.ReadUntil("Setting Nick Prefix to ."); + + // OnPrivNoticeMessage + client1.Write("NOTICE .nick2 :secret notice"); + QByteArray noticeMsg; + ircd1.ReadUntilAndGet("NOTICE nick2 :+OK ", noticeMsg); + ircd2.Write(":user!user@user/test " + noticeMsg); + QByteArray noticeLine; + client2.ReadUntilAndGet("NOTICE nick2 :", noticeLine); + QByteArray noticemessage = noticeLine.mid(noticeLine.lastIndexOf(':') + 1); + EXPECT_EQ(noticemessage, "secret notice"); + + // OnUserActionMessage + client1.Write("PRIVMSG .nick2 :\001ACTION waves\001"); + QByteArray actionMsg; + ircd1.ReadUntilAndGet("PRIVMSG nick2 :\001ACTION +OK ", actionMsg); + ircd2.Write(":user!user@user/test " + actionMsg); + QByteArray actionLine; + client2.ReadUntilAndGet("PRIVMSG nick2 :", actionLine); + QByteArray actionMessage = actionLine.mid(actionLine.lastIndexOf(':') + 1); + EXPECT_EQ(actionMessage, "\001ACTION waves\001"); + + client1.Write("JOIN #test"); + client2.Write("JOIN #test"); + + QByteArray chanKey = "channelKey123"; + client1.Write(QByteArray("PRIVMSG *crypt :SetKey #test ") + chanKey); + client1.ReadUntil(QByteArray("Set encryption key for [#test] to [") + + chanKey + "]"); + client2.Write(QByteArray("PRIVMSG *crypt :SetKey #test ") + chanKey); + client2.ReadUntil(QByteArray("Set encryption key for [#test] to [") + + chanKey + "]"); + + // OnChanTextMessage + client1.Write("PRIVMSG #test :channel secret"); + QByteArray chanMsg; + ircd1.ReadUntilAndGet("PRIVMSG #test :+OK ", chanMsg); + ircd2.Write(":user!user@user/test " + chanMsg); + client2.ReadUntil("channel secret"); + + // OnChanNoticeMessage + client1.Write("NOTICE #test :chan notice"); + QByteArray chanNoticeMsg; + ircd1.ReadUntilAndGet("NOTICE #test :+OK ", chanNoticeMsg); + ircd2.Write(":user!user@user/test " + chanNoticeMsg); + client2.ReadUntil("chan notice"); + + // OnChanActionMessage + client1.Write("PRIVMSG #test :\001ACTION dances\001"); + QByteArray chanActionMsg; + ircd1.ReadUntilAndGet("PRIVMSG #test :\001ACTION +OK ", chanActionMsg); + ircd2.Write(":user!user@user/test " + chanActionMsg); + client2.ReadUntil("dances"); + + // OnTopicMessage / OnNumericMessage + client1.Write("TOPIC #test :new chan topic"); + QByteArray chanTopicEncrypted; + ircd1.ReadUntilAndGet("TOPIC #test :+OK ", chanTopicEncrypted); + chanTopicEncrypted = "+OK " + chanTopicEncrypted.mid( + chanTopicEncrypted.lastIndexOf(' ') + 1); + ircd2.Write(":ircd2 332 nick2 #test :" + chanTopicEncrypted); + client2.ReadUntil("new chan topic"); } TEST_F(ZNCTest, AutoAttachModule) {