diff mbox series

[nf,2/3] netfilter: nf_tables: Deduplicate nft_register_obj audit logs

Message ID 20230923015351.15707-3-phil@nwl.cc (mailing list archive)
State Handled Elsewhere
Headers show
Series Review nf_tables audit logging | expand

Commit Message

Phil Sutter Sept. 23, 2023, 1:53 a.m. UTC
When adding/updating an object, the transaction handler emits suitable
audit log entries already, the one in nft_obj_notify() is redundant. To
fix that (and retain the audit logging from objects' 'update' callback),
Introduce an "audit log free" variant for internal use.

Fixes: c520292f29b80 ("audit: log nftables configuration change events once per table")
Signed-off-by: Phil Sutter <phil@nwl.cc>
---
 net/netfilter/nf_tables_api.c                 | 44 ++++++++++++-------
 .../testing/selftests/netfilter/nft_audit.sh  | 20 +++++++++
 2 files changed, 48 insertions(+), 16 deletions(-)

Comments

Pablo Neira Ayuso Sept. 27, 2023, 7:41 p.m. UTC | #1
Hi Phil,

On Sat, Sep 23, 2023 at 03:53:50AM +0200, Phil Sutter wrote:
> When adding/updating an object, the transaction handler emits suitable
> audit log entries already, the one in nft_obj_notify() is redundant. To
> fix that (and retain the audit logging from objects' 'update' callback),
> Introduce an "audit log free" variant for internal use.
> 
> Fixes: c520292f29b80 ("audit: log nftables configuration change events once per table")
> Signed-off-by: Phil Sutter <phil@nwl.cc>
> ---
>  net/netfilter/nf_tables_api.c                 | 44 ++++++++++++-------
>  .../testing/selftests/netfilter/nft_audit.sh  | 20 +++++++++
>  2 files changed, 48 insertions(+), 16 deletions(-)
> 
> diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c
> index 0e5d9bdba82b8..48d50df950a18 100644
> --- a/net/netfilter/nf_tables_api.c
> +++ b/net/netfilter/nf_tables_api.c
> @@ -8046,24 +8046,14 @@ static int nf_tables_delobj(struct sk_buff *skb, const struct nfnl_info *info,
>  	return nft_delobj(&ctx, obj);
>  }
>  
> -void nft_obj_notify(struct net *net, const struct nft_table *table,
> -		    struct nft_object *obj, u32 portid, u32 seq, int event,
> -		    u16 flags, int family, int report, gfp_t gfp)
> +static void
> +__nft_obj_notify(struct net *net, const struct nft_table *table,
> +		 struct nft_object *obj, u32 portid, u32 seq, int event,
> +		 u16 flags, int family, int report, gfp_t gfp)
>  {
>  	struct nftables_pernet *nft_net = nft_pernet(net);
>  	struct sk_buff *skb;
>  	int err;
> -	char *buf = kasprintf(gfp, "%s:%u",
> -			      table->name, nft_net->base_seq);
> -
> -	audit_log_nfcfg(buf,
> -			family,
> -			obj->handle,
> -			event == NFT_MSG_NEWOBJ ?
> -				 AUDIT_NFT_OP_OBJ_REGISTER :
> -				 AUDIT_NFT_OP_OBJ_UNREGISTER,
> -			gfp);
> -	kfree(buf);
>  
>  	if (!report &&
>  	    !nfnetlink_has_listeners(net, NFNLGRP_NFTABLES))
> @@ -8086,13 +8076,35 @@ void nft_obj_notify(struct net *net, const struct nft_table *table,
>  err:
>  	nfnetlink_set_err(net, portid, NFNLGRP_NFTABLES, -ENOBUFS);
>  }
> +
> +void nft_obj_notify(struct net *net, const struct nft_table *table,
> +		    struct nft_object *obj, u32 portid, u32 seq, int event,
> +		    u16 flags, int family, int report, gfp_t gfp)
> +{
> +	struct nftables_pernet *nft_net = nft_pernet(net);
> +	char *buf = kasprintf(gfp, "%s:%u",
> +			      table->name, nft_net->base_seq);
> +
> +	audit_log_nfcfg(buf,
> +			family,
> +			obj->handle,
> +			event == NFT_MSG_NEWOBJ ?
> +				 AUDIT_NFT_OP_OBJ_REGISTER :
> +				 AUDIT_NFT_OP_OBJ_UNREGISTER,
> +			gfp);
> +	kfree(buf);
> +
> +	__nft_obj_notify(net, table, obj, portid, seq, event,
> +			 flags, family, report, gfp);
> +}
>  EXPORT_SYMBOL_GPL(nft_obj_notify);

OK, so nft_obj_notify() is called from nft_quota to notify that the
quota is depleted and the audit log is still there in this case.

>  static void nf_tables_obj_notify(const struct nft_ctx *ctx,
>  				 struct nft_object *obj, int event)
>  {
> -	nft_obj_notify(ctx->net, ctx->table, obj, ctx->portid, ctx->seq, event,
> -		       ctx->flags, ctx->family, ctx->report, GFP_KERNEL);
> +	__nft_obj_notify(ctx->net, ctx->table, obj, ctx->portid,
> +			 ctx->seq, event, ctx->flags, ctx->family,
> +			 ctx->report, GFP_KERNEL);
>  }

This function is called from the commit path to send the event
notification, and it should send the audit log?

Is this nf_tables_commit_audit_log() that provides the redundant log,
right?

>  /*
> diff --git a/tools/testing/selftests/netfilter/nft_audit.sh b/tools/testing/selftests/netfilter/nft_audit.sh
> index 0b3255e7b3538..bb34329e02a7f 100755
> --- a/tools/testing/selftests/netfilter/nft_audit.sh
> +++ b/tools/testing/selftests/netfilter/nft_audit.sh
> @@ -85,6 +85,26 @@ do_test "nft add set t1 s2 $setblock; add set t1 s3 { $settype; }" \
>  do_test "nft add element t1 s3 $setelem" \
>  "table=t1 family=2 entries=3 op=nft_register_setelem"
>  
> +# adding counters
> +
> +do_test 'nft add counter t1 c1' \
> +'table=t1 family=2 entries=1 op=nft_register_obj'
> +
> +do_test 'nft add counter t2 c1; add counter t2 c2' \
> +'table=t2 family=2 entries=2 op=nft_register_obj'
> +
> +# adding/updating quotas
> +
> +do_test 'nft add quota t1 q1 { 10 bytes }' \
> +'table=t1 family=2 entries=1 op=nft_register_obj'
> +
> +do_test 'nft add quota t2 q1 { 10 bytes }; add quota t2 q2 { 10 bytes }' \
> +'table=t2 family=2 entries=2 op=nft_register_obj'
> +
> +# changing the quota value triggers obj update path
> +do_test 'nft add quota t1 q1 { 20 bytes }' \
> +'table=t1 family=2 entries=1 op=nft_register_obj'
> +
>  # resetting rules
>  
>  do_test 'nft reset rules t1 c2' \
> -- 
> 2.41.0
>
Richard Guy Briggs Sept. 28, 2023, 1:37 a.m. UTC | #2
On 2023-09-23 03:53, Phil Sutter wrote:
> When adding/updating an object, the transaction handler emits suitable
> audit log entries already, the one in nft_obj_notify() is redundant. To
> fix that (and retain the audit logging from objects' 'update' callback),
> Introduce an "audit log free" variant for internal use.
> 
> Fixes: c520292f29b80 ("audit: log nftables configuration change events once per table")
> Signed-off-by: Phil Sutter <phil@nwl.cc>

Reviewed-by: Richard Guy Briggs <rgb@redhat.com>

> ---
>  net/netfilter/nf_tables_api.c                 | 44 ++++++++++++-------
>  .../testing/selftests/netfilter/nft_audit.sh  | 20 +++++++++
>  2 files changed, 48 insertions(+), 16 deletions(-)
> 
> diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c
> index 0e5d9bdba82b8..48d50df950a18 100644
> --- a/net/netfilter/nf_tables_api.c
> +++ b/net/netfilter/nf_tables_api.c
> @@ -8046,24 +8046,14 @@ static int nf_tables_delobj(struct sk_buff *skb, const struct nfnl_info *info,
>  	return nft_delobj(&ctx, obj);
>  }
>  
> -void nft_obj_notify(struct net *net, const struct nft_table *table,
> -		    struct nft_object *obj, u32 portid, u32 seq, int event,
> -		    u16 flags, int family, int report, gfp_t gfp)
> +static void
> +__nft_obj_notify(struct net *net, const struct nft_table *table,
> +		 struct nft_object *obj, u32 portid, u32 seq, int event,
> +		 u16 flags, int family, int report, gfp_t gfp)
>  {
>  	struct nftables_pernet *nft_net = nft_pernet(net);
>  	struct sk_buff *skb;
>  	int err;
> -	char *buf = kasprintf(gfp, "%s:%u",
> -			      table->name, nft_net->base_seq);
> -
> -	audit_log_nfcfg(buf,
> -			family,
> -			obj->handle,
> -			event == NFT_MSG_NEWOBJ ?
> -				 AUDIT_NFT_OP_OBJ_REGISTER :
> -				 AUDIT_NFT_OP_OBJ_UNREGISTER,
> -			gfp);
> -	kfree(buf);
>  
>  	if (!report &&
>  	    !nfnetlink_has_listeners(net, NFNLGRP_NFTABLES))
> @@ -8086,13 +8076,35 @@ void nft_obj_notify(struct net *net, const struct nft_table *table,
>  err:
>  	nfnetlink_set_err(net, portid, NFNLGRP_NFTABLES, -ENOBUFS);
>  }
> +
> +void nft_obj_notify(struct net *net, const struct nft_table *table,
> +		    struct nft_object *obj, u32 portid, u32 seq, int event,
> +		    u16 flags, int family, int report, gfp_t gfp)
> +{
> +	struct nftables_pernet *nft_net = nft_pernet(net);
> +	char *buf = kasprintf(gfp, "%s:%u",
> +			      table->name, nft_net->base_seq);
> +
> +	audit_log_nfcfg(buf,
> +			family,
> +			obj->handle,
> +			event == NFT_MSG_NEWOBJ ?
> +				 AUDIT_NFT_OP_OBJ_REGISTER :
> +				 AUDIT_NFT_OP_OBJ_UNREGISTER,
> +			gfp);
> +	kfree(buf);
> +
> +	__nft_obj_notify(net, table, obj, portid, seq, event,
> +			 flags, family, report, gfp);
> +}
>  EXPORT_SYMBOL_GPL(nft_obj_notify);
>  
>  static void nf_tables_obj_notify(const struct nft_ctx *ctx,
>  				 struct nft_object *obj, int event)
>  {
> -	nft_obj_notify(ctx->net, ctx->table, obj, ctx->portid, ctx->seq, event,
> -		       ctx->flags, ctx->family, ctx->report, GFP_KERNEL);
> +	__nft_obj_notify(ctx->net, ctx->table, obj, ctx->portid,
> +			 ctx->seq, event, ctx->flags, ctx->family,
> +			 ctx->report, GFP_KERNEL);
>  }
>  
>  /*
> diff --git a/tools/testing/selftests/netfilter/nft_audit.sh b/tools/testing/selftests/netfilter/nft_audit.sh
> index 0b3255e7b3538..bb34329e02a7f 100755
> --- a/tools/testing/selftests/netfilter/nft_audit.sh
> +++ b/tools/testing/selftests/netfilter/nft_audit.sh
> @@ -85,6 +85,26 @@ do_test "nft add set t1 s2 $setblock; add set t1 s3 { $settype; }" \
>  do_test "nft add element t1 s3 $setelem" \
>  "table=t1 family=2 entries=3 op=nft_register_setelem"
>  
> +# adding counters
> +
> +do_test 'nft add counter t1 c1' \
> +'table=t1 family=2 entries=1 op=nft_register_obj'
> +
> +do_test 'nft add counter t2 c1; add counter t2 c2' \
> +'table=t2 family=2 entries=2 op=nft_register_obj'
> +
> +# adding/updating quotas
> +
> +do_test 'nft add quota t1 q1 { 10 bytes }' \
> +'table=t1 family=2 entries=1 op=nft_register_obj'
> +
> +do_test 'nft add quota t2 q1 { 10 bytes }; add quota t2 q2 { 10 bytes }' \
> +'table=t2 family=2 entries=2 op=nft_register_obj'
> +
> +# changing the quota value triggers obj update path
> +do_test 'nft add quota t1 q1 { 20 bytes }' \
> +'table=t1 family=2 entries=1 op=nft_register_obj'
> +
>  # resetting rules
>  
>  do_test 'nft reset rules t1 c2' \
> -- 
> 2.41.0
> 

- RGB

--
Richard Guy Briggs <rgb@redhat.com>
Sr. S/W Engineer, Kernel Security, Base Operating Systems
Remote, Ottawa, Red Hat Canada
Upstream IRC: SunRaycer
Voice: +1.613.860 2354 SMS: +1.613.518.6570
Phil Sutter Sept. 28, 2023, 2:48 p.m. UTC | #3
Hi Pablo,

On Wed, Sep 27, 2023 at 09:41:53PM +0200, Pablo Neira Ayuso wrote:
> On Sat, Sep 23, 2023 at 03:53:50AM +0200, Phil Sutter wrote:
> > When adding/updating an object, the transaction handler emits suitable
> > audit log entries already, the one in nft_obj_notify() is redundant. To
> > fix that (and retain the audit logging from objects' 'update' callback),
> > Introduce an "audit log free" variant for internal use.
> > 
> > Fixes: c520292f29b80 ("audit: log nftables configuration change events once per table")
> > Signed-off-by: Phil Sutter <phil@nwl.cc>
> > ---
> >  net/netfilter/nf_tables_api.c                 | 44 ++++++++++++-------
> >  .../testing/selftests/netfilter/nft_audit.sh  | 20 +++++++++
> >  2 files changed, 48 insertions(+), 16 deletions(-)
> > 
> > diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c
> > index 0e5d9bdba82b8..48d50df950a18 100644
> > --- a/net/netfilter/nf_tables_api.c
> > +++ b/net/netfilter/nf_tables_api.c
> > @@ -8046,24 +8046,14 @@ static int nf_tables_delobj(struct sk_buff *skb, const struct nfnl_info *info,
> >  	return nft_delobj(&ctx, obj);
> >  }
> >  
> > -void nft_obj_notify(struct net *net, const struct nft_table *table,
> > -		    struct nft_object *obj, u32 portid, u32 seq, int event,
> > -		    u16 flags, int family, int report, gfp_t gfp)
> > +static void
> > +__nft_obj_notify(struct net *net, const struct nft_table *table,
> > +		 struct nft_object *obj, u32 portid, u32 seq, int event,
> > +		 u16 flags, int family, int report, gfp_t gfp)
> >  {
> >  	struct nftables_pernet *nft_net = nft_pernet(net);
> >  	struct sk_buff *skb;
> >  	int err;
> > -	char *buf = kasprintf(gfp, "%s:%u",
> > -			      table->name, nft_net->base_seq);
> > -
> > -	audit_log_nfcfg(buf,
> > -			family,
> > -			obj->handle,
> > -			event == NFT_MSG_NEWOBJ ?
> > -				 AUDIT_NFT_OP_OBJ_REGISTER :
> > -				 AUDIT_NFT_OP_OBJ_UNREGISTER,
> > -			gfp);
> > -	kfree(buf);
> >  
> >  	if (!report &&
> >  	    !nfnetlink_has_listeners(net, NFNLGRP_NFTABLES))
> > @@ -8086,13 +8076,35 @@ void nft_obj_notify(struct net *net, const struct nft_table *table,
> >  err:
> >  	nfnetlink_set_err(net, portid, NFNLGRP_NFTABLES, -ENOBUFS);
> >  }
> > +
> > +void nft_obj_notify(struct net *net, const struct nft_table *table,
> > +		    struct nft_object *obj, u32 portid, u32 seq, int event,
> > +		    u16 flags, int family, int report, gfp_t gfp)
> > +{
> > +	struct nftables_pernet *nft_net = nft_pernet(net);
> > +	char *buf = kasprintf(gfp, "%s:%u",
> > +			      table->name, nft_net->base_seq);
> > +
> > +	audit_log_nfcfg(buf,
> > +			family,
> > +			obj->handle,
> > +			event == NFT_MSG_NEWOBJ ?
> > +				 AUDIT_NFT_OP_OBJ_REGISTER :
> > +				 AUDIT_NFT_OP_OBJ_UNREGISTER,
> > +			gfp);
> > +	kfree(buf);
> > +
> > +	__nft_obj_notify(net, table, obj, portid, seq, event,
> > +			 flags, family, report, gfp);
> > +}
> >  EXPORT_SYMBOL_GPL(nft_obj_notify);
> 
> OK, so nft_obj_notify() is called from nft_quota to notify that the
> quota is depleted and the audit log is still there in this case.

Exactly. I needed an internal __nft_obj_notify() which just serves
notify_list but not audit.

> >  static void nf_tables_obj_notify(const struct nft_ctx *ctx,
> >  				 struct nft_object *obj, int event)
> >  {
> > -	nft_obj_notify(ctx->net, ctx->table, obj, ctx->portid, ctx->seq, event,
> > -		       ctx->flags, ctx->family, ctx->report, GFP_KERNEL);
> > +	__nft_obj_notify(ctx->net, ctx->table, obj, ctx->portid,
> > +			 ctx->seq, event, ctx->flags, ctx->family,
> > +			 ctx->report, GFP_KERNEL);
> >  }
> 
> This function is called from the commit path to send the event
> notification, and it should send the audit log?
> 
> Is this nf_tables_commit_audit_log() that provides the redundant log,
> right?

It's easier to to make nf_tables_obj_notify() skip the audit log and
not introduce special casing in nf_tables_commit_audit_log() or call
nf_tables_commit_audit_collect() for all transaction entries but NEWOBJ
and DELOBJ.

Cheers, Phil
Pablo Neira Ayuso Sept. 28, 2023, 7:14 p.m. UTC | #4
On Thu, Sep 28, 2023 at 04:48:11PM +0200, Phil Sutter wrote:
> On Wed, Sep 27, 2023 at 09:41:53PM +0200, Pablo Neira Ayuso wrote:
> > OK, so nft_obj_notify() is called from nft_quota to notify that the
> > quota is depleted and the audit log is still there in this case.
> 
> Exactly. I needed an internal __nft_obj_notify() which just serves
> notify_list but not audit.

Patch LGTM, thanks for explaining!
Paul Moore Oct. 3, 2023, 5:50 p.m. UTC | #5
On Fri, Sep 22, 2023 at 9:53 PM Phil Sutter <phil@nwl.cc> wrote:
>
> When adding/updating an object, the transaction handler emits suitable
> audit log entries already, the one in nft_obj_notify() is redundant. To
> fix that (and retain the audit logging from objects' 'update' callback),
> Introduce an "audit log free" variant for internal use.
>
> Fixes: c520292f29b80 ("audit: log nftables configuration change events once per table")
> Signed-off-by: Phil Sutter <phil@nwl.cc>
> ---
>  net/netfilter/nf_tables_api.c                 | 44 ++++++++++++-------
>  .../testing/selftests/netfilter/nft_audit.sh  | 20 +++++++++
>  2 files changed, 48 insertions(+), 16 deletions(-)

Thanks for working on this Phil, it looks good to me from an audit perspective.

Acked-by: Paul Moore <paul@paul-moore.com> (Audit)
diff mbox series

Patch

diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c
index 0e5d9bdba82b8..48d50df950a18 100644
--- a/net/netfilter/nf_tables_api.c
+++ b/net/netfilter/nf_tables_api.c
@@ -8046,24 +8046,14 @@  static int nf_tables_delobj(struct sk_buff *skb, const struct nfnl_info *info,
 	return nft_delobj(&ctx, obj);
 }
 
-void nft_obj_notify(struct net *net, const struct nft_table *table,
-		    struct nft_object *obj, u32 portid, u32 seq, int event,
-		    u16 flags, int family, int report, gfp_t gfp)
+static void
+__nft_obj_notify(struct net *net, const struct nft_table *table,
+		 struct nft_object *obj, u32 portid, u32 seq, int event,
+		 u16 flags, int family, int report, gfp_t gfp)
 {
 	struct nftables_pernet *nft_net = nft_pernet(net);
 	struct sk_buff *skb;
 	int err;
-	char *buf = kasprintf(gfp, "%s:%u",
-			      table->name, nft_net->base_seq);
-
-	audit_log_nfcfg(buf,
-			family,
-			obj->handle,
-			event == NFT_MSG_NEWOBJ ?
-				 AUDIT_NFT_OP_OBJ_REGISTER :
-				 AUDIT_NFT_OP_OBJ_UNREGISTER,
-			gfp);
-	kfree(buf);
 
 	if (!report &&
 	    !nfnetlink_has_listeners(net, NFNLGRP_NFTABLES))
@@ -8086,13 +8076,35 @@  void nft_obj_notify(struct net *net, const struct nft_table *table,
 err:
 	nfnetlink_set_err(net, portid, NFNLGRP_NFTABLES, -ENOBUFS);
 }
+
+void nft_obj_notify(struct net *net, const struct nft_table *table,
+		    struct nft_object *obj, u32 portid, u32 seq, int event,
+		    u16 flags, int family, int report, gfp_t gfp)
+{
+	struct nftables_pernet *nft_net = nft_pernet(net);
+	char *buf = kasprintf(gfp, "%s:%u",
+			      table->name, nft_net->base_seq);
+
+	audit_log_nfcfg(buf,
+			family,
+			obj->handle,
+			event == NFT_MSG_NEWOBJ ?
+				 AUDIT_NFT_OP_OBJ_REGISTER :
+				 AUDIT_NFT_OP_OBJ_UNREGISTER,
+			gfp);
+	kfree(buf);
+
+	__nft_obj_notify(net, table, obj, portid, seq, event,
+			 flags, family, report, gfp);
+}
 EXPORT_SYMBOL_GPL(nft_obj_notify);
 
 static void nf_tables_obj_notify(const struct nft_ctx *ctx,
 				 struct nft_object *obj, int event)
 {
-	nft_obj_notify(ctx->net, ctx->table, obj, ctx->portid, ctx->seq, event,
-		       ctx->flags, ctx->family, ctx->report, GFP_KERNEL);
+	__nft_obj_notify(ctx->net, ctx->table, obj, ctx->portid,
+			 ctx->seq, event, ctx->flags, ctx->family,
+			 ctx->report, GFP_KERNEL);
 }
 
 /*
diff --git a/tools/testing/selftests/netfilter/nft_audit.sh b/tools/testing/selftests/netfilter/nft_audit.sh
index 0b3255e7b3538..bb34329e02a7f 100755
--- a/tools/testing/selftests/netfilter/nft_audit.sh
+++ b/tools/testing/selftests/netfilter/nft_audit.sh
@@ -85,6 +85,26 @@  do_test "nft add set t1 s2 $setblock; add set t1 s3 { $settype; }" \
 do_test "nft add element t1 s3 $setelem" \
 "table=t1 family=2 entries=3 op=nft_register_setelem"
 
+# adding counters
+
+do_test 'nft add counter t1 c1' \
+'table=t1 family=2 entries=1 op=nft_register_obj'
+
+do_test 'nft add counter t2 c1; add counter t2 c2' \
+'table=t2 family=2 entries=2 op=nft_register_obj'
+
+# adding/updating quotas
+
+do_test 'nft add quota t1 q1 { 10 bytes }' \
+'table=t1 family=2 entries=1 op=nft_register_obj'
+
+do_test 'nft add quota t2 q1 { 10 bytes }; add quota t2 q2 { 10 bytes }' \
+'table=t2 family=2 entries=2 op=nft_register_obj'
+
+# changing the quota value triggers obj update path
+do_test 'nft add quota t1 q1 { 20 bytes }' \
+'table=t1 family=2 entries=1 op=nft_register_obj'
+
 # resetting rules
 
 do_test 'nft reset rules t1 c2' \