Skip to content

Commit

Permalink
Merge git://git.kernel.org/pub/scm/linux/kernel/git/pablo/nf
Browse files Browse the repository at this point in the history
Pablo Neira Ayuso says:

====================
netfilter fixes for net

The following patchset contains netfilter fixes for your net tree,
they are:

1) Fix missing MODULE_LICENSE() in the new nf_reject_ipv{4,6} modules.

2) Restrict nat and masq expressions to the nat chain type. Otherwise,
   users may crash their kernel if they attach a nat/masq rule to a non
   nat chain.

3) Fix hook validation in nft_compat when non-base chains are used.
   Basically, initialize hook_mask to zero.

4) Make sure you use match/targets in nft_compat from the right chain
   type. The existing validation relies on the table name which can be
   avoided by

5) Better netlink attribute validation in nft_nat. This expression has
   to reject the configuration when no address and proto configurations
   are specified.

6) Interpret NFTA_NAT_REG_*_MAX if only if NFTA_NAT_REG_*_MIN is set.
   Yet another sanity check to reject incorrect configurations from
   userspace.

7) Conditional NAT attribute dumping depending on the existing
   configuration.
====================

Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
David S. Miller committed Oct 20, 2014
2 parents 95ff886 + 1e2d56a commit ce8ec48
Show file tree
Hide file tree
Showing 10 changed files with 165 additions and 41 deletions.
3 changes: 3 additions & 0 deletions include/net/netfilter/nf_tables.h
Original file line number Diff line number Diff line change
Expand Up @@ -530,6 +530,9 @@ enum nft_chain_type {
NFT_CHAIN_T_MAX
};

int nft_chain_validate_dependency(const struct nft_chain *chain,
enum nft_chain_type type);

