Skip to content

Commit c0391b6

Browse files
committed
netfilter: nf_tables: missing validation from the abort path
If userspace does not include the trailing end of batch message, then nfnetlink aborts the transaction. This allows to check that ruleset updates trigger no errors. After this patch, invoking this command from the prerouting chain: # nft -c add rule x y fib saddr . oif type local fails since oif is not supported there. This patch fixes the lack of rule validation from the abort/check path to catch configuration errors such as the one above. Fixes: a654de8 ("netfilter: nf_tables: fix chain dependency validation") Signed-off-by: Pablo Neira Ayuso <[email protected]>
1 parent 46d6c5a commit c0391b6

File tree

3 files changed

+36
-10
lines changed

3 files changed

+36
-10
lines changed

include/linux/netfilter/nfnetlink.h

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,14 +24,21 @@ struct nfnl_callback {
2424
const u_int16_t attr_count; /* number of nlattr's */
2525
};
2626

27+
enum nfnl_abort_action {
28+
NFNL_ABORT_NONE = 0,
29+
NFNL_ABORT_AUTOLOAD,
30+
NFNL_ABORT_VALIDATE,
31+
};
32+
2733
struct nfnetlink_subsystem {
2834
const char *name;
2935
__u8 subsys_id; /* nfnetlink subsystem ID */
3036
__u8 cb_count; /* number of callbacks */
3137
const struct nfnl_callback *cb; /* callback for individual types */
3238
struct module *owner;
3339
int (*commit)(struct net *net, struct sk_buff *skb);
34-
int (*abort)(struct net *net, struct sk_buff *skb, bool autoload);
40+
int (*abort)(struct net *net, struct sk_buff *skb,
41+
enum nfnl_abort_action action);
3542
void (*cleanup)(struct net *net);
3643
bool (*valid_genid)(struct net *net, u32 genid);
3744
};

net/netfilter/nf_tables_api.c

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8053,12 +8053,16 @@ static void nf_tables_abort_release(struct nft_trans *trans)
80538053
kfree(trans);
80548054
}
80558055

8056-
static int __nf_tables_abort(struct net *net, bool autoload)
8056+
static int __nf_tables_abort(struct net *net, enum nfnl_abort_action action)
80578057
{
80588058
struct nft_trans *trans, *next;
80598059
struct nft_trans_elem *te;
80608060
struct nft_hook *hook;
80618061

8062+
if (action == NFNL_ABORT_VALIDATE &&
8063+
nf_tables_validate(net) < 0)
8064+
return -EAGAIN;
8065+
80628066
list_for_each_entry_safe_reverse(trans, next, &net->nft.commit_list,
80638067
list) {
80648068
switch (trans->msg_type) {
@@ -8190,7 +8194,7 @@ static int __nf_tables_abort(struct net *net, bool autoload)
81908194
nf_tables_abort_release(trans);
81918195
}
81928196

8193-
if (autoload)
8197+
if (action == NFNL_ABORT_AUTOLOAD)
81948198
nf_tables_module_autoload(net);
81958199
else
81968200
nf_tables_module_autoload_cleanup(net);
@@ -8203,9 +8207,10 @@ static void nf_tables_cleanup(struct net *net)
82038207
nft_validate_state_update(net, NFT_VALIDATE_SKIP);
82048208
}
82058209

8206-
static int nf_tables_abort(struct net *net, struct sk_buff *skb, bool autoload)
8210+
static int nf_tables_abort(struct net *net, struct sk_buff *skb,
8211+
enum nfnl_abort_action action)
82078212
{
8208-
int ret = __nf_tables_abort(net, autoload);
8213+
int ret = __nf_tables_abort(net, action);
82098214

82108215
mutex_unlock(&net->nft.commit_mutex);
82118216

@@ -8836,7 +8841,7 @@ static void __net_exit nf_tables_exit_net(struct net *net)
88368841
{
88378842
mutex_lock(&net->nft.commit_mutex);
88388843
if (!list_empty(&net->nft.commit_list))
8839-
__nf_tables_abort(net, false);
8844+
__nf_tables_abort(net, NFNL_ABORT_NONE);
88408845
__nft_release_tables(net);
88418846
mutex_unlock(&net->nft.commit_mutex);
88428847
WARN_ON_ONCE(!list_empty(&net->nft.tables));

net/netfilter/nfnetlink.c

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -333,7 +333,7 @@ static void nfnetlink_rcv_batch(struct sk_buff *skb, struct nlmsghdr *nlh,
333333
return netlink_ack(skb, nlh, -EINVAL, NULL);
334334
replay:
335335
status = 0;
336-
336+
replay_abort:
337337
skb = netlink_skb_clone(oskb, GFP_KERNEL);
338338
if (!skb)
339339
return netlink_ack(oskb, nlh, -ENOMEM, NULL);
@@ -499,7 +499,7 @@ static void nfnetlink_rcv_batch(struct sk_buff *skb, struct nlmsghdr *nlh,
499499
}
500500
done:
501501
if (status & NFNL_BATCH_REPLAY) {
502-
ss->abort(net, oskb, true);
502+
ss->abort(net, oskb, NFNL_ABORT_AUTOLOAD);
503503
nfnl_err_reset(&err_list);
504504
kfree_skb(skb);
505505
module_put(ss->owner);
@@ -510,11 +510,25 @@ static void nfnetlink_rcv_batch(struct sk_buff *skb, struct nlmsghdr *nlh,
510510
status |= NFNL_BATCH_REPLAY;
511511
goto done;
512512
} else if (err) {
513-
ss->abort(net, oskb, false);
513+
ss->abort(net, oskb, NFNL_ABORT_NONE);
514514
netlink_ack(oskb, nlmsg_hdr(oskb), err, NULL);
515515
}
516516
} else {
517-
ss->abort(net, oskb, false);
517+
enum nfnl_abort_action abort_action;
518+
519+
if (status & NFNL_BATCH_FAILURE)
520+
abort_action = NFNL_ABORT_NONE;
521+
else
522+
abort_action = NFNL_ABORT_VALIDATE;
523+
524+
err = ss->abort(net, oskb, abort_action);
525+
if (err == -EAGAIN) {
526+
nfnl_err_reset(&err_list);
527+
kfree_skb(skb);
528+
module_put(ss->owner);
529+
status |= NFNL_BATCH_FAILURE;
530+
goto replay_abort;
531+
}
518532
}
519533
if (ss->cleanup)
520534
ss->cleanup(net);

0 commit comments

Comments
 (0)