netlink_skb_parms has to fit into skb->cb, and netlink skbs are cloned
together with it for broadcasts, so there is neither room for kuid_t
and kgid_t holding UUIDs nor a single owner for an allocation.

Store the sender ids as classic kernel ids: a UUID or GUID becomes -1,
which reaches the receiver of SCM_CREDENTIALS as the overflow id. The
kernel's own permission checks on netlink messages use the socket's
file credentials, not these.

Assisted-by: Claude:claude-opus-5-5

Signed-off-by: Jan Koester <[email protected]>
---
 include/linux/netlink.h  |  2 +-
 include/net/scm.h        | 28 ++++++++++++++++++++++++++++
 lib/kobject_uevent.c     |  8 ++++----
 net/netlink/af_netlink.c |  4 ++--
 4 files changed, 35 insertions(+), 7 deletions(-)

diff --git a/include/linux/netlink.h b/include/linux/netlink.h
index 882e9c1b6c..999c7a352e 100644
--- a/include/linux/netlink.h
+++ b/include/linux/netlink.h
@@ -23,7 +23,7 @@ enum netlink_skb_flags {
 };
 
 struct netlink_skb_parms {
-       struct scm_creds        creds;          /* Skb credentials      */
+       struct scm_creds_classic creds;         /* Skb credentials      */
        __u32                   portid;
        __u32                   dst_group;
        __u32                   flags;
diff --git a/include/net/scm.h b/include/net/scm.h
index 86ae6bc109..daa52e3d44 100644
--- a/include/net/scm.h
+++ b/include/net/scm.h
@@ -23,6 +23,34 @@ struct scm_creds {
        kgid_t  gid;
 };
 
+/*
+ * Sender credentials for skb->cb of skbs that get cloned along with it
+ * (netlink, bluetooth): no room for kuid_t and kgid_t, and no single owner
+ * for an allocation. The ids are classic kernel ids; a UUID or GUID becomes
+ * -1, which the receiver of SCM_CREDENTIALS sees as the overflow id.
+ */
+struct scm_creds_classic {
+       u32     pid;
+       uid_t   uid;
+       gid_t   gid;
+};
+
+static inline void scm_creds_to_classic(struct scm_creds_classic *classic,
+                                       const struct scm_creds *creds)
+{
+       classic->pid = creds->pid;
+       classic->uid = __kuid_val(creds->uid);
+       classic->gid = __kgid_val(creds->gid);
+}
+
+static inline void scm_creds_from_classic(struct scm_creds *creds,
+                                         const struct scm_creds_classic 
*classic)
+{
+       creds->pid = classic->pid;
+       creds->uid = KUIDT_INIT(classic->uid);
+       creds->gid = KGIDT_INIT(classic->gid);
+}
+
 #ifdef CONFIG_UNIX
 struct unix_edge;
 #endif
diff --git a/lib/kobject_uevent.c b/lib/kobject_uevent.c
index ddbc4d7482..8b63044881 100644
--- a/lib/kobject_uevent.c
+++ b/lib/kobject_uevent.c
@@ -297,8 +297,8 @@ static struct sk_buff *alloc_uevent_skb(struct 
kobj_uevent_env *env,
        skb_put_data(skb, env->buf, env->buflen);
 
        parms = &NETLINK_CB(skb);
-       parms->creds.uid = GLOBAL_ROOT_UID;
-       parms->creds.gid = GLOBAL_ROOT_GID;
+       parms->creds.uid = __kuid_val(GLOBAL_ROOT_UID);
+       parms->creds.gid = __kgid_val(GLOBAL_ROOT_GID);
        parms->dst_group = 1;
        parms->portid = 0;
 
@@ -362,12 +362,12 @@ static int uevent_net_broadcast_tagged(struct sock *usk,
                /* fix uid */
                root_uid = make_kuid(owning_user_ns, 0);
                if (uid_valid(root_uid))
-                       parms->creds.uid = root_uid;
+                       parms->creds.uid = __kuid_val(root_uid);
 
                /* fix gid */
                root_gid = make_kgid(owning_user_ns, 0);
                if (gid_valid(root_gid))
-                       parms->creds.gid = root_gid;
+                       parms->creds.gid = __kgid_val(root_gid);
        }
 
        ret = netlink_broadcast(usk, skb, 0, 1, GFP_KERNEL);
diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c
index 3029894eda..282a5d0da4 100644
--- a/net/netlink/af_netlink.c
+++ b/net/netlink/af_netlink.c
@@ -1901,7 +1901,7 @@ static int netlink_sendmsg(struct socket *sock, struct 
msghdr *msg, size_t len)
 
        NETLINK_CB(skb).portid  = nlk->portid;
        NETLINK_CB(skb).dst_group = dst_group;
-       NETLINK_CB(skb).creds   = scm.creds;
+       scm_creds_to_classic(&NETLINK_CB(skb).creds, &scm.creds);
        NETLINK_CB(skb).flags   = netlink_skb_flags;
 
        err = -EFAULT;
@@ -1994,7 +1994,7 @@ static int netlink_recvmsg(struct socket *sock, struct 
msghdr *msg, size_t len,
                netlink_cmsg_listen_all_nsid(sk, msg, skb);
 
        memset(&scm, 0, sizeof(scm));
-       scm.creds = *NETLINK_CREDS(skb);
+       scm_creds_from_classic(&scm.creds, NETLINK_CREDS(skb));
        if (flags & MSG_TRUNC)
                copied = data_skb->len;
 
-- 
2.56.0


Reply via email to