struct nft_stats {
u64 bytes;
u64 pkts;
Expand Down
3 changes: 3 additions & 0 deletions include/net/netfilter/nft_masq.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,7 @@ int nft_masq_init(const struct nft_ctx *ctx,

int nft_masq_dump(struct sk_buff *skb, const struct nft_expr *expr);

int nft_masq_validate(const struct nft_ctx *ctx, const struct nft_expr *expr,
const struct nft_data **data);

#endif /* _NFT_MASQ_H_ */
3 changes: 3 additions & 0 deletions net/ipv4/netfilter/nf_reject_ipv4.c
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
* published by the Free Software Foundation.
*/

#include <linux/module.h>
#include <net/ip.h>
#include <net/tcp.h>
#include <net/route.h>
Expand Down Expand Up @@ -125,3 +126,5 @@ void nf_send_reset(struct sk_buff *oldskb, int hook)
kfree_skb(nskb);
}
EXPORT_SYMBOL_GPL(nf_send_reset);

MODULE_LICENSE("GPL");
1 change: 1 addition & 0 deletions net/ipv4/netfilter/nft_masq_ipv4.c
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ static const struct nft_expr_ops nft_masq_ipv4_ops = {
.eval = nft_masq_ipv4_eval,
.init = nft_masq_init,
.dump = nft_masq_dump,
.validate = nft_masq_validate,
};

static struct nft_expr_type nft_masq_ipv4_type __read_mostly = {
Expand Down
4 changes: 4 additions & 0 deletions net/ipv6/netfilter/nf_reject_ipv6.c
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/

#include <linux/module.h>
#include <net/ipv6.h>
#include <net/ip6_route.h>
#include <net/ip6_fib.h>
Expand Down Expand Up @@ -161,3 +163,5 @@ void nf_send_reset6(struct net *net, struct sk_buff *oldskb, int hook)
ip6_local_out(nskb);
}
EXPORT_SYMBOL_GPL(nf_send_reset6);

MODULE_LICENSE("GPL");
1 change: 1 addition & 0 deletions net/ipv6/netfilter/nft_masq_ipv6.c
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ static const struct nft_expr_ops nft_masq_ipv6_ops = {
.eval = nft_masq_ipv6_eval,
.init = nft_masq_init,
.dump = nft_masq_dump,
.validate = nft_masq_validate,
};

static struct nft_expr_type nft_masq_ipv6_type __read_mostly = {
Expand Down
14 changes: 14 additions & 0 deletions net/netfilter/nf_tables_api.c
Original file line number Diff line number Diff line change
Expand Up @@ -3744,6 +3744,20 @@ static const struct nfnetlink_subsystem nf_tables_subsys = {
.abort = nf_tables_abort,
};

int nft_chain_validate_dependency(const struct nft_chain *chain,
enum nft_chain_type type)
{
const struct nft_base_chain *basechain;

if (chain->flags & NFT_BASE_CHAIN) {
basechain = nft_base_chain(chain);
if (basechain->type->type != type)
return -EOPNOTSUPP;
}
return 0;
}
EXPORT_SYMBOL_GPL(nft_chain_validate_dependency);

/*
* Loop detection - walk through the ruleset beginning at the destination chain
* of a new jump until either the source chain is reached (loop) or all
Expand Down
79 changes: 70 additions & 9 deletions net/netfilter/nft_compat.c
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,52 @@
#include <linux/netfilter/x_tables.h>
#include <linux/netfilter_ipv4/ip_tables.h>
#include <linux/netfilter_ipv6/ip6_tables.h>
#include <asm/uaccess.h> /* for set_fs */
#include <net/netfilter/nf_tables.h>

static const struct {
const char *name;
u8 type;
} table_to_chaintype[] = {
{ "filter", NFT_CHAIN_T_DEFAULT },
{ "raw", NFT_CHAIN_T_DEFAULT },
{ "security", NFT_CHAIN_T_DEFAULT },
{ "mangle", NFT_CHAIN_T_ROUTE },
{ "nat", NFT_CHAIN_T_NAT },
{ },
};

static int nft_compat_table_to_chaintype(const char *table)
{
int i;

for (i = 0; table_to_chaintype[i].name != NULL; i++) {
if (strcmp(table_to_chaintype[i].name, table) == 0)
return table_to_chaintype[i].type;
}

return -1;
}

static int nft_compat_chain_validate_dependency(const char *tablename,
const struct nft_chain *chain)
{
enum nft_chain_type type;
const struct nft_base_chain *basechain;

if (!tablename || !(chain->flags & NFT_BASE_CHAIN))
return 0;

type = nft_compat_table_to_chaintype(tablename);
if (type < 0)
return -EINVAL;

basechain = nft_base_chain(chain);
if (basechain->type->type != type)
return -EINVAL;

return 0;
}

union nft_entry {
struct ipt_entry e4;
struct ip6t_entry e6;
Expand Down Expand Up @@ -95,6 +138,8 @@ nft_target_set_tgchk_param(struct xt_tgchk_param *par,
const struct nf_hook_ops *ops = &basechain->ops[0];

par->hook_mask = 1 << ops->hooknum;
} else {
par->hook_mask = 0;
}
par->family = ctx->afi->family;
}
Expand Down Expand Up @@ -151,6 +196,10 @@ nft_target_init(const struct nft_ctx *ctx, const struct nft_expr *expr,
union nft_entry e = {};
int ret;

ret = nft_compat_chain_validate_dependency(target->table, ctx->chain);
if (ret < 0)
goto err;

target_compat_from_user(target, nla_data(tb[NFTA_TARGET_INFO]), info);

if (ctx->nla[NFTA_RULE_COMPAT]) {
Expand Down Expand Up @@ -216,18 +265,21 @@ static int nft_target_validate(const struct nft_ctx *ctx,
{
struct xt_target *target = expr->ops->data;
unsigned int hook_mask = 0;
int ret;

if (ctx->chain->flags & NFT_BASE_CHAIN) {
const struct nft_base_chain *basechain =
nft_base_chain(ctx->chain);
const struct nf_hook_ops *ops = &basechain->ops[0];

hook_mask = 1 << ops->hooknum;
if (hook_mask & target->hooks)
return 0;
if (!(hook_mask & target->hooks))
return -EINVAL;

/* This target is being called from an invalid chain */
return -EINVAL;
ret = nft_compat_chain_validate_dependency(target->table,
ctx->chain);
if (ret < 0)
return ret;
}
return 0;
}
Expand Down Expand Up @@ -293,6 +345,8 @@ nft_match_set_mtchk_param(struct xt_mtchk_param *par, const struct nft_ctx *ctx,
const struct nf_hook_ops *ops = &basechain->ops[0];

par->hook_mask = 1 << ops->hooknum;
} else {
par->hook_mask = 0;
}
par->family = ctx->afi->family;
}
Expand Down Expand Up @@ -320,6 +374,10 @@ nft_match_init(const struct nft_ctx *ctx, const struct nft_expr *expr,
union nft_entry e = {};
int ret;

ret = nft_compat_chain_validate_dependency(match->name, ctx->chain);
if (ret < 0)
goto err;

match_compat_from_user(match, nla_data(tb[NFTA_MATCH_INFO]), info);

if (ctx->nla[NFTA_RULE_COMPAT]) {
Expand Down Expand Up @@ -379,18 +437,21 @@ static int nft_match_validate(const struct nft_ctx *ctx,
{
struct xt_match *match = expr->ops->data;
unsigned int hook_mask = 0;
int ret;

if (ctx->chain->flags & NFT_BASE_CHAIN) {
const struct nft_base_chain *basechain =
nft_base_chain(ctx->chain);
const struct nf_hook_ops *ops = &basechain->ops[0];

hook_mask = 1 << ops->hooknum;
if (hook_mask & match->hooks)
return 0;
if (!(hook_mask & match->hooks))
return -EINVAL;

/* This match is being called from an invalid chain */
return -EINVAL;
ret = nft_compat_chain_validate_dependency(match->name,
ctx->chain);
if (ret < 0)
return ret;
}
return 0;
}
Expand Down
12 changes: 12 additions & 0 deletions net/netfilter/nft_masq.c
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,11 @@ int nft_masq_init(const struct nft_ctx *ctx,
const struct nlattr * const tb[])
{
struct nft_masq *priv = nft_expr_priv(expr);
int err;

err = nft_chain_validate_dependency(ctx->chain, NFT_CHAIN_T_NAT);
if (err < 0)
return err;

if (tb[NFTA_MASQ_FLAGS] == NULL)
return 0;
Expand Down Expand Up @@ -55,5 +60,12 @@ int nft_masq_dump(struct sk_buff *skb, const struct nft_expr *expr)
}
EXPORT_SYMBOL_GPL(nft_masq_dump);

int nft_masq_validate(const struct nft_ctx *ctx, const struct nft_expr *expr,
const struct nft_data **data)
{
return nft_chain_validate_dependency(ctx->chain, NFT_CHAIN_T_NAT);
}
EXPORT_SYMBOL_GPL(nft_masq_validate);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Arturo Borrero Gonzalez <arturo.borrero.glez@gmail.com>");
86 changes: 54 additions & 32 deletions net/netfilter/nft_nat.c
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,13 @@ static int nft_nat_init(const struct nft_ctx *ctx, const struct nft_expr *expr,
u32 family;
int err;

if (tb[NFTA_NAT_TYPE] == NULL)
err = nft_chain_validate_dependency(ctx->chain, NFT_CHAIN_T_NAT);
if (err < 0)
return err;

if (tb[NFTA_NAT_TYPE] == NULL ||
(tb[NFTA_NAT_REG_ADDR_MIN] == NULL &&
tb[NFTA_NAT_REG_PROTO_MIN] == NULL))
return -EINVAL;

switch (ntohl(nla_get_be32(tb[NFTA_NAT_TYPE]))) {
Expand All @@ -120,38 +126,44 @@ static int nft_nat_init(const struct nft_ctx *ctx, const struct nft_expr *expr,
priv->family = family;

if (tb[NFTA_NAT_REG_ADDR_MIN]) {
priv->sreg_addr_min = ntohl(nla_get_be32(
tb[NFTA_NAT_REG_ADDR_MIN]));
priv->sreg_addr_min =
ntohl(nla_get_be32(tb[NFTA_NAT_REG_ADDR_MIN]));

err = nft_validate_input_register(priv->sreg_addr_min);
if (err < 0)
return err;
}

if (tb[NFTA_NAT_REG_ADDR_MAX]) {
priv->sreg_addr_max = ntohl(nla_get_be32(
tb[NFTA_NAT_REG_ADDR_MAX]));
err = nft_validate_input_register(priv->sreg_addr_max);
if (err < 0)
return err;
} else
priv->sreg_addr_max = priv->sreg_addr_min;
if (tb[NFTA_NAT_REG_ADDR_MAX]) {
priv->sreg_addr_max =
ntohl(nla_get_be32(tb[NFTA_NAT_REG_ADDR_MAX]));

err = nft_validate_input_register(priv->sreg_addr_max);
if (err < 0)
return err;
} else {
priv->sreg_addr_max = priv->sreg_addr_min;
}
}

if (tb[NFTA_NAT_REG_PROTO_MIN]) {
priv->sreg_proto_min = ntohl(nla_get_be32(
tb[NFTA_NAT_REG_PROTO_MIN]));
priv->sreg_proto_min =
ntohl(nla_get_be32(tb[NFTA_NAT_REG_PROTO_MIN]));

err = nft_validate_input_register(priv->sreg_proto_min);
if (err < 0)
return err;
}

if (tb[NFTA_NAT_REG_PROTO_MAX]) {
priv->sreg_proto_max = ntohl(nla_get_be32(
tb[NFTA_NAT_REG_PROTO_MAX]));
err = nft_validate_input_register(priv->sreg_proto_max);
if (err < 0)
return err;
} else
priv->sreg_proto_max = priv->sreg_proto_min;
if (tb[NFTA_NAT_REG_PROTO_MAX]) {
priv->sreg_proto_max =
ntohl(nla_get_be32(tb[NFTA_NAT_REG_PROTO_MAX]));

err = nft_validate_input_register(priv->sreg_proto_max);
if (err < 0)
return err;
} else {
priv->sreg_proto_max = priv->sreg_proto_min;
}
}

if (tb[NFTA_NAT_FLAGS]) {
priv->flags = ntohl(nla_get_be32(tb[NFTA_NAT_FLAGS]));
Expand Down Expand Up @@ -179,17 +191,19 @@ static int nft_nat_dump(struct sk_buff *skb, const struct nft_expr *expr)

if (nla_put_be32(skb, NFTA_NAT_FAMILY, htonl(priv->family)))
goto nla_put_failure;
if (nla_put_be32(skb,
NFTA_NAT_REG_ADDR_MIN, htonl(priv->sreg_addr_min)))
goto nla_put_failure;
if (nla_put_be32(skb,
NFTA_NAT_REG_ADDR_MAX, htonl(priv->sreg_addr_max)))
goto nla_put_failure;

if (priv->sreg_addr_min) {
if (nla_put_be32(skb, NFTA_NAT_REG_ADDR_MIN,
htonl(priv->sreg_addr_min)) ||
nla_put_be32(skb, NFTA_NAT_REG_ADDR_MAX,
htonl(priv->sreg_addr_max)))
goto nla_put_failure;
}

if (priv->sreg_proto_min) {
if (nla_put_be32(skb, NFTA_NAT_REG_PROTO_MIN,
htonl(priv->sreg_proto_min)))
goto nla_put_failure;
if (nla_put_be32(skb, NFTA_NAT_REG_PROTO_MAX,
htonl(priv->sreg_proto_min)) ||
nla_put_be32(skb, NFTA_NAT_REG_PROTO_MAX,
htonl(priv->sreg_proto_max)))
goto nla_put_failure;
}
Expand All @@ -205,13 +219,21 @@ static int nft_nat_dump(struct sk_buff *skb, const struct nft_expr *expr)
return -1;
}

static int nft_nat_validate(const struct nft_ctx *ctx,
const struct nft_expr *expr,
const struct nft_data **data)
{
return nft_chain_validate_dependency(ctx->chain, NFT_CHAIN_T_NAT);
}

static struct nft_expr_type nft_nat_type;
static const struct nft_expr_ops nft_nat_ops = {
.type = &nft_nat_type,
.size = NFT_EXPR_SIZE(sizeof(struct nft_nat)),
.eval = nft_nat_eval,
.init = nft_nat_init,
.dump = nft_nat_dump,
.validate = nft_nat_validate,
};

static struct nft_expr_type nft_nat_type __read_mostly = {
Expand Down

0 comments on commit ce8ec48

Please sign in to comment.