netfilter: nf_tables: validate catch-all set elements

catch-all set element might jump/goto to chain that uses expressions
that require validation.

Fixes: aaa31047a6 ("netfilter: nftables: add catch-all set element support")
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
This commit is contained in:
Pablo Neira Ayuso 2023-04-17 12:14:29 +02:00
parent c55c0e91c8
commit d46fc89414
3 changed files with 66 additions and 38 deletions

View File

@ -1085,6 +1085,10 @@ struct nft_chain {
}; };
int nft_chain_validate(const struct nft_ctx *ctx, const struct nft_chain *chain); int nft_chain_validate(const struct nft_ctx *ctx, const struct nft_chain *chain);
int nft_setelem_validate(const struct nft_ctx *ctx, struct nft_set *set,
const struct nft_set_iter *iter,
struct nft_set_elem *elem);
int nft_set_catchall_validate(const struct nft_ctx *ctx, struct nft_set *set);
enum nft_chain_types { enum nft_chain_types {
NFT_CHAIN_T_DEFAULT = 0, NFT_CHAIN_T_DEFAULT = 0,

View File

@ -3447,6 +3447,64 @@ static int nft_table_validate(struct net *net, const struct nft_table *table)
return 0; return 0;
} }
int nft_setelem_validate(const struct nft_ctx *ctx, struct nft_set *set,
const struct nft_set_iter *iter,
struct nft_set_elem *elem)
{
const struct nft_set_ext *ext = nft_set_elem_ext(set, elem->priv);
struct nft_ctx *pctx = (struct nft_ctx *)ctx;
const struct nft_data *data;
int err;
if (nft_set_ext_exists(ext, NFT_SET_EXT_FLAGS) &&
*nft_set_ext_flags(ext) & NFT_SET_ELEM_INTERVAL_END)
return 0;
data = nft_set_ext_data(ext);
switch (data->verdict.code) {
case NFT_JUMP:
case NFT_GOTO:
pctx->level++;
err = nft_chain_validate(ctx, data->verdict.chain);
if (err < 0)
return err;
pctx->level--;
break;
default:
break;
}
return 0;
}
struct nft_set_elem_catchall {
struct list_head list;
struct rcu_head rcu;
void *elem;
};
int nft_set_catchall_validate(const struct nft_ctx *ctx, struct nft_set *set)
{
u8 genmask = nft_genmask_next(ctx->net);
struct nft_set_elem_catchall *catchall;
struct nft_set_elem elem;
struct nft_set_ext *ext;
int ret = 0;
list_for_each_entry_rcu(catchall, &set->catchall_list, list) {
ext = nft_set_elem_ext(set, catchall->elem);
if (!nft_set_elem_active(ext, genmask))
continue;
elem.priv = catchall->elem;
ret = nft_setelem_validate(ctx, set, NULL, &elem);
if (ret < 0)
return ret;
}
return ret;
}
static struct nft_rule *nft_rule_lookup_byid(const struct net *net, static struct nft_rule *nft_rule_lookup_byid(const struct net *net,
const struct nft_chain *chain, const struct nft_chain *chain,
const struct nlattr *nla); const struct nlattr *nla);
@ -4759,12 +4817,6 @@ err_set_name:
return err; return err;
} }
struct nft_set_elem_catchall {
struct list_head list;
struct rcu_head rcu;
void *elem;
};
static void nft_set_catchall_destroy(const struct nft_ctx *ctx, static void nft_set_catchall_destroy(const struct nft_ctx *ctx,
struct nft_set *set) struct nft_set *set)
{ {

View File

@ -199,37 +199,6 @@ nla_put_failure:
return -1; return -1;
} }
static int nft_lookup_validate_setelem(const struct nft_ctx *ctx,
struct nft_set *set,
const struct nft_set_iter *iter,
struct nft_set_elem *elem)
{
const struct nft_set_ext *ext = nft_set_elem_ext(set, elem->priv);
struct nft_ctx *pctx = (struct nft_ctx *)ctx;
const struct nft_data *data;
int err;
if (nft_set_ext_exists(ext, NFT_SET_EXT_FLAGS) &&
*nft_set_ext_flags(ext) & NFT_SET_ELEM_INTERVAL_END)
return 0;
data = nft_set_ext_data(ext);
switch (data->verdict.code) {
case NFT_JUMP:
case NFT_GOTO:
pctx->level++;
err = nft_chain_validate(ctx, data->verdict.chain);
if (err < 0)
return err;
pctx->level--;
break;
default:
break;
}
return 0;
}
static int nft_lookup_validate(const struct nft_ctx *ctx, static int nft_lookup_validate(const struct nft_ctx *ctx,
const struct nft_expr *expr, const struct nft_expr *expr,
const struct nft_data **d) const struct nft_data **d)
@ -245,9 +214,12 @@ static int nft_lookup_validate(const struct nft_ctx *ctx,
iter.skip = 0; iter.skip = 0;
iter.count = 0; iter.count = 0;
iter.err = 0; iter.err = 0;
iter.fn = nft_lookup_validate_setelem; iter.fn = nft_setelem_validate;
priv->set->ops->walk(ctx, priv->set, &iter); priv->set->ops->walk(ctx, priv->set, &iter);
if (!iter.err)
iter.err = nft_set_catchall_validate(ctx, priv->set);
if (iter.err < 0) if (iter.err < 0)
return iter.err; return iter.err;