netfilter: ctnetlink: don’t use conntrack/expect object addresses as id [Linux 4.14.120]

netfilter: ctnetlink: don’t use conntrack/expect object addresses as id [Linux 4.14.120]

This Linux kernel change "netfilter: ctnetlink: don’t use conntrack/expect object addresses as id" is included in the Linux 4.14.120 release. This change is authored by Florian Westphal <fw [at] strlen.de> on Mon Apr 1 13:08:54 2019 +0200. The commit for this change in Linux stable tree is f862c13 (patch) which is from upstream commit 3c79107. The same Linux upstream change may have been applied to various maintained Linux releases and you can find all Linux releases containing changes from upstream 3c79107.

netfilter: ctnetlink: don't use conntrack/expect object addresses as id

[ Upstream commit 3c79107631db1f7fd32cf3f7368e4672004a3010 ]

else, we leak the addresses to userspace via ctnetlink events
and dumps.

Compute an ID on demand based on the immutable parts of nf_conn struct.

Another advantage compared to using an address is that there is no
immediate re-use of the same ID in case the conntrack entry is freed and
reallocated again immediately.

Fixes: 3583240249ef ("[NETFILTER]: nf_conntrack_expect: kill unique ID")
Fixes: 7f85f914721f ("[NETFILTER]: nf_conntrack: kill unique ID")
Signed-off-by: Florian Westphal <fw@strlen.de>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
Signed-off-by: Sasha Levin <sashal@kernel.org>

There are 71 lines of Linux source code added/deleted in this change. Code changes to Linux kernel are as follows.

 include/net/netfilter/nf_conntrack.h |  2 ++
 net/netfilter/nf_conntrack_core.c    | 35 +++++++++++++++++++++++++++++++++++
 net/netfilter/nf_conntrack_netlink.c | 34 +++++++++++++++++++++++++++++-----
 3 files changed, 66 insertions(+), 5 deletions(-)

diff --git a/include/net/netfilter/nf_conntrack.h b/include/net/netfilter/nf_conntrack.h
index 792c3f6..93bbae8 100644
--- a/include/net/netfilter/nf_conntrack.h
+++ b/include/net/netfilter/nf_conntrack.h
@@ -315,6 +315,8 @@ struct nf_conn *nf_ct_tmpl_alloc(struct net *net,
                 gfp_t flags);
 void nf_ct_tmpl_free(struct nf_conn *tmpl);

