aboutsummaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
authorPablo Neira Ayuso <pablo@netfilter.org>2014-10-14 04:13:48 -0400
committerPablo Neira Ayuso <pablo@netfilter.org>2014-10-18 08:14:07 -0400
commitf3f5ddeddd6aeadcef523d55ea9288e3d5c1cbc3 (patch)
tree8515ff30729229052cd91d69a49322424a05290b /net
parent493618a92c6afdd3f6224ab586f169d6a259bb06 (diff)
netfilter: nft_compat: validate chain type in match/target
We have to validate the real chain type to ensure that matches/targets are not used out from their scope (eg. MASQUERADE in nat chain type). The existing validation relies on the table name, but this is not sufficient since userspace can fool us by using the appropriate table name with a different chain type. Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
Diffstat (limited to 'net')
-rw-r--r--net/netfilter/nft_compat.c75
1 files changed, 66 insertions, 9 deletions
diff --git a/net/netfilter/nft_compat.c b/net/netfilter/nft_compat.c
index 44ae273b4391..0480f57a4eb6 100644
--- a/net/netfilter/nft_compat.c
+++ b/net/netfilter/nft_compat.c
@@ -19,9 +19,52 @@
19#include <linux/netfilter/x_tables.h> 19#include <linux/netfilter/x_tables.h>
20#include <linux/netfilter_ipv4/ip_tables.h> 20#include <linux/netfilter_ipv4/ip_tables.h>
21#include <linux/netfilter_ipv6/ip6_tables.h> 21#include <linux/netfilter_ipv6/ip6_tables.h>
22#include <asm/uaccess.h> /* for set_fs */
23#include <net/netfilter/nf_tables.h> 22#include <net/netfilter/nf_tables.h>
24 23
24static const struct {
25 const char *name;
26 u8 type;
27} table_to_chaintype[] = {
28 { "filter", NFT_CHAIN_T_DEFAULT },
29 { "raw", NFT_CHAIN_T_DEFAULT },
30 { "security", NFT_CHAIN_T_DEFAULT },
31 { "mangle", NFT_CHAIN_T_ROUTE },
32 { "nat", NFT_CHAIN_T_NAT },
33 { },
34};
35
36static int nft_compat_table_to_chaintype(const char *table)
37{
38 int i;
39
40 for (i = 0; table_to_chaintype[i].name != NULL; i++) {
41 if (strcmp(table_to_chaintype[i].name, table) == 0)
42 return table_to_chaintype[i].type;
43 }
44
45 return -1;
46}
47
48static int nft_compat_chain_validate_dependency(const char *tablename,
49 const struct nft_chain *chain)
50{
51 enum nft_chain_type type;
52 const struct nft_base_chain *basechain;
53
54 if (!tablename || !(chain->flags & NFT_BASE_CHAIN))
55 return 0;
56
57 type = nft_compat_table_to_chaintype(tablename);
58 if (type < 0)
59 return -EINVAL;
60
61 basechain = nft_base_chain(chain);
62 if (basechain->type->type != type)
63 return -EINVAL;
64
65 return 0;
66}
67
25union nft_entry { 68union nft_entry {
26 struct ipt_entry e4; 69 struct ipt_entry e4;
27 struct ip6t_entry e6; 70 struct ip6t_entry e6;
@@ -153,6 +196,10 @@ nft_target_init(const struct nft_ctx *ctx, const struct nft_expr *expr,
153 union nft_entry e = {}; 196 union nft_entry e = {};
154 int ret; 197 int ret;
155 198
199 ret = nft_compat_chain_validate_dependency(target->table, ctx->chain);
200 if (ret < 0)
201 goto err;
202
156 target_compat_from_user(target, nla_data(tb[NFTA_TARGET_INFO]), info); 203 target_compat_from_user(target, nla_data(tb[NFTA_TARGET_INFO]), info);
157 204
158 if (ctx->nla[NFTA_RULE_COMPAT]) { 205 if (ctx->nla[NFTA_RULE_COMPAT]) {
@@ -218,6 +265,7 @@ static int nft_target_validate(const struct nft_ctx *ctx,
218{ 265{
219 struct xt_target *target = expr->ops->data; 266 struct xt_target *target = expr->ops->data;
220 unsigned int hook_mask = 0; 267 unsigned int hook_mask = 0;
268 int ret;
221 269
222 if (ctx->chain->flags & NFT_BASE_CHAIN) { 270 if (ctx->chain->flags & NFT_BASE_CHAIN) {
223 const struct nft_base_chain *basechain = 271 const struct nft_base_chain *basechain =
@@ -225,11 +273,13 @@ static int nft_target_validate(const struct nft_ctx *ctx,
225 const struct nf_hook_ops *ops = &basechain->ops[0]; 273 const struct nf_hook_ops *ops = &basechain->ops[0];
226 274
227 hook_mask = 1 << ops->hooknum; 275 hook_mask = 1 << ops->hooknum;
228 if (hook_mask & target->hooks) 276 if (!(hook_mask & target->hooks))
229 return 0; 277 return -EINVAL;
230 278
231 /* This target is being called from an invalid chain */ 279 ret = nft_compat_chain_validate_dependency(target->table,
232 return -EINVAL; 280 ctx->chain);
281 if (ret < 0)
282 return ret;
233 } 283 }
234 return 0; 284 return 0;
235} 285}
@@ -324,6 +374,10 @@ nft_match_init(const struct nft_ctx *ctx, const struct nft_expr *expr,
324 union nft_entry e = {}; 374 union nft_entry e = {};
325 int ret; 375 int ret;
326 376
377 ret = nft_compat_chain_validate_dependency(match->name, ctx->chain);
378 if (ret < 0)
379 goto err;
380
327 match_compat_from_user(match, nla_data(tb[NFTA_MATCH_INFO]), info); 381 match_compat_from_user(match, nla_data(tb[NFTA_MATCH_INFO]), info);
328 382
329 if (ctx->nla[NFTA_RULE_COMPAT]) { 383 if (ctx->nla[NFTA_RULE_COMPAT]) {
@@ -383,6 +437,7 @@ static int nft_match_validate(const struct nft_ctx *ctx,
383{ 437{
384 struct xt_match *match = expr->ops->data; 438 struct xt_match *match = expr->ops->data;
385 unsigned int hook_mask = 0; 439 unsigned int hook_mask = 0;
440 int ret;
386 441
387 if (ctx->chain->flags & NFT_BASE_CHAIN) { 442 if (ctx->chain->flags & NFT_BASE_CHAIN) {
388 const struct nft_base_chain *basechain = 443 const struct nft_base_chain *basechain =
@@ -390,11 +445,13 @@ static int nft_match_validate(const struct nft_ctx *ctx,
390 const struct nf_hook_ops *ops = &basechain->ops[0]; 445 const struct nf_hook_ops *ops = &basechain->ops[0];
391 446
392 hook_mask = 1 << ops->hooknum; 447 hook_mask = 1 << ops->hooknum;
393 if (hook_mask & match->hooks) 448 if (!(hook_mask & match->hooks))
394 return 0; 449 return -EINVAL;
395 450
396 /* This match is being called from an invalid chain */ 451 ret = nft_compat_chain_validate_dependency(match->name,
397 return -EINVAL; 452 ctx->chain);
453 if (ret < 0)
454 return ret;
398 } 455 }
399 return 0; 456 return 0;
400} 457}