netfilter: nf_conntrack_irc: Tighten matching on DCC message
authorDavid Leadbeater <dgl@dgl.cx>
Fri, 26 Aug 2022 04:56:57 +0000 (14:56 +1000)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Wed, 28 Sep 2022 08:55:45 +0000 (10:55 +0200)
[ Upstream commit e8d5dfd1d8747b56077d02664a8838c71ced948e ]

CTCP messages should only be at the start of an IRC message, not
anywhere within it.

While the helper only decodes packes in the ORIGINAL direction, its
possible to make a client send a CTCP message back by empedding one into
a PING request.  As-is, thats enough to make the helper believe that it
saw a CTCP message.

Fixes: 869f37d8e48f ("[NETFILTER]: nf_conntrack/nf_nat: add IRC helper port")
Signed-off-by: David Leadbeater <dgl@dgl.cx>
Signed-off-by: Florian Westphal <fw@strlen.de>
Signed-off-by: Sasha Levin <sashal@kernel.org>
net/netfilter/nf_conntrack_irc.c

index c6a8bdc3a226dddb520ae29daf9bd478d549ebb8..5d630288f86c9adca956a2725872b59a6596ffdc 100644 (file)
@@ -150,15 +150,37 @@ static int help(struct sk_buff *skb, unsigned int protoff,
        data = ib_ptr;
        data_limit = ib_ptr + skb->len - dataoff;
 
-       /* strlen("\1DCC SENT t AAAAAAAA P\1\n")=24
-        * 5+MINMATCHLEN+strlen("t AAAAAAAA P\1\n")=14 */
-       while (data < data_limit - (19 + MINMATCHLEN)) {
-               if (memcmp(data, "\1DCC ", 5)) {
+       /* Skip any whitespace */
+       while (data < data_limit - 10) {
+               if (*data == ' ' || *data == '\r' || *data == '\n')
+                       data++;
+               else
+                       break;
+       }
+
+       /* strlen("PRIVMSG x ")=10 */
+       if (data < data_limit - 10) {
+               if (strncasecmp("PRIVMSG ", data, 8))
+                       goto out;
+               data += 8;
+       }
+
+       /* strlen(" :\1DCC SENT t AAAAAAAA P\1\n")=26
+        * 7+MINMATCHLEN+strlen("t AAAAAAAA P\1\n")=26
+        */
+       while (data < data_limit - (21 + MINMATCHLEN)) {
+               /* Find first " :", the start of message */
+               if (memcmp(data, " :", 2)) {
                        data++;
                        continue;
                }
+               data += 2;
+
+               /* then check that place only for the DCC command */
+               if (memcmp(data, "\1DCC ", 5))
+                       goto out;
                data += 5;
-               /* we have at least (19+MINMATCHLEN)-5 bytes valid data left */
+               /* we have at least (21+MINMATCHLEN)-(2+5) bytes valid data left */
 
                iph = ip_hdr(skb);
                pr_debug("DCC found in master %pI4:%u %pI4:%u\n",
@@ -174,7 +196,7 @@ static int help(struct sk_buff *skb, unsigned int protoff,
                        pr_debug("DCC %s detected\n", dccprotos[i]);
 
                        /* we have at least
-                        * (19+MINMATCHLEN)-5-dccprotos[i].matchlen bytes valid
+                        * (21+MINMATCHLEN)-7-dccprotos[i].matchlen bytes valid
                         * data left (== 14/13 bytes) */
                        if (parse_dcc(data, data_limit, &dcc_ip,
                                       &dcc_port, &addr_beg_p, &addr_end_p)) {