diff mbox series

[net,2/2] drop_monitor: Require 'CAP_SYS_ADMIN' when joining "events" group

Message ID 20231206213102.1824398-3-idosch@nvidia.com (mailing list archive)
State Accepted
Commit e03781879a0d524ce3126678d50a80484a513c4b
Delegated to: Netdev Maintainers
Headers show
Series Generic netlink multicast fixes | expand

Checks

Context Check Description
netdev/series_format success Posting correctly formatted
netdev/tree_selection success Clearly marked for net
netdev/ynl success SINGLE THREAD; Generated files up to date; no warnings/errors; no diff in generated;
netdev/fixes_present success Fixes tag present in non-next series
netdev/header_inline success No static functions without inline keyword in header files
netdev/build_32bit success Errors and warnings before: 1167 this patch: 1167
netdev/cc_maintainers success CCed 6 of 6 maintainers
netdev/build_clang success Errors and warnings before: 1143 this patch: 1143
netdev/verify_signedoff success Signed-off-by tag matches author and committer
netdev/deprecated_api success None detected
netdev/check_selftest success No net selftest shell script
netdev/verify_fixes success Fixes tag looks correct
netdev/build_allmodconfig_warn success Errors and warnings before: 1194 this patch: 1194
netdev/checkpatch success total: 0 errors, 0 warnings, 0 checks, 42 lines checked
netdev/build_clang_rust success No Rust files in patch. Skipping build
netdev/kdoc success Errors and warnings before: 0 this patch: 0
netdev/source_inline success Was 0 now: 0

Commit Message

Ido Schimmel Dec. 6, 2023, 9:31 p.m. UTC
The "NET_DM" generic netlink family notifies drop locations over the
"events" multicast group. This is problematic since by default generic
netlink allows non-root users to listen to these notifications.

Fix by adding a new field to the generic netlink multicast group
structure that when set prevents non-root users or root without the
'CAP_SYS_ADMIN' capability (in the user namespace owning the network
namespace) from joining the group. Set this field for the "events"
group. Use 'CAP_SYS_ADMIN' rather than 'CAP_NET_ADMIN' because of the
nature of the information that is shared over this group.

Note that the capability check in this case will always be performed
against the initial user namespace since the family is not netns aware
and only operates in the initial network namespace.

A new field is added to the structure rather than using the "flags"
field because the existing field uses uAPI flags and it is inappropriate
to add a new uAPI flag for an internal kernel check. In net-next we can
rework the "flags" field to use internal flags and fold the new field
into it. But for now, in order to reduce the amount of changes, add a
new field.

Since the information can only be consumed by root, mark the control
plane operations that start and stop the tracing as root-only using the
'GENL_ADMIN_PERM' flag.

Tested using [1].

Before:

 # capsh -- -c ./dm_repo
 # capsh --drop=cap_sys_admin -- -c ./dm_repo

After:

 # capsh -- -c ./dm_repo
 # capsh --drop=cap_sys_admin -- -c ./dm_repo
 Failed to join "events" multicast group

[1]
 $ cat dm.c
 #include <stdio.h>
 #include <netlink/genl/ctrl.h>
 #include <netlink/genl/genl.h>
 #include <netlink/socket.h>

 int main(int argc, char **argv)
 {
 	struct nl_sock *sk;
 	int grp, err;

 	sk = nl_socket_alloc();
 	if (!sk) {
 		fprintf(stderr, "Failed to allocate socket\n");
 		return -1;
 	}

 	err = genl_connect(sk);
 	if (err) {
 		fprintf(stderr, "Failed to connect socket\n");
 		return err;
 	}

 	grp = genl_ctrl_resolve_grp(sk, "NET_DM", "events");
 	if (grp < 0) {
 		fprintf(stderr,
 			"Failed to resolve \"events\" multicast group\n");
 		return grp;
 	}

 	err = nl_socket_add_memberships(sk, grp, NFNLGRP_NONE);
 	if (err) {
 		fprintf(stderr, "Failed to join \"events\" multicast group\n");
 		return err;
 	}

 	return 0;
 }
 $ gcc -I/usr/include/libnl3 -lnl-3 -lnl-genl-3 -o dm_repo dm.c

