netfilter: ctnetlink: Fix regression in CTA_STATUS processing
authorKevin Cernekee <cernekee@chromium.org>
Thu, 26 Jan 2017 22:49:43 +0000 (14:49 -0800)
committerPablo Neira Ayuso <pablo@netfilter.org>
Mon, 6 Feb 2017 11:48:26 +0000 (12:48 +0100)
The libnetfilter_conntrack userland library always sets IPS_CONFIRMED
when building a CTA_STATUS attribute.  If this toggles the bit from
0->1, the parser will return an error.  On Linux 4.4+ this will cause any
NFQA_EXP attribute in the packet to be ignored.  This breaks conntrackd's
userland helpers because they operate on unconfirmed connections.

Instead of returning -EBUSY if the user program asks to modify an
unchangeable bit, simply ignore the change.

Also, fix the logic so that user programs are allowed to clear
the bits that they are allowed to change.

Signed-off-by: Kevin Cernekee <cernekee@chromium.org>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
include/uapi/linux/netfilter/nf_conntrack_common.h
net/netfilter/nf_conntrack_netlink.c

index 6d074d14ee274309f9e35b967e1728f26506fd6e..6a8e33dd4ecbd83b0d254d4cf2486dfe5cacfd34 100644 (file)
@@ -82,6 +82,10 @@ enum ip_conntrack_status {
        IPS_DYING_BIT = 9,
        IPS_DYING = (1 << IPS_DYING_BIT),
 
+       /* Bits that cannot be altered from userland. */
+       IPS_UNCHANGEABLE_MASK = (IPS_NAT_DONE_MASK | IPS_NAT_MASK |
+                                IPS_EXPECTED | IPS_CONFIRMED | IPS_DYING),
+
        /* Connection has fixed timeout. */
        IPS_FIXED_TIMEOUT_BIT = 10,
        IPS_FIXED_TIMEOUT = (1 << IPS_FIXED_TIMEOUT_BIT),
index 27540455dc62a3e827e2267a0b25e8ed210f8cb9..bf04b7e9d6f7330162f21012200f68fcf2634be3 100644 (file)
@@ -2269,6 +2269,30 @@ nla_put_failure:
        return -ENOSPC;
 }
 
+static int
+ctnetlink_update_status(struct nf_conn *ct, const struct nlattr * const cda[])
+{
+       unsigned int status = ntohl(nla_get_be32(cda[CTA_STATUS]));
+       unsigned long d = ct->status ^ status;
+
+       if (d & IPS_SEEN_REPLY && !(status & IPS_SEEN_REPLY))
+               /* SEEN_REPLY bit can only be set */
+               return -EBUSY;
+
+       if (d & IPS_ASSURED && !(status & IPS_ASSURED))
+               /* ASSURED bit can only be set */
+               return -EBUSY;
+
+       /* This check is less strict than ctnetlink_change_status()
+        * because callers often flip IPS_EXPECTED bits when sending
+        * an NFQA_CT attribute to the kernel.  So ignore the
+        * unchangeable bits but do not error out.
+        */
+       ct->status = (status & ~IPS_UNCHANGEABLE_MASK) |
+                    (ct->status & IPS_UNCHANGEABLE_MASK);
+       return 0;
+}
+
 static int
 ctnetlink_glue_parse_ct(const struct nlattr *cda[], struct nf_conn *ct)
 {
@@ -2280,7 +2304,7 @@ ctnetlink_glue_parse_ct(const struct nlattr *cda[], struct nf_conn *ct)
                        return err;
        }
        if (cda[CTA_STATUS]) {
-               err = ctnetlink_change_status(ct, cda);
+               err = ctnetlink_update_status(ct, cda);
                if (err < 0)
                        return err;
        }