Skip to content

Commit a654de8

Browse files
committed
netfilter: nf_tables: fix chain dependency validation
The following ruleset: add table ip filter add chain ip filter input { type filter hook input priority 4; } add chain ip filter ap add rule ip filter input jump ap add rule ip filter ap masquerade results in a panic, because the masquerade extension should be rejected from the filter chain. The existing validation is missing a chain dependency check when the rule is added to the non-base chain. This patch fixes the problem by walking down the rules from the basechains, searching for either immediate or lookup expressions, then jumping to non-base chains and again walking down the rules to perform the expression validation, so we make sure the full ruleset graph is validated. This is done only once from the commit phase, in case of problem, we abort the transaction and perform fine grain validation for error reporting. This patch requires 0030879 ("netfilter: nfnetlink: allow commit to fail") to achieve this behaviour. This patch also adds a cleanup callback to nfnl batch interface to reset the validate state from the exit path. As a result of this patch, nf_tables_check_loops() doesn't use ->validate to check for loops, instead it just checks for immediate expressions. Reported-by: Taehee Yoo <[email protected]> Signed-off-by: Pablo Neira Ayuso <[email protected]>
1 parent 1a893b4 commit a654de8

File tree

8 files changed

+208
-32
lines changed

8 files changed

+208
-32
lines changed

include/linux/netfilter/nfnetlink.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ struct nfnetlink_subsystem {
3131
const struct nfnl_callback *cb; /* callback for individual types */
3232
int (*commit)(struct net *net, struct sk_buff *skb);
3333
int (*abort)(struct net *net, struct sk_buff *skb);
34+
void (*cleanup)(struct net *net);
3435
bool (*valid_genid)(struct net *net, u32 genid);
3536
};
3637

include/net/netfilter/nf_tables.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -874,6 +874,8 @@ struct nft_chain {
874874
struct nft_rule **rules_next;
875875
};
876876

