[RFC PATCH v2 25/41] uidgid: parse and print ids in their text form
Jan Koester
jan.koester at tuxist.de
Wed Oct 21 03:15:45 UTC 2026
Add helpers for interfaces that pass ids as text:
- make_kuid_str()/make_kgid_str() parse what kuid_str() writes: a
decimal number, a UUID, or a GUID in braces,
- kuid_str_munged()/kgid_str_munged() write an id as seen from a user
namespace, the overflow id if it has no mapping there,
- kuid_str_signed()/kgid_str_signed() write a legacy id as a signed
number, for output that used "%d",
- KUID_STR() and friends wrap them as expressions with a buffer of the
enclosing block, so that printf-like calls can replace
"%u", from_kuid(&init_user_ns, uid) by "%s", KUID_STR(uid) and print
legacy ids exactly as before.
Assisted-by: Claude:claude-opus-5-5
Signed-off-by: Jan Koester <jan.koester at tuxist.de>
---
include/linux/uidgid.h | 19 ++++++
kernel/uidgid_r.c | 152 +++++++++++++++++++++++++++++++++++++++--
2 files changed, 167 insertions(+), 4 deletions(-)
diff --git a/include/linux/uidgid.h b/include/linux/uidgid.h
index ac7786f760..03ece8a4cb 100644
--- a/include/linux/uidgid.h
+++ b/include/linux/uidgid.h
@@ -190,6 +190,25 @@ static inline u32 kgid_hash(kgid_t gid)
char *kuid_str(kuid_t uid, char buf[KID_STR_LEN]);
char *kgid_str(kgid_t gid, char buf[KID_STR_LEN]);
+char *kuid_str_signed(kuid_t uid, char buf[KID_STR_LEN]);
+char *kgid_str_signed(kgid_t gid, char buf[KID_STR_LEN]);
+
+/*
+ * The text form of an id as an expression, in a buffer that lives until
+ * the end of the enclosing block -- for "%s" in place of "%u" (or "%d",
+ * with the _D variants, which write a legacy id as a signed number) of
+ * from_kuid(&init_user_ns, uid) in printf-like calls.
+ */
+#define KUID_STR(uid) kuid_str(uid, (char [KID_STR_LEN]) {})
+#define KGID_STR(gid) kgid_str(gid, (char [KID_STR_LEN]) {})
+#define KUID_STR_D(uid) kuid_str_signed(uid, (char [KID_STR_LEN]) {})
+#define KGID_STR_D(gid) kgid_str_signed(gid, (char [KID_STR_LEN]) {})
+char *kuid_str_munged(struct user_namespace *to, kuid_t uid,
+ char buf[KID_STR_LEN]);
+char *kgid_str_munged(struct user_namespace *to, kgid_t gid,
+ char buf[KID_STR_LEN]);
+kuid_t make_kuid_str(struct user_namespace *from, const char *str);
+kgid_t make_kgid_str(struct user_namespace *from, const char *str);
#ifdef CONFIG_USER_NS
diff --git a/kernel/uidgid_r.c b/kernel/uidgid_r.c
index d71aecddb2..9bba60f413 100644
--- a/kernel/uidgid_r.c
+++ b/kernel/uidgid_r.c
@@ -12,6 +12,7 @@
#include <linux/uidgid.h>
#include <linux/uidgid_r.h>
#include <linux/user_namespace.h>
+#include <linux/uuid.h>
/*
* Whether @id is well formed: a known type, reserved bytes zero, a UUID of
@@ -278,11 +279,12 @@ int put_kgid_r(struct user_namespace *to, kgid_t kgid,
}
EXPORT_SYMBOL(put_kgid_r);
-static char *kid_str(u32 type, u32 legacy, const uuid_t *b, char *buf)
+static char *kid_str(u32 type, u32 legacy, const uuid_t *b, char *buf,
+ bool sgn)
{
switch (type) {
case KID_LEGACY:
- snprintf(buf, KID_STR_LEN, "%u", legacy);
+ snprintf(buf, KID_STR_LEN, sgn ? "%d" : "%u", legacy);
break;
case KID_UUID:
snprintf(buf, KID_STR_LEN, "%pUb", b);
@@ -304,7 +306,7 @@ static char *kid_str(u32 type, u32 legacy, const uuid_t *b, char *buf)
*/
char *kuid_str(kuid_t uid, char buf[KID_STR_LEN])
{
- return kid_str(uid.type, __kuid_val(uid), &uid.uuid, buf);
+ return kid_str(uid.type, __kuid_val(uid), &uid.uuid, buf, false);
}
EXPORT_SYMBOL(kuid_str);
@@ -318,6 +320,148 @@ EXPORT_SYMBOL(kuid_str);
*/
char *kgid_str(kgid_t gid, char buf[KID_STR_LEN])
{
- return kid_str(gid.type, __kgid_val(gid), &gid.uuid, buf);
+ return kid_str(gid.type, __kgid_val(gid), &gid.uuid, buf, false);
}
EXPORT_SYMBOL(kgid_str);
+
+/**
+ * kuid_str_signed - like kuid_str(), but a legacy uid as a signed number
+ * @uid: the kuid
+ * @buf: buffer of KID_STR_LEN bytes
+ *
+ * For callers that used to print from_kuid() with "%d".
+ *
+ * Return: @buf.
+ */
+char *kuid_str_signed(kuid_t uid, char buf[KID_STR_LEN])
+{
+ return kid_str(uid.type, __kuid_val(uid), &uid.uuid, buf, true);
+}
+EXPORT_SYMBOL(kuid_str_signed);
+
+/**
+ * kgid_str_signed - like kgid_str(), but a legacy gid as a signed number
+ * @gid: the kgid
+ * @buf: buffer of KID_STR_LEN bytes
+ *
+ * For callers that used to print from_kgid() with "%d".
+ *
+ * Return: @buf.
+ */
+char *kgid_str_signed(kgid_t gid, char buf[KID_STR_LEN])
+{
+ return kid_str(gid.type, __kgid_val(gid), &gid.uuid, buf, true);
+}
+EXPORT_SYMBOL(kgid_str_signed);
+
+/*
+ * Parses the text forms of kuid_str() -- a decimal number, a UUID, a GUID
+ * in braces -- with an optional trailing newline.
+ */
+static int kid_r_from_str(const char *str, struct __kernel_uid_r *id)
+{
+ char buf[KID_STR_LEN];
+ size_t len = strlen(str);
+
+ if (len && str[len - 1] == '\n')
+ len--;
+ if (!len || len >= sizeof(buf))
+ return -EINVAL;
+ memcpy(buf, str, len);
+ buf[len] = '\0';
+ memset(id, 0, sizeof(*id));
+
+ if (len == UUID_STRING_LEN) {
+ id->type = UID_R_UUID;
+ return uuid_parse(buf, (uuid_t *)id->uuid);
+ }
+ if (len == UUID_STRING_LEN + 2 && buf[0] == '{' && buf[len - 1] == '}') {
+ buf[len - 1] = '\0';
+ id->type = UID_R_GUID;
+ return guid_parse(buf + 1, (guid_t *)id->guid);
+ }
+ id->type = UID_R_LEGACY;
+ return kstrtou32(buf, 10, &id->legacy);
+}
+
+/**
+ * make_kuid_str - map the text form of a uid into a kuid
+ * @from: user namespace the uid is in
+ * @str: a decimal number, a UUID, or a GUID in braces; a trailing newline
+ * is allowed
+ *
+ * Return: the kuid, INVALID_UID if @str is malformed or has no mapping in
+ * @from.
+ */
+kuid_t make_kuid_str(struct user_namespace *from, const char *str)
+{
+ struct __kernel_uid_r id;
+
+ if (kid_r_from_str(str, &id))
+ return INVALID_UID;
+ return make_kuid_r(from, &id);
+}
+EXPORT_SYMBOL(make_kuid_str);
+
+/**
+ * make_kgid_str - map the text form of a gid into a kgid
+ * @from: user namespace the gid is in
+ * @str: a decimal number, a UUID, or a GUID in braces; a trailing newline
+ * is allowed
+ *
+ * Return: the kgid, INVALID_GID if @str is malformed or has no mapping in
+ * @from.
+ */
+kgid_t make_kgid_str(struct user_namespace *from, const char *str)
+{
+ struct __kernel_uid_r id;
+
+ if (kid_r_from_str(str, &id))
+ return INVALID_GID;
+ return make_kgid_r(from, &id);
+}
+EXPORT_SYMBOL(make_kgid_str);
+
+/**
+ * kuid_str_munged - text form of a kuid in a user namespace
+ * @to: user namespace the uid is wanted in
+ * @uid: the kuid
+ * @buf: buffer of KID_STR_LEN bytes
+ *
+ * Return: @buf, holding the uid in @to like kuid_str() does, overflowuid if
+ * @uid has no mapping there.
+ */
+char *kuid_str_munged(struct user_namespace *to, kuid_t uid,
+ char buf[KID_STR_LEN])
+{
+ if (!kuid_has_mapping(to, uid))
+ snprintf(buf, KID_STR_LEN, "%u", overflowuid);
+ else if (uid_is_legacy(uid))
+ snprintf(buf, KID_STR_LEN, "%u", from_kuid(to, uid));
+ else
+ kuid_str(uid, buf);
+ return buf;
+}
+EXPORT_SYMBOL(kuid_str_munged);
+
+/**
+ * kgid_str_munged - text form of a kgid in a user namespace
+ * @to: user namespace the gid is wanted in
+ * @gid: the kgid
+ * @buf: buffer of KID_STR_LEN bytes
+ *
+ * Return: @buf, holding the gid in @to like kgid_str() does, overflowgid if
+ * @gid has no mapping there.
+ */
+char *kgid_str_munged(struct user_namespace *to, kgid_t gid,
+ char buf[KID_STR_LEN])
+{
+ if (!kgid_has_mapping(to, gid))
+ snprintf(buf, KID_STR_LEN, "%u", overflowgid);
+ else if (gid_is_legacy(gid))
+ snprintf(buf, KID_STR_LEN, "%u", from_kgid(to, gid));
+ else
+ kgid_str(gid, buf);
+ return buf;
+}
+EXPORT_SYMBOL(kgid_str_munged);
--
2.56.0
More information about the Linux-security-module-archive
mailing list