]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/blob
4c50a96d9e04167a541c340375d1e1669e56298e
[thirdparty/kernel/stable-queue.git] /
1 From d6dad3d8cc56f69645aadd6204af0f1b39258f04 Mon Sep 17 00:00:00 2001
2 From: Sasha Levin <sashal@kernel.org>
3 Date: Thu, 29 Oct 2020 13:50:03 +0100
4 Subject: netfilter: nf_tables: missing validation from the abort path
5
6 From: Pablo Neira Ayuso <pablo@netfilter.org>
7
8 [ Upstream commit c0391b6ab810381df632677a1dcbbbbd63d05b6d ]
9
10 If userspace does not include the trailing end of batch message, then
11 nfnetlink aborts the transaction. This allows to check that ruleset
12 updates trigger no errors.
13
14 After this patch, invoking this command from the prerouting chain:
15
16 # nft -c add rule x y fib saddr . oif type local
17
18 fails since oif is not supported there.
19
20 This patch fixes the lack of rule validation from the abort/check path
21 to catch configuration errors such as the one above.
22
23 Fixes: a654de8fdc18 ("netfilter: nf_tables: fix chain dependency validation")
24 Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
25 Signed-off-by: Sasha Levin <sashal@kernel.org>
26 ---
27 include/linux/netfilter/nfnetlink.h | 9 ++++++++-
28 net/netfilter/nf_tables_api.c | 15 ++++++++++-----
29 net/netfilter/nfnetlink.c | 22 ++++++++++++++++++----
30 3 files changed, 36 insertions(+), 10 deletions(-)
31
32 diff --git a/include/linux/netfilter/nfnetlink.h b/include/linux/netfilter/nfnetlink.h
33 index 89016d08f6a27..f6267e2883f26 100644
34 --- a/include/linux/netfilter/nfnetlink.h
35 +++ b/include/linux/netfilter/nfnetlink.h
36 @@ -24,6 +24,12 @@ struct nfnl_callback {
37 const u_int16_t attr_count; /* number of nlattr's */
38 };
39
40 +enum nfnl_abort_action {
41 + NFNL_ABORT_NONE = 0,
42 + NFNL_ABORT_AUTOLOAD,
43 + NFNL_ABORT_VALIDATE,
44 +};
45 +
46 struct nfnetlink_subsystem {
47 const char *name;
48 __u8 subsys_id; /* nfnetlink subsystem ID */
49 @@ -31,7 +37,8 @@ struct nfnetlink_subsystem {
50 const struct nfnl_callback *cb; /* callback for individual types */
51 struct module *owner;
52 int (*commit)(struct net *net, struct sk_buff *skb);
53 - int (*abort)(struct net *net, struct sk_buff *skb, bool autoload);
54 + int (*abort)(struct net *net, struct sk_buff *skb,
55 + enum nfnl_abort_action action);
56 void (*cleanup)(struct net *net);
57 bool (*valid_genid)(struct net *net, u32 genid);
58 };
59 diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c
60 index 1c90bd1fce60c..4305d96334082 100644
61 --- a/net/netfilter/nf_tables_api.c
62 +++ b/net/netfilter/nf_tables_api.c
63 @@ -7992,12 +7992,16 @@ static void nf_tables_abort_release(struct nft_trans *trans)
64 kfree(trans);
65 }
66
67 -static int __nf_tables_abort(struct net *net, bool autoload)
68 +static int __nf_tables_abort(struct net *net, enum nfnl_abort_action action)
69 {
70 struct nft_trans *trans, *next;
71 struct nft_trans_elem *te;
72 struct nft_hook *hook;
73
74 + if (action == NFNL_ABORT_VALIDATE &&
75 + nf_tables_validate(net) < 0)
76 + return -EAGAIN;
77 +
78 list_for_each_entry_safe_reverse(trans, next, &net->nft.commit_list,
79 list) {
80 switch (trans->msg_type) {
81 @@ -8129,7 +8133,7 @@ static int __nf_tables_abort(struct net *net, bool autoload)
82 nf_tables_abort_release(trans);
83 }
84
85 - if (autoload)
86 + if (action == NFNL_ABORT_AUTOLOAD)
87 nf_tables_module_autoload(net);
88 else
89 nf_tables_module_autoload_cleanup(net);
90 @@ -8142,9 +8146,10 @@ static void nf_tables_cleanup(struct net *net)
91 nft_validate_state_update(net, NFT_VALIDATE_SKIP);
92 }
93
94 -static int nf_tables_abort(struct net *net, struct sk_buff *skb, bool autoload)
95 +static int nf_tables_abort(struct net *net, struct sk_buff *skb,
96 + enum nfnl_abort_action action)
97 {
98 - int ret = __nf_tables_abort(net, autoload);
99 + int ret = __nf_tables_abort(net, action);
100
101 mutex_unlock(&net->nft.commit_mutex);
102
103 @@ -8775,7 +8780,7 @@ static void __net_exit nf_tables_exit_net(struct net *net)
104 {
105 mutex_lock(&net->nft.commit_mutex);
106 if (!list_empty(&net->nft.commit_list))
107 - __nf_tables_abort(net, false);
108 + __nf_tables_abort(net, NFNL_ABORT_NONE);
109 __nft_release_tables(net);
110 mutex_unlock(&net->nft.commit_mutex);
111 WARN_ON_ONCE(!list_empty(&net->nft.tables));
112 diff --git a/net/netfilter/nfnetlink.c b/net/netfilter/nfnetlink.c
113 index 3a2e64e13b227..212c37f53f5f4 100644
114 --- a/net/netfilter/nfnetlink.c
115 +++ b/net/netfilter/nfnetlink.c
116 @@ -316,7 +316,7 @@ static void nfnetlink_rcv_batch(struct sk_buff *skb, struct nlmsghdr *nlh,
117 return netlink_ack(skb, nlh, -EINVAL, NULL);
118 replay:
119 status = 0;
120 -
121 +replay_abort:
122 skb = netlink_skb_clone(oskb, GFP_KERNEL);
123 if (!skb)
124 return netlink_ack(oskb, nlh, -ENOMEM, NULL);
125 @@ -482,7 +482,7 @@ ack:
126 }
127 done:
128 if (status & NFNL_BATCH_REPLAY) {
129 - ss->abort(net, oskb, true);
130 + ss->abort(net, oskb, NFNL_ABORT_AUTOLOAD);
131 nfnl_err_reset(&err_list);
132 kfree_skb(skb);
133 module_put(ss->owner);
134 @@ -493,11 +493,25 @@ done:
135 status |= NFNL_BATCH_REPLAY;
136 goto done;
137 } else if (err) {
138 - ss->abort(net, oskb, false);
139 + ss->abort(net, oskb, NFNL_ABORT_NONE);
140 netlink_ack(oskb, nlmsg_hdr(oskb), err, NULL);
141 }
142 } else {
143 - ss->abort(net, oskb, false);
144 + enum nfnl_abort_action abort_action;
145 +
146 + if (status & NFNL_BATCH_FAILURE)
147 + abort_action = NFNL_ABORT_NONE;
148 + else
149 + abort_action = NFNL_ABORT_VALIDATE;
150 +
151 + err = ss->abort(net, oskb, abort_action);
152 + if (err == -EAGAIN) {
153 + nfnl_err_reset(&err_list);
154 + kfree_skb(skb);
155 + module_put(ss->owner);
156 + status |= NFNL_BATCH_FAILURE;
157 + goto replay_abort;
158 + }
159 }
160 if (ss->cleanup)
161 ss->cleanup(net);
162 --
163 2.27.0
164