/proc//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//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 --- 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