877+
int nft_chain_validate(const struct nft_ctx *ctx, const struct nft_chain *chain);
878+
877879
enum nft_chain_types {
878880
NFT_CHAIN_T_DEFAULT = 0,
879881
NFT_CHAIN_T_ROUTE,

include/net/netfilter/nf_tables_core.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
#ifndef _NET_NF_TABLES_CORE_H
33
#define _NET_NF_TABLES_CORE_H
44

5+
#include <net/netfilter/nf_tables.h>
6+
57
extern struct nft_expr_type nft_imm_type;
68
extern struct nft_expr_type nft_cmp_type;
79
extern struct nft_expr_type nft_lookup_type;
@@ -23,6 +25,12 @@ struct nft_cmp_fast_expr {
2325
u8 len;
2426
};
2527

28+
struct nft_immediate_expr {
29+
struct nft_data data;
30+
enum nft_registers dreg:8;
31+
u8 dlen;
32+
};
33+
2634
/* Calculate the mask for the nft_cmp_fast expression. On big endian the
2735
* mask needs to include the *upper* bytes when interpreting that data as
2836
* something smaller than the full u32, therefore a cpu_to_le32 is done.

include/net/netns/nftables.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ struct netns_nftables {
99
struct list_head commit_list;
1010
unsigned int base_seq;
1111
u8 gencursor;
12+
u8 validate_state;
1213
};
1314

1415
#endif

net/netfilter/nf_tables_api.c

Lines changed: 129 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,28 @@ static LIST_HEAD(nf_tables_objects);
2828
static LIST_HEAD(nf_tables_flowtables);
2929
static u64 table_handle;
3030

31+
enum {
32+
NFT_VALIDATE_SKIP = 0,
33+
NFT_VALIDATE_NEED,
34+
NFT_VALIDATE_DO,
35+
};
36+
37+
static void nft_validate_state_update(struct net *net, u8 new_validate_state)
38+
{
39+
switch (net->nft.validate_state) {
40+
case NFT_VALIDATE_SKIP:
41+
WARN_ON_ONCE(new_validate_state == NFT_VALIDATE_DO);
42+
break;
43+
case NFT_VALIDATE_NEED:
44+
break;
45+
case NFT_VALIDATE_DO:
46+
if (new_validate_state == NFT_VALIDATE_NEED)
47+
return;
48+
}
49+
50+
net->nft.validate_state = new_validate_state;
51+
}
52+
3153
static void nft_ctx_init(struct nft_ctx *ctx,
3254
struct net *net,
3355
const struct sk_buff *skb,
@@ -1921,19 +1943,7 @@ static int nf_tables_newexpr(const struct nft_ctx *ctx,
19211943
goto err1;
19221944
}
19231945

1924-
if (ops->validate) {
1925-
const struct nft_data *data = NULL;
1926-
1927-
err = ops->validate(ctx, expr, &data);
1928-
if (err < 0)
1929-
goto err2;
1930-
}
1931-
19321946
return 0;
1933-
1934-
err2:
1935-
if (ops->destroy)
1936-
ops->destroy(ctx, expr);
19371947
err1:
19381948
expr->ops = NULL;
19391949
return err;
@@ -2299,6 +2309,53 @@ static void nf_tables_rule_release(const struct nft_ctx *ctx,
22992309
nf_tables_rule_destroy(ctx, rule);
23002310
}
23012311

2312+
int nft_chain_validate(const struct nft_ctx *ctx, const struct nft_chain *chain)
2313+
{
2314+
struct nft_expr *expr, *last;
2315+
const struct nft_data *data;
2316+
struct nft_rule *rule;
2317+
int err;
2318+
2319+
list_for_each_entry(rule, &chain->rules, list) {
2320+
if (!nft_is_active_next(ctx->net, rule))
2321+
continue;
2322+
2323+
nft_rule_for_each_expr(expr, last, rule) {
2324+
if (!expr->ops->validate)
2325+
continue;
2326+
2327+
err = expr->ops->validate(ctx, expr, &data);
2328+
if (err < 0)
2329+
return err;
2330+
}
2331+
}
2332+
2333+
return 0;
2334+
}
2335+
EXPORT_SYMBOL_GPL(nft_chain_validate);
2336+
2337+
static int nft_table_validate(struct net *net, const struct nft_table *table)
2338+
{
2339+
struct nft_chain *chain;
2340+
struct nft_ctx ctx = {
2341+
.net = net,
2342+
.family = table->family,
2343+
};
2344+
int err;
2345+
2346+
list_for_each_entry(chain, &table->chains, list) {
2347+
if (!nft_is_base_chain(chain))
2348+
continue;
2349+
2350+
ctx.chain = chain;
2351+
err = nft_chain_validate(&ctx, chain);
2352+
if (err < 0)
2353+
return err;
2354+
}
2355+
2356+
return 0;
2357+
}
2358+
23022359
#define NFT_RULE_MAXEXPRS 128
23032360

23042361
static struct nft_expr_info *info;
@@ -2426,6 +2483,10 @@ static int nf_tables_newrule(struct net *net, struct sock *nlsk,
24262483
err = nf_tables_newexpr(&ctx, &info[i], expr);
24272484
if (err < 0)
24282485
goto err2;
2486+
2487+
if (info[i].ops->validate)
2488+
nft_validate_state_update(net, NFT_VALIDATE_NEED);
2489+
24292490
info[i].ops = NULL;
24302491
expr = nft_expr_next(expr);
24312492
}
@@ -2469,8 +2530,11 @@ static int nf_tables_newrule(struct net *net, struct sock *nlsk,
24692530
}
24702531
}
24712532
chain->use++;
2472-
return 0;
24732533

2534+
if (net->nft.validate_state == NFT_VALIDATE_DO)
2535+
return nft_table_validate(net, table);
2536+
2537+
return 0;
24742538
err2:
24752539
nf_tables_rule_release(&ctx, rule);
24762540
err1:
@@ -4112,6 +4176,12 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set,
41124176
d2.type, d2.len);
41134177
if (err < 0)
41144178
goto err3;
4179+
4180+
if (d2.type == NFT_DATA_VERDICT &&
4181+
(data.verdict.code == NFT_GOTO ||
4182+
data.verdict.code == NFT_JUMP))
4183+
nft_validate_state_update(ctx->net,
4184+
NFT_VALIDATE_NEED);
41154185
}
41164186

41174187
nft_set_ext_add_length(&tmpl, NFT_SET_EXT_DATA, d2.len);
@@ -4211,7 +4281,7 @@ static int nf_tables_newsetelem(struct net *net, struct sock *nlsk,
42114281
const struct nlattr *attr;
42124282
struct nft_set *set;
42134283
struct nft_ctx ctx;
4214-
int rem, err = 0;
4284+
int rem, err;
42154285

42164286
if (nla[NFTA_SET_ELEM_LIST_ELEMENTS] == NULL)
42174287
return -EINVAL;
@@ -4232,9 +4302,13 @@ static int nf_tables_newsetelem(struct net *net, struct sock *nlsk,
42324302
nla_for_each_nested(attr, nla[NFTA_SET_ELEM_LIST_ELEMENTS], rem) {
42334303
err = nft_add_set_elem(&ctx, set, attr, nlh->nlmsg_flags);
42344304
if (err < 0)
4235-
break;
4305+
return err;
42364306
}
4237-
return err;
4307+
4308+
if (net->nft.validate_state == NFT_VALIDATE_DO)
4309+
return nft_table_validate(net, ctx.table);
4310+
4311+
return 0;
42384312
}
42394313

42404314
/**
@@ -5867,6 +5941,27 @@ static const struct nfnl_callback nf_tables_cb[NFT_MSG_MAX] = {
58675941
},
58685942
};
58695943

5944+
static int nf_tables_validate(struct net *net)
5945+
{
5946+
struct nft_table *table;
5947+
5948+
switch (net->nft.validate_state) {
5949+
case NFT_VALIDATE_SKIP:
5950+
break;
5951+
case NFT_VALIDATE_NEED:
5952+
nft_validate_state_update(net, NFT_VALIDATE_DO);
5953+
/* fall through */
5954+
case NFT_VALIDATE_DO:
5955+
list_for_each_entry(table, &net->nft.tables, list) {
5956+
if (nft_table_validate(net, table) < 0)
5957+
return -EAGAIN;
5958+
}
5959+
break;
5960+
}
5961+
5962+
return 0;
5963+
}
5964+
58705965
static void nft_chain_commit_update(struct nft_trans *trans)
58715966
{
58725967
struct nft_base_chain *basechain;
@@ -6055,6 +6150,10 @@ static int nf_tables_commit(struct net *net, struct sk_buff *skb)
60556150
struct nft_chain *chain;
60566151
struct nft_table *table;
60576152

6153+
/* 0. Validate ruleset, otherwise roll back for error reporting. */
6154+
if (nf_tables_validate(net) < 0)
6155+
return -EAGAIN;
6156+
60586157
/* 1. Allocate space for next generation rules_gen_X[] */
60596158
list_for_each_entry_safe(trans, next, &net->nft.commit_list, list) {
60606159
int ret;
@@ -6349,6 +6448,11 @@ static int nf_tables_abort(struct net *net, struct sk_buff *skb)
63496448
return 0;
63506449
}
63516450

6451+
static void nf_tables_cleanup(struct net *net)
6452+
{
6453+
nft_validate_state_update(net, NFT_VALIDATE_SKIP);
6454+
}
6455+
63526456
static bool nf_tables_valid_genid(struct net *net, u32 genid)
63536457
{
63546458
return net->nft.base_seq == genid;
@@ -6361,6 +6465,7 @@ static const struct nfnetlink_subsystem nf_tables_subsys = {
63616465
.cb = nf_tables_cb,
63626466
.commit = nf_tables_commit,
63636467
.abort = nf_tables_abort,
6468+
.cleanup = nf_tables_cleanup,
63646469
.valid_genid = nf_tables_valid_genid,
63656470
};
63666471

@@ -6444,19 +6549,18 @@ static int nf_tables_check_loops(const struct nft_ctx *ctx,
64446549

64456550
list_for_each_entry(rule, &chain->rules, list) {
64466551
nft_rule_for_each_expr(expr, last, rule) {
6447-
const struct nft_data *data = NULL;
6552+
struct nft_immediate_expr *priv;
6553+
const struct nft_data *data;
64486554
int err;
64496555

6450-
if (!expr->ops->validate)
6556+
if (strcmp(expr->ops->type->name, "immediate"))
64516557
continue;
64526558

6453-
err = expr->ops->validate(ctx, expr, &data);
6454-
if (err < 0)
6455-
return err;
6456-
6457-
if (data == NULL)
6559+
priv = nft_expr_priv(expr);
6560+
if (priv->dreg != NFT_REG_VERDICT)
64586561
continue;
64596562

6563+
data = &priv->data;
64606564
switch (data->verdict.code) {
64616565
case NFT_JUMP:
64626566
case NFT_GOTO:
@@ -6936,6 +7040,8 @@ static int __net_init nf_tables_init_net(struct net *net)
69367040
INIT_LIST_HEAD(&net->nft.tables);
69377041
INIT_LIST_HEAD(&net->nft.commit_list);
69387042
net->nft.base_seq = 1;
7043+
net->nft.validate_state = NFT_VALIDATE_SKIP;
7044+
69397045
return 0;
69407046
}
69417047

net/netfilter/nfnetlink.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -460,6 +460,8 @@ static void nfnetlink_rcv_batch(struct sk_buff *skb, struct nlmsghdr *nlh,
460460
} else {
461461
ss->abort(net, oskb);
462462
}
463+
if (ss->cleanup)
464+
ss->cleanup(net);
463465

464466
nfnl_err_deliver(&err_list, oskb);
465467
nfnl_unlock(subsys_id);

net/netfilter/nft_immediate.c

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,6 @@
1717
#include <net/netfilter/nf_tables_core.h>
1818
#include <net/netfilter/nf_tables.h>
1919

20-
struct nft_immediate_expr {
21-
struct nft_data data;
22-
enum nft_registers dreg:8;
23-
u8 dlen;
24-
};
25-
2620
static void nft_immediate_eval(const struct nft_expr *expr,
2721
struct nft_regs *regs,
2822
const struct nft_pktinfo *pkt)
@@ -101,12 +95,27 @@ static int nft_immediate_dump(struct sk_buff *skb, const struct nft_expr *expr)
10195

10296
static int nft_immediate_validate(const struct nft_ctx *ctx,
10397
const struct nft_expr *expr,
104-
const struct nft_data **data)
98+
const struct nft_data **d)
10599
{
106100
const struct nft_immediate_expr *priv = nft_expr_priv(expr);
101+
const struct nft_data *data;
102+
int err;
107103

108-
if (priv->dreg == NFT_REG_VERDICT)
109-
*data = &priv->data;
104+
if (priv->dreg != NFT_REG_VERDICT)
105+
return 0;
106+
107+
data = &priv->data;
108+
109+
switch (data->verdict.code) {
110+
case NFT_JUMP:
111+
case NFT_GOTO:
112+
err = nft_chain_validate(ctx, data->verdict.chain);
113+
if (err < 0)
114+
return err;
115+
break;
116+
default:
117+
break;
118+
}
110119

111120
return 0;
112121
}

0 commit comments

Comments
 (0)