+u32 nf_ct_get_id(const struct nf_conn *ct);
+
 static inline void
 nf_ct_set(struct sk_buff *skb, struct nf_conn *ct, enum ip_conntrack_info info)
 {
diff --git a/net/netfilter/nf_conntrack_core.c b/net/netfilter/nf_conntrack_core.c
index 06520bf..fa49a62 100644
--- a/net/netfilter/nf_conntrack_core.c
+++ b/net/netfilter/nf_conntrack_core.c
@@ -25,6 +25,7 @@
 #include <linux/slab.h>
 #include <linux/random.h>
 #include <linux/jhash.h>
+#include <linux/siphash.h>
 #include <linux/err.h>
 #include <linux/percpu.h>
 #include <linux/moduleparam.h>
@@ -300,6 +301,40 @@ bool nf_ct_get_tuplepr(const struct sk_buff *skb, unsigned int nhoff,
 }
 EXPORT_SYMBOL_GPL(nf_ct_invert_tuple);

+/* Generate a almost-unique pseudo-id for a given conntrack.
+ *
+ * intentionally doesn't re-use any of the seeds used for hash
+ * table location, we assume id gets exposed to userspace.
+ *
+ * Following nf_conn items do not change throughout lifetime
+ * of the nf_conn after it has been committed to main hash table:
+ *
+ * 1. nf_conn address
+ * 2. nf_conn->ext address
+ * 3. nf_conn->master address (normally NULL)
+ * 4. tuple
+ * 5. the associated net namespace
+ */
+u32 nf_ct_get_id(const struct nf_conn *ct)
+{
+   static __read_mostly siphash_key_t ct_id_seed;
+   unsigned long a, b, c, d;
+
+   net_get_random_once(&ct_id_seed, sizeof(ct_id_seed));
+
+   a = (unsigned long)ct;
+   b = (unsigned long)ct->master ^ net_hash_mix(nf_ct_net(ct));
+   c = (unsigned long)ct->ext;
+   d = (unsigned long)siphash(&ct->tuplehash, sizeof(ct->tuplehash),
+                  &ct_id_seed);
+#ifdef CONFIG_64BIT
+   return siphash_4u64((u64)a, (u64)b, (u64)c, (u64)d, &ct_id_seed);
+#else
+   return siphash_4u32((u32)a, (u32)b, (u32)c, (u32)d, &ct_id_seed);
+#endif
+}
+EXPORT_SYMBOL_GPL(nf_ct_get_id);
+
 static void
 clean_from_lists(struct nf_conn *ct)
 {
diff --git a/net/netfilter/nf_conntrack_netlink.c b/net/netfilter/nf_conntrack_netlink.c
index 48dab14..c781c9a 100644
--- a/net/netfilter/nf_conntrack_netlink.c
+++ b/net/netfilter/nf_conntrack_netlink.c
@@ -29,6 +29,7 @@
 #include <linux/spinlock.h>
 #include <linux/interrupt.h>
 #include <linux/slab.h>
+#include <linux/siphash.h>

 #include <linux/netfilter.h>
 #include <net/netlink.h>
@@ -445,7 +446,9 @@ static int ctnetlink_dump_ct_seq_adj(struct sk_buff *skb, struct nf_conn *ct)

 static int ctnetlink_dump_id(struct sk_buff *skb, const struct nf_conn *ct)
 {
-   if (nla_put_be32(skb, CTA_ID, htonl((unsigned long)ct)))
+   __be32 id = (__force __be32)nf_ct_get_id(ct);
+
+   if (nla_put_be32(skb, CTA_ID, id))
        goto nla_put_failure;
    return 0;

@@ -1179,8 +1182,9 @@ static int ctnetlink_del_conntrack(struct net *net, struct sock *ctnl,
    ct = nf_ct_tuplehash_to_ctrack(h);

    if (cda[CTA_ID]) {
-       u_int32_t id = ntohl(nla_get_be32(cda[CTA_ID]));
-       if (id != (u32)(unsigned long)ct) {
+       __be32 id = nla_get_be32(cda[CTA_ID]);
+
+       if (id != (__force __be32)nf_ct_get_id(ct)) {
            nf_ct_put(ct);
            return -ENOENT;
        }
@@ -2521,6 +2525,25 @@ static int ctnetlink_exp_dump_mask(struct sk_buff *skb,

 static const union nf_inet_addr any_addr;

+static __be32 nf_expect_get_id(const struct nf_conntrack_expect *exp)
+{
+   static __read_mostly siphash_key_t exp_id_seed;
+   unsigned long a, b, c, d;
+
+   net_get_random_once(&exp_id_seed, sizeof(exp_id_seed));
+
+   a = (unsigned long)exp;
+   b = (unsigned long)exp->helper;
+   c = (unsigned long)exp->master;
+   d = (unsigned long)siphash(&exp->tuple, sizeof(exp->tuple), &exp_id_seed);
+
+#ifdef CONFIG_64BIT
+   return (__force __be32)siphash_4u64((u64)a, (u64)b, (u64)c, (u64)d, &exp_id_seed);
+#else
+   return (__force __be32)siphash_4u32((u32)a, (u32)b, (u32)c, (u32)d, &exp_id_seed);
+#endif
+}
+
 static int
 ctnetlink_exp_dump_expect(struct sk_buff *skb,
              const struct nf_conntrack_expect *exp)
@@ -2568,7 +2591,7 @@ static int ctnetlink_exp_dump_mask(struct sk_buff *skb,
    }
 #endif
    if (nla_put_be32(skb, CTA_EXPECT_TIMEOUT, htonl(timeout)) ||
-       nla_put_be32(skb, CTA_EXPECT_ID, htonl((unsigned long)exp)) ||
+       nla_put_be32(skb, CTA_EXPECT_ID, nf_expect_get_id(exp)) ||
        nla_put_be32(skb, CTA_EXPECT_FLAGS, htonl(exp->flags)) ||
        nla_put_be32(skb, CTA_EXPECT_CLASS, htonl(exp->class)))
        goto nla_put_failure;
@@ -2873,7 +2896,8 @@ static int ctnetlink_get_expect(struct net *net, struct sock *ctnl,

    if (cda[CTA_EXPECT_ID]) {
        __be32 id = nla_get_be32(cda[CTA_EXPECT_ID]);
-       if (ntohl(id) != (u32)(unsigned long)exp) {
+
+       if (id != nf_expect_get_id(exp)) {
            nf_ct_expect_put(exp);
            return -ENOENT;
        }

Leave a Reply

Your email address will not be published. Required fields are marked *