Fixes: 9a8afc8d3962 ("Network Drop Monitor: Adding drop monitor implementation & Netlink protocol")
Reported-by: "The UK's National Cyber Security Centre (NCSC)" <security@ncsc.gov.uk>
Signed-off-by: Ido Schimmel <idosch@nvidia.com>
---
 include/net/genetlink.h | 2 ++
 net/core/drop_monitor.c | 4 +++-
 net/netlink/genetlink.c | 3 +++
 3 files changed, 8 insertions(+), 1 deletion(-)

Comments

Jacob Keller Dec. 6, 2023, 11:19 p.m. UTC | #1
On 12/6/2023 1:31 PM, Ido Schimmel wrote:
> The "NET_DM" generic netlink family notifies drop locations over the
> "events" multicast group. This is problematic since by default generic
> netlink allows non-root users to listen to these notifications.
> 
> Fix by adding a new field to the generic netlink multicast group
> structure that when set prevents non-root users or root without the
> 'CAP_SYS_ADMIN' capability (in the user namespace owning the network
> namespace) from joining the group. Set this field for the "events"
> group. Use 'CAP_SYS_ADMIN' rather than 'CAP_NET_ADMIN' because of the
> nature of the information that is shared over this group.
> 
> Note that the capability check in this case will always be performed
> against the initial user namespace since the family is not netns aware
> and only operates in the initial network namespace.
> 
> A new field is added to the structure rather than using the "flags"
> field because the existing field uses uAPI flags and it is inappropriate
> to add a new uAPI flag for an internal kernel check. In net-next we can
> rework the "flags" field to use internal flags and fold the new field
> into it. But for now, in order to reduce the amount of changes, add a
> new field.
> 
> Since the information can only be consumed by root, mark the control
> plane operations that start and stop the tracing as root-only using the
> 'GENL_ADMIN_PERM' flag.
> 

Makes sense.

Reviewed-by: Jacob Keller <jacob.e.keller@intel.com>
Jiri Pirko Dec. 7, 2023, 10:17 a.m. UTC | #2
Wed, Dec 06, 2023 at 10:31:02PM CET, idosch@nvidia.com wrote:
>The "NET_DM" generic netlink family notifies drop locations over the
>"events" multicast group. This is problematic since by default generic
>netlink allows non-root users to listen to these notifications.
>
>Fix by adding a new field to the generic netlink multicast group
>structure that when set prevents non-root users or root without the
>'CAP_SYS_ADMIN' capability (in the user namespace owning the network
>namespace) from joining the group. Set this field for the "events"
>group. Use 'CAP_SYS_ADMIN' rather than 'CAP_NET_ADMIN' because of the
>nature of the information that is shared over this group.
>
>Note that the capability check in this case will always be performed
>against the initial user namespace since the family is not netns aware
>and only operates in the initial network namespace.
>
>A new field is added to the structure rather than using the "flags"
>field because the existing field uses uAPI flags and it is inappropriate
>to add a new uAPI flag for an internal kernel check. In net-next we can
>rework the "flags" field to use internal flags and fold the new field
>into it. But for now, in order to reduce the amount of changes, add a
>new field.
>
>Since the information can only be consumed by root, mark the control
>plane operations that start and stop the tracing as root-only using the
>'GENL_ADMIN_PERM' flag.
>
>Tested using [1].
>
>Before:
>
> # capsh -- -c ./dm_repo
> # capsh --drop=cap_sys_admin -- -c ./dm_repo
>
>After:
>
> # capsh -- -c ./dm_repo
> # capsh --drop=cap_sys_admin -- -c ./dm_repo
> Failed to join "events" multicast group
>
>[1]
> $ cat dm.c
> #include <stdio.h>
> #include <netlink/genl/ctrl.h>
> #include <netlink/genl/genl.h>
> #include <netlink/socket.h>
>
> int main(int argc, char **argv)
> {
> 	struct nl_sock *sk;
> 	int grp, err;
>
> 	sk = nl_socket_alloc();
> 	if (!sk) {
> 		fprintf(stderr, "Failed to allocate socket\n");
> 		return -1;
> 	}
>
> 	err = genl_connect(sk);
> 	if (err) {
> 		fprintf(stderr, "Failed to connect socket\n");
> 		return err;
> 	}
>
> 	grp = genl_ctrl_resolve_grp(sk, "NET_DM", "events");
> 	if (grp < 0) {
> 		fprintf(stderr,
> 			"Failed to resolve \"events\" multicast group\n");
> 		return grp;
> 	}
>
> 	err = nl_socket_add_memberships(sk, grp, NFNLGRP_NONE);
> 	if (err) {
> 		fprintf(stderr, "Failed to join \"events\" multicast group\n");
> 		return err;
> 	}
>
> 	return 0;
> }
> $ gcc -I/usr/include/libnl3 -lnl-3 -lnl-genl-3 -o dm_repo dm.c
>
>Fixes: 9a8afc8d3962 ("Network Drop Monitor: Adding drop monitor implementation & Netlink protocol")
>Reported-by: "The UK's National Cyber Security Centre (NCSC)" <security@ncsc.gov.uk>
>Signed-off-by: Ido Schimmel <idosch@nvidia.com>

