Skip to content

Commit 1721738

Browse files
committed
netfilter: nf_tables: work around newrule after chain binding
jira VULN-430 cve CVE-2023-4244 commit-author Florian Westphal <fwestpha@redhat.com> commit d2fd2e4 upstream-diff There is no upstream commit to diff with. Picked directly from RH's branch. See message below for details. JIRA: https://issues.redhat.com/browse/RHEL-1720 JIRA: https://issues.redhat.com/browse/RHEL-1721 Upstream Status: RHEL only RHEL only. Proposed upstream but was rejected. I don't think we can force a rebase of nftables userland in RHEL <= 9.4. Even if we can do this, we would still need this change for z-stream. This change SHOULD NOT be forwarded into versions later than RHEL 9.4. For those releases nftables userspace should be updated to release 1.0.7 or later instead. nftables versions prior to commit 3975430b12d9 ("src: expand table command before evaluation"), i.e. 1.0.6 and earlier, will handle the following snippet in the wrong order: table ip t { chain c { jump { counter; } } } 1. create the table, chain,c and an anon chain. 2. append a rule to chain c to jump to the anon chain. 3. append the rule(s) (here: "counter") to the anon chain. (step 3 should be before 2). With below commit, this is now rejected by the kernel. Reason is that the 'jump {' rule added to chain c adds an explicit binding (dependency), i.e. the kernel will automatically remove the anon chain when userspace later asks to delete the 'jump {' rule from chain c. This caused crashes in the kernel in case of a errors further down in the same transaction. The abort part has to unroll all pending changes, including the request to add the rule 'jump {'. As its already bound, all the rules added to it get deleted as well. Because we tolerated late-add-after-bind, the transaction log also contains the NEWRULE requests (here "counter"), so those were deleted again. Instead of rejecting newrule-to-bound-chain, allow it iff the anon chain is new in this transaction and we are appending. Mark the newrule transaction as already_bound so abort path skips them. Fixes: 0ebc106 ("netfilter: nf_tables: disallow rule addition to bound chain via NFTA_RULE_CHAIN_ID") Reported-by: Timo Sigurdsson <public_timo.s@silentcreek.de> Closes: https://lore.kernel.org/netfilter-devel/20230911213750.5B4B663206F5@dd20004.kasserver.com/ Signed-off-by: Florian Westphal <fw@strlen.de> Signed-off-by: Florian Westphal <fwestpha@redhat.com> (cherry picked from commit d2fd2e4) Signed-off-by: Marcin Wcisło <marcin.wcislo@conclusive.pl>
1 parent 66a1d3c commit 1721738

File tree

1 file changed

+30
-2
lines changed

1 file changed

+30
-2
lines changed

net/netfilter/nf_tables_api.c

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3551,6 +3551,26 @@ static struct nft_rule *nft_rule_lookup_byid(const struct net *net,
35513551
const struct nft_chain *chain,
35523552
const struct nlattr *nla);
35533553

3554+
/* nft <= 1.0.6 appends rules to anon chains after they have been bound */
3555+
static bool nft_rule_work_around_old_version(const struct nfnl_info *info,
3556+
struct nft_chain *chain)
3557+
{
3558+
/* bound (anonymous) chain is already used */
3559+
if (nft_is_active(info->net, chain))
3560+
return false;
3561+
3562+
/* nft never asks to replace rules here */
3563+
if (info->nlh->nlmsg_flags & (NLM_F_REPLACE | NLM_F_EXCL))
3564+
return false;
3565+
3566+
/* nft and it only ever appends. */
3567+
if ((info->nlh->nlmsg_flags & NLM_F_APPEND) == 0)
3568+
return false;
3569+
3570+
pr_warn_once("enabling workaround for nftables 1.0.6 and older\n");
3571+
return true;
3572+
}
3573+
35543574
#define NFT_RULE_MAXEXPRS 128
35553575

35563576
static int nf_tables_newrule(struct sk_buff *skb, const struct nfnl_info *info,
@@ -3564,6 +3584,7 @@ static int nf_tables_newrule(struct sk_buff *skb, const struct nfnl_info *info,
35643584
struct nft_expr_info *expr_info = NULL;
35653585
u8 family = info->nfmsg->nfgen_family;
35663586
struct nft_flow_rule *flow = NULL;
3587+
bool add_after_bind = false;
35673588
struct net *net = info->net;
35683589
struct nft_userdata *udata;
35693590
struct nft_table *table;
@@ -3603,8 +3624,12 @@ static int nf_tables_newrule(struct sk_buff *skb, const struct nfnl_info *info,
36033624
return -EINVAL;
36043625
}
36053626

3606-
if (nft_chain_is_bound(chain))
3607-
return -EOPNOTSUPP;
3627+
if (nft_chain_is_bound(chain)) {
3628+
if (!nft_rule_work_around_old_version(info, chain))
3629+
return -EOPNOTSUPP;
3630+
3631+
add_after_bind = true;
3632+
}
36083633

36093634
if (nla[NFTA_RULE_HANDLE]) {
36103635
handle = be64_to_cpu(nla_get_be64(nla[NFTA_RULE_HANDLE]));
@@ -3749,6 +3774,9 @@ static int nf_tables_newrule(struct sk_buff *skb, const struct nfnl_info *info,
37493774
goto err_destroy_flow_rule;
37503775
}
37513776

3777+
if (add_after_bind)
3778+
nft_trans_rule_bound(trans) = true;
3779+
37523780
if (info->nlh->nlmsg_flags & NLM_F_APPEND) {
37533781
if (old_rule)
37543782
list_add_rcu(&rule->list, &old_rule->list);

0 commit comments

Comments
 (0)