drbd: don't leak the shared secret to unprivileged netlink dumps

The conversion to explicit netlink serialization dropped the
exclude_sensitive parameter from net_conf_to_skb(), so each caller has
to sanitize by hand. Two dump paths were missed:
drbd_nl_get_connections_dumpit() and the volume-less connection branch
of get_one_status(). Neither op carries GENL_ADMIN_PERM, so any
unprivileged local user could read the CRAM-HMAC secret.

Add a net_conf_to_skb_sanitized() wrapper and route all three callers
through it.

Fixes: 8098eeb693 ("drbd: replace genl_magic with explicit netlink serialization")
Reported-by: Vivek Parikh <vivek.parikh@breachx.ai>
Signed-off-by: Christoph Böhmwalder <christoph.boehmwalder@linbit.com>
Link: https://patch.msgid.link/20260814151617.73752-1-christoph.boehmwalder@linbit.com
Signed-off-by: Jens Axboe <axboe@kernel.dk>
This commit is contained in:
Christoph Böhmwalder
2026-08-14 17:16:17 +02:00
committed by Jens Axboe
parent 7f40b34646
commit c71bf113df

View File

@@ -3306,6 +3306,26 @@ static int nla_put_drbd_cfg_context(struct sk_buff *skb,
return -EMSGSIZE;
}
/*
* net_conf_to_skb() serializes the shared secret verbatim. Any path that can
* answer a request from an unprivileged process must pass exclude_sensitive,
* so the secret is blanked in a private copy before it reaches the skb.
*/
static int net_conf_to_skb_sanitized(struct sk_buff *skb, struct net_conf *nc,
bool exclude_sensitive)
{
struct net_conf nc_clean;
if (!exclude_sensitive)
return net_conf_to_skb(skb, nc);
nc_clean = *nc;
memset(nc_clean.shared_secret, 0, sizeof(nc_clean.shared_secret));
nc_clean.shared_secret_len = 0;
return net_conf_to_skb(skb, &nc_clean);
}
/*
* The generic netlink dump callbacks are called outside the genl_lock(), so
* they cannot use the simple attribute parsing code which uses global
@@ -3621,7 +3641,8 @@ int drbd_nl_get_connections_dumpit(struct sk_buff *skb, struct netlink_callback
goto out;
net_conf = rcu_dereference(connection->net_conf);
if (net_conf) {
err = net_conf_to_skb(skb, net_conf);
err = net_conf_to_skb_sanitized(skb, net_conf,
!capable(CAP_SYS_ADMIN));
if (err)
goto out;
}
@@ -3842,18 +3863,8 @@ static int nla_put_status_info(struct sk_buff *skb, struct drbd_device *device,
struct net_conf *nc;
nc = rcu_dereference(first_peer_device(device)->connection->net_conf);
if (nc) {
if (exclude_sensitive) {
struct net_conf nc_clean = *nc;
memset(nc_clean.shared_secret, 0,
sizeof(nc_clean.shared_secret));
nc_clean.shared_secret_len = 0;
err = net_conf_to_skb(skb, &nc_clean);
} else {
err = net_conf_to_skb(skb, nc);
}
}
if (nc)
err = net_conf_to_skb_sanitized(skb, nc, exclude_sensitive);
}
rcu_read_unlock();
if (err)
@@ -4058,7 +4069,7 @@ static int get_one_status(struct sk_buff *skb, struct netlink_callback *cb)
struct net_conf *nc;
nc = rcu_dereference(connection->net_conf);
if (nc && net_conf_to_skb(skb, nc) != 0)
if (nc && net_conf_to_skb_sanitized(skb, nc, true) != 0)
goto cancel;
}
goto done;