Reviewed-by: Jiri Pirko <jiri@nvidia.com>
diff mbox series

Patch

diff --git a/include/net/genetlink.h b/include/net/genetlink.h
index e18a4c0d69ee..c53244f20437 100644
--- a/include/net/genetlink.h
+++ b/include/net/genetlink.h
@@ -12,10 +12,12 @@ 
  * struct genl_multicast_group - generic netlink multicast group
  * @name: name of the multicast group, names are per-family
  * @flags: GENL_* flags (%GENL_ADMIN_PERM or %GENL_UNS_ADMIN_PERM)
+ * @cap_sys_admin: whether %CAP_SYS_ADMIN is required for binding
  */
 struct genl_multicast_group {
 	char			name[GENL_NAMSIZ];
 	u8			flags;
+	u8			cap_sys_admin:1;
 };
 
 struct genl_split_ops;
diff --git a/net/core/drop_monitor.c b/net/core/drop_monitor.c
index aff31cd944c2..b240d9aae4a6 100644
--- a/net/core/drop_monitor.c
+++ b/net/core/drop_monitor.c
@@ -183,7 +183,7 @@  static struct sk_buff *reset_per_cpu_data(struct per_cpu_dm_data *data)
 }
 
 static const struct genl_multicast_group dropmon_mcgrps[] = {
-	{ .name = "events", },
+	{ .name = "events", .cap_sys_admin = 1 },
 };
 
 static void send_dm_alert(struct work_struct *work)
@@ -1619,11 +1619,13 @@  static const struct genl_small_ops dropmon_ops[] = {
 		.cmd = NET_DM_CMD_START,
 		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
 		.doit = net_dm_cmd_trace,
+		.flags = GENL_ADMIN_PERM,
 	},
 	{
 		.cmd = NET_DM_CMD_STOP,
 		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
 		.doit = net_dm_cmd_trace,
+		.flags = GENL_ADMIN_PERM,
 	},
 	{
 		.cmd = NET_DM_CMD_CONFIG_GET,
diff --git a/net/netlink/genetlink.c b/net/netlink/genetlink.c
index 92ef5ed2e7b0..9c7ffd10df2a 100644
--- a/net/netlink/genetlink.c
+++ b/net/netlink/genetlink.c
@@ -1691,6 +1691,9 @@  static int genl_bind(struct net *net, int group)
 		if ((grp->flags & GENL_UNS_ADMIN_PERM) &&
 		    !ns_capable(net->user_ns, CAP_NET_ADMIN))
 			ret = -EPERM;
+		if (grp->cap_sys_admin &&
+		    !ns_capable(net->user_ns, CAP_SYS_ADMIN))
+			ret = -EPERM;
 
 		break;
 	}