[RFC PATCH v2 26/41] proc: show UUID and GUID ids in status, accept them for loginuid

Jan Koester jan.koester at tuxist.de
Wed Oct 21 03:15:46 UTC 2026


/proc/<pid>/status shows ids without a 32 bit form as the overflow id.
Add Uid_r:, Gid_r: and Groups_r: lines with the same ids as text
(kuid_str()): a number, a UUID, or a GUID in braces, munged as usual
for ids the reader's user namespace can't see.

/proc/<pid>/loginuid accepts the text form of a UUID or GUID, so login
services can record such users, and shows such a loginuid as text.
Classic loginuids read and write as before, including 4294967295 for
"unset".

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

Signed-off-by: Jan Koester <jan.koester at tuxist.de>
---
 fs/proc/array.c | 17 ++++++++++++++++-
 fs/proc/base.c  | 38 ++++++++++++++++++++++++--------------
 2 files changed, 40 insertions(+), 15 deletions(-)

diff --git a/fs/proc/array.c b/fs/proc/array.c
index f6f75d2067..5b66ff32f7 100644
--- a/fs/proc/array.c
+++ b/fs/proc/array.c
@@ -156,6 +156,7 @@ static inline void task_state(struct seq_file *m, struct pid_namespace *ns,
 	const struct cred *cred;
 	pid_t ppid, tpid = 0, tgid, ngid;
 	unsigned int max_fds = 0;
+	char idbuf[KID_STR_LEN];
 
 	rcu_read_lock();
 	tracer = ptrace_parent(p);
@@ -200,10 +201,24 @@ static inline void task_state(struct seq_file *m, struct pid_namespace *ns,
 	for (g = 0; g < group_info->ngroups; g++)
 		seq_put_decimal_ull(m, g ? " " : "",
 				from_kgid_munged(user_ns, group_info->gid[g]));
-	put_cred(cred);
 	/* Trailing space shouldn't have been added in the first place. */
 	seq_putc(m, ' ');
 
+	/* The same ids including UUIDs and GUIDs, as kuid_str() writes them */
+	seq_printf(m, "\nUid_r:\t%s", kuid_str_munged(user_ns, cred->uid, idbuf));
+	seq_printf(m, "\t%s", kuid_str_munged(user_ns, cred->euid, idbuf));
+	seq_printf(m, "\t%s", kuid_str_munged(user_ns, cred->suid, idbuf));
+	seq_printf(m, "\t%s", kuid_str_munged(user_ns, cred->fsuid, idbuf));
+	seq_printf(m, "\nGid_r:\t%s", kgid_str_munged(user_ns, cred->gid, idbuf));
+	seq_printf(m, "\t%s", kgid_str_munged(user_ns, cred->egid, idbuf));
+	seq_printf(m, "\t%s", kgid_str_munged(user_ns, cred->sgid, idbuf));
+	seq_printf(m, "\t%s", kgid_str_munged(user_ns, cred->fsgid, idbuf));
+	seq_puts(m, "\nGroups_r:\t");
+	for (g = 0; g < group_info->ngroups; g++)
+		seq_printf(m, "%s%s", g ? " " : "",
+			   kgid_str_munged(user_ns, group_info->gid[g], idbuf));
+	put_cred(cred);
+
 #ifdef CONFIG_PID_NS
 	seq_puts(m, "\nNStgid:");
 	for (g = ns->level; g <= pid->level; g++)
diff --git a/fs/proc/base.c b/fs/proc/base.c
index 6a39de424f..ec3147e7ab 100644
--- a/fs/proc/base.c
+++ b/fs/proc/base.c
@@ -1290,20 +1290,26 @@ static const struct file_operations proc_oom_score_adj_operations = {
 };
 
 #ifdef CONFIG_AUDIT
-#define TMPBUFLEN 11
+#define TMPBUFLEN KID_STR_LEN
 static ssize_t proc_loginuid_read(struct file * file, char __user * buf,
 				  size_t count, loff_t *ppos)
 {
 	struct inode * inode = file_inode(file);
 	struct task_struct *task = get_proc_task(inode);
+	struct user_namespace *ns = file->f_cred->user_ns;
 	ssize_t length;
 	char tmpbuf[TMPBUFLEN];
+	kuid_t loginuid;
 
 	if (!task)
 		return -ESRCH;
-	length = scnprintf(tmpbuf, TMPBUFLEN, "%u",
-			   from_kuid(file->f_cred->user_ns,
-				     audit_get_loginuid(task)));
+	loginuid = audit_get_loginuid(task);
+	/* a UUID or GUID loginuid as text, if @ns can see it */
+	if (!uid_is_legacy(loginuid) && kuid_has_mapping(ns, loginuid))
+		length = strlen(kuid_str(loginuid, tmpbuf));
+	else
+		length = scnprintf(tmpbuf, TMPBUFLEN, "%u",
+				   from_kuid(ns, loginuid));
 	put_task_struct(task);
 	return simple_read_from_buffer(buf, count, ppos, tmpbuf, length);
 }
@@ -1312,7 +1318,7 @@ static ssize_t proc_loginuid_write(struct file * file, const char __user * buf,
 				   size_t count, loff_t *ppos)
 {
 	struct inode * inode = file_inode(file);
-	uid_t loginuid;
+	char tmpbuf[TMPBUFLEN];
 	kuid_t kloginuid;
 	int rv;
 
@@ -1332,16 +1338,20 @@ static ssize_t proc_loginuid_write(struct file * file, const char __user * buf,
 		return -EINVAL;
 	}
 
-	rv = kstrtou32_from_user(buf, count, 10, &loginuid);
-	if (rv < 0)
-		return rv;
+	/* a decimal uid, a UUID or a GUID in braces */
+	if (count >= sizeof(tmpbuf))
+		return -EINVAL;
+	if (copy_from_user(tmpbuf, buf, count))
+		return -EFAULT;
+	tmpbuf[count] = '\0';
 
-	/* is userspace tring to explicitly UNSET the loginuid? */
-	if (loginuid == AUDIT_UID_UNSET) {
-		kloginuid = INVALID_UID;
-	} else {
-		kloginuid = make_kuid(file->f_cred->user_ns, loginuid);
-		if (!uid_valid(kloginuid))
+	kloginuid = make_kuid_str(file->f_cred->user_ns, tmpbuf);
+	if (!uid_valid(kloginuid)) {
+		u32 loginuid;
+
+		/* is userspace tring to explicitly UNSET the loginuid? */
+		if (kstrtou32(tmpbuf, 10, &loginuid) ||
+		    loginuid != AUDIT_UID_UNSET)
 			return -EINVAL;
 	}
 
-- 
2.56.0




More information about the Linux-security-module-archive mailing list