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

This Linux kernel change "netfilter: ctnetlink: don’t use conntrack/expect object addresses as id" is included in the Linux 3.16.72 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 3d8b3d0 (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

commit 3c79107631db1f7fd32cf3f7368e4672004a3010 upstream.

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 <[email protected]>
Signed-off-by: Pablo Neira Ayuso <[email protected]>
[bwh: Backported to 3.16:
 - Include <net/netns/hash.h> in nf_conntrack_core.c
 - Adjust context]
Signed-off-by: Ben Hutchings <[email protected]>

There are 72 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    | 36 ++++++++++++++++++++++++++++++++++++
 net/netfilter/nf_conntrack_netlink.c | 34 +++++++++++++++++++++++++++++-----
 3 files changed, 67 insertions(+), 5 deletions(-)

diff --git a/include/net/netfilter/nf_conntrack.h b/include/net/netfilter/nf_conntrack.h
index 5c53572..3b66e31 100644
--- a/include/net/netfilter/nf_conntrack.h
+++ b/include/net/netfilter/nf_conntrack.h
@@ -289,6 +289,8 @@ static inline bool nf_is_loopback_packet(const struct sk_buff *skb)

 void nf_conntrack_tmpl_insert(struct net *net, struct nf_conn *tmpl);

+u32 nf_ct_get_id(const struct nf_conn *ct);
+
 #define NF_CT_STAT_INC(net, count)   __this_cpu_inc((net)->ct.stat->count)
 #define NF_CT_STAT_INC_ATOMIC(net, count) this_cpu_inc((net)->ct.stat->count)

diff --git a/net/netfilter/nf_conntrack_core.c b/net/netfilter/nf_conntrack_core.c
index cdd4738..170681b 100644
--- a/net/netfilter/nf_conntrack_core.c
+++ b/net/netfilter/nf_conntrack_core.c
@@ -23,6 +23,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>
@@ -52,6 +53,7 @@
 #include <net/netfilter/nf_nat.h>
 #include <net/netfilter/nf_nat_core.h>
 #include <net/netfilter/nf_nat_helper.h>
+#include <net/netns/hash.h>

 #define NF_CONNTRACK_VERSION   "0.5.0"

@@ -232,6 +234,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 faa736b..b9bf8b0 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>
@@ -435,7 +436,9 @@ static int ctnetlink_label_size(const struct nf_conn *ct)
 static inline 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;

@@ -1047,8 +1050,9 @@ struct ctnetlink_dump_filter {
    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;
        }
@@ -2321,6 +2325,25 @@ static int ctnetlink_nfqueue_exp_parse(const struct nlattr * const *cda,

 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)
@@ -2368,7 +2391,7 @@ static int ctnetlink_nfqueue_exp_parse(const struct nlattr * const *cda,
    }
 #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;
@@ -2664,7 +2687,8 @@ static int ctnetlink_dump_exp_ct(struct sock *ctnl, struct sk_buff *skb,

    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 *