[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