Merge git://git.kernel.org/pub/scm/linux/kernel/git/pablo/nf

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>
This commit is contained in:
David S. Miller 2014-10-20 11:57:47 -04:00
commit ce8ec48967
10 changed files with 165 additions and 41 deletions

View File

@ -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;

View File

@ -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_ */

View File

@ -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>
@ -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");

View File

@ -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 = {

View File

@ -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>
@ -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");

View File

@ -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 = {

View File

@ -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

View File

@ -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;
@ -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;
}
@ -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]) {
@ -216,6 +265,7 @@ 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 =
@ -223,11 +273,13 @@ static int nft_target_validate(const struct nft_ctx *ctx,
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;
}
@ -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;
}
@ -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]) {
@ -379,6 +437,7 @@ 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 =
@ -386,11 +445,13 @@ static int nft_match_validate(const struct nft_ctx *ctx,
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;
}

View File

@ -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;
@ -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>");

View File

@ -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]))) {
@ -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]));
@ -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;
}
@ -205,6 +219,13 @@ 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,
@ -212,6 +233,7 @@ static const struct nft_expr_ops nft_nat_ops = {
.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 = {