A conntrack can point to a master conntrack, which itself may point to another master, and so on. There is currently no bound on how deep this chain can grow. Force a max depth in nf_ct_expect_alloc(): walk the master chain and reject allocation if it would exceed 8 levels of nesting. Also add ctnetlink_find_get_master and use it so that ctnetlink create paths return -EMLINK instead of falling through to nf_ct_expect_alloc() and returning a misleading -ENOMEM. This prevents unbounded ct->master->master->... chains. This also restricts the number of call forward/redirects that h323 and sip helpers will follow. Reported-by: Daehyeon Ko <4ncienth@gmail.com> Signed-off-by: Florian Westphal --- v2: increase cap to 8, no other changes. include/net/netfilter/nf_conntrack_helper.h | 15 +++++++++ net/netfilter/nf_conntrack_expect.c | 15 +++++++-- net/netfilter/nf_conntrack_netlink.c | 36 +++++++++++++++------ 3 files changed, 53 insertions(+), 13 deletions(-) diff --git a/include/net/netfilter/nf_conntrack_helper.h b/include/net/netfilter/nf_conntrack_helper.h index 335b8c43694f..53a8eda848dc 100644 --- a/include/net/netfilter/nf_conntrack_helper.h +++ b/include/net/netfilter/nf_conntrack_helper.h @@ -29,6 +29,8 @@ enum nf_ct_helper_flags { #define NF_CT_HELPER_NAME_LEN 16 +#define NF_CT_MAX_EXPECT_CHAIN_LEN 8 + /* Must be kept in sync with the classes defined by helpers */ #define NF_CT_MAX_EXPECT_CLASSES 4 @@ -159,6 +161,19 @@ static inline void nf_ct_help_put(const struct nf_conn *ct) kfree_rcu(helper, rcu); } +static inline bool nf_ct_master_acceptable(const struct nf_conn *m) +{ + unsigned int depth = 0; + + while (m->master) { + if (++depth >= NF_CT_MAX_EXPECT_CHAIN_LEN) + return false; + m = m->master; + } + + return true; +} + int nf_conntrack_helper_init(void); void nf_conntrack_helper_fini(void); diff --git a/net/netfilter/nf_conntrack_expect.c b/net/netfilter/nf_conntrack_expect.c index 06242c86e1dc..62e434cd57c9 100644 --- a/net/netfilter/nf_conntrack_expect.c +++ b/net/netfilter/nf_conntrack_expect.c @@ -299,13 +299,22 @@ void nf_ct_unexpect_related(struct nf_conntrack_expect *exp) } EXPORT_SYMBOL_GPL(nf_ct_unexpect_related); -/* We don't increase the master conntrack refcount for non-fulfilled - * conntracks. During the conntrack destruction, the expectations are - * always killed before the conntrack itself */ +/** + * nf_ct_expect_alloc - allocate a new conntrack expectation + * @me: The conntrack to be used as master. + * + * The master conntrack refcount is not incremented; expectations + * are always killed before the master conntrack is destroyed. + * + * Return: a new expectation on success or NULL on failure. + */ struct nf_conntrack_expect *nf_ct_expect_alloc(struct nf_conn *me) { struct nf_conntrack_expect *new; + if (!nf_ct_master_acceptable(me)) + return NULL; + new = kmem_cache_zalloc(nf_ct_expect_cachep, GFP_ATOMIC); if (!new) return NULL; diff --git a/net/netfilter/nf_conntrack_netlink.c b/net/netfilter/nf_conntrack_netlink.c index 1f8d94a05240..30428d520e43 100644 --- a/net/netfilter/nf_conntrack_netlink.c +++ b/net/netfilter/nf_conntrack_netlink.c @@ -2231,6 +2231,26 @@ ctnetlink_change_conntrack(struct nf_conn *ct, return 0; } +static struct nf_conn * +ctnetlink_find_get_master(struct net *net, const struct nf_conntrack_zone *zone, + const struct nf_conntrack_tuple *tuple) +{ + struct nf_conntrack_tuple_hash *master_h; + struct nf_conn *master_ct; + + master_h = nf_conntrack_find_get(net, zone, tuple); + if (!master_h) + return ERR_PTR(-ENOENT); + + master_ct = nf_ct_tuplehash_to_ctrack(master_h); + if (!nf_ct_master_acceptable(master_ct)) { + nf_ct_put(master_ct); + return ERR_PTR(-EMLINK); + } + + return master_ct; +} + static struct nf_conn * ctnetlink_create_conntrack(struct net *net, const struct nf_conntrack_zone *zone, @@ -2356,7 +2376,6 @@ ctnetlink_create_conntrack(struct net *net, /* setup master conntrack: this is a confirmed expectation */ if (cda[CTA_TUPLE_MASTER]) { struct nf_conntrack_tuple master; - struct nf_conntrack_tuple_hash *master_h; struct nf_conn *master_ct; err = ctnetlink_parse_tuple(cda, &master, CTA_TUPLE_MASTER, @@ -2364,12 +2383,11 @@ ctnetlink_create_conntrack(struct net *net, if (err < 0) goto err2; - master_h = nf_conntrack_find_get(net, zone, &master); - if (master_h == NULL) { - err = -ENOENT; + master_ct = ctnetlink_find_get_master(net, zone, &master); + if (IS_ERR(master_ct)) { + err = PTR_ERR(master_ct); goto err2; } - master_ct = nf_ct_tuplehash_to_ctrack(master_h); __set_bit(IPS_EXPECTED_BIT, &ct->status); ct->master = master_ct; } @@ -3613,7 +3631,6 @@ ctnetlink_create_expect(struct net *net, u_int8_t u3, u32 portid, int report) { struct nf_conntrack_tuple tuple, mask, master_tuple; - struct nf_conntrack_tuple_hash *h = NULL; struct nf_conntrack_expect *exp; struct nf_conn *ct; int err; @@ -3633,10 +3650,9 @@ ctnetlink_create_expect(struct net *net, return err; /* Look for master conntrack of this expectation */ - h = nf_conntrack_find_get(net, zone, &master_tuple); - if (!h) - return -ENOENT; - ct = nf_ct_tuplehash_to_ctrack(h); + ct = ctnetlink_find_get_master(net, zone, &master_tuple); + if (IS_ERR(ct)) + return PTR_ERR(ct); rcu_read_lock(); exp = ctnetlink_alloc_expect(cda, ct, NULL, &tuple, &mask); -- 2.56.0