netfilter: nf_tables: validate catch-all set elements
authorPablo Neira Ayuso <pablo@netfilter.org>
Mon, 17 Apr 2023 10:14:29 +0000 (12:14 +0200)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Wed, 26 Apr 2023 12:28:33 +0000 (14:28 +0200)
[ Upstream commit d46fc894147cf98dd6e8210aa99ed46854191840 ]

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

Fixes: aaa31047a6d2 ("netfilter: nftables: add catch-all set element support")
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
Signed-off-by: Sasha Levin <sashal@kernel.org>
include/net/netfilter/nf_tables.h
net/netfilter/nf_tables_api.c
net/netfilter/nft_lookup.c

index 1daeded..6bacbf5 100644 (file)
@@ -1078,6 +1078,10 @@ struct nft_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 {
        NFT_CHAIN_T_DEFAULT = 0,
index ee052a5..251f4a9 100644 (file)
@@ -3391,6 +3391,64 @@ static int nft_table_validate(struct net *net, const struct nft_table *table)
        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,
                                             const struct nft_chain *chain,
                                             const struct nlattr *nla);
@@ -4695,12 +4753,6 @@ err_set_name:
        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,
                                     struct nft_set *set)
 {
index dfae127..d9ad1aa 100644 (file)
@@ -198,37 +198,6 @@ nla_put_failure:
        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,
                               const struct nft_expr *expr,
                               const struct nft_data **d)
@@ -244,9 +213,12 @@ static int nft_lookup_validate(const struct nft_ctx *ctx,
        iter.skip       = 0;
        iter.count      = 0;
        iter.err        = 0;
-       iter.fn         = nft_lookup_validate_setelem;
+       iter.fn         = nft_setelem_validate;
 
        priv->set->ops->walk(ctx, priv->set, &iter);
+       if (!iter.err)
+               iter.err = nft_set_catchall_validate(ctx, priv->set);
+
        if (iter.err < 0)
                return iter.err;