[PATCH v3 04/25] fsuidgid: add fsid mapping helpers
From: Christian Brauner <hidden>
Date: 2020-02-18 14:36:24
Also in:
linux-fsdevel, linux-security-module, lkml
Subsystem:
the rest · Maintainer:
Linus Torvalds
This adds a set of helpers to translate between kfsuid/kfsgid and their
userspace fsuid/fsgid counter parts relative to a given user namespace.
- kuid_t make_kfsuid(struct user_namespace *from, uid_t fsuid)
Maps a user-namespace fsuid pair into a kfsuid.
If no fsuid mappings have been written it behaves identical to calling
make_kuid(). This ensures backwards compatibility for workloads unaware
or not in need of fsid mappings.
- kgid_t make_kfsgid(struct user_namespace *from, gid_t fsgid)
Maps a user-namespace fsgid pair into a kfsgid.
If no fsgid mappings have been written it behaves identical to calling
make_kgid(). This ensures backwards compatibility for workloads unaware
or not in need of fsid mappings.
- uid_t from_kfsuid(struct user_namespace *to, kuid_t fsuid)
Creates a fsuid from a kfsuid user-namespace pair if possible.
If no fsuid mappings have been written it behaves identical to calling
from_kuid(). This ensures backwards compatibility for workloads unaware
or not in need of fsid mappings.
- gid_t from_kfsgid(struct user_namespace *to, kgid_t fsgid)
Creates a fsgid from a kfsgid user-namespace pair if possible.
If no fsgid mappings have been written it behaves identical to calling
make_kgid(). This ensures backwards compatibility for workloads unaware
or not in need of fsid mappings.
- uid_t from_kfsuid_munged(struct user_namespace *to, kuid_t fsuid)
Always creates a fsuid from a kfsuid user-namespace pair.
If no fsuid mappings have been written it behaves identical to calling
from_kuid(). This ensures backwards compatibility for workloads unaware
or not in need of fsid mappings.
- gid_t from_kfsgid_munged(struct user_namespace *to, kgid_t fsgid)
Always creates a fsgid from a kfsgid user-namespace pair if possible.
If no fsgid mappings have been written it behaves identical to calling
make_kgid(). This ensures backwards compatibility for workloads unaware
or not in need of fsid mappings.
- bool kfsuid_has_mapping(struct user_namespace *ns, kuid_t uid)
Check whether this kfsuid has a mapping in the provided user namespace.
If no fsuid mappings have been written it behaves identical to calling
from_kuid(). This ensures backwards compatibility for workloads unaware
or not in need of fsid mappings.
- bool kfsgid_has_mapping(struct user_namespace *ns, kgid_t gid)
Check whether this kfsgid has a mapping in the provided user namespace.
If no fsgid mappings have been written it behaves identical to calling
make_kgid(). This ensures backwards compatibility for workloads unaware
or not in need of fsid mappings.
- kuid_t kfsuid_to_kuid(struct user_namespace *to, kuid_t kfsuid)
Translate from a kfsuid into a kuid.
- kgid_t kfsgid_to_kgid(struct user_namespace *to, kgid_t kfsgid)
Translate from a kfsgid into a kgid.
- kuid_t kuid_to_kfsuid(struct user_namespace *to, kuid_t kuid)
Translate from a kuid into a kfsuid.
- kgid_t kgid_to_kfsuid(struct user_namespace *to, kgid_t kgid)
Translate from a kgid into a kfsgid.
Cc: Jann Horn <jannh@google.com>
Signed-off-by: Christian Brauner <redacted>
---
/* v2 */
unchanged
/* v3 */
- Jann Horn [off-list ref]:
- Split changes to map_write() to implement fsid mappings into three separate
patches: basic fsid helpers, preparatory changes to map_write(), actual
fsid mapping support in map_write().
---
include/linux/fsuidgid.h | 122 +++++++++++++++++++++++++++++++++
kernel/user_namespace.c | 141 ++++++++++++++++++++++++++++++++++++++-
2 files changed, 261 insertions(+), 2 deletions(-)
create mode 100644 include/linux/fsuidgid.h
diff --git a/include/linux/fsuidgid.h b/include/linux/fsuidgid.h
new file mode 100644
index 000000000000..46763591f4e6
--- /dev/null
+++ b/include/linux/fsuidgid.h@@ -0,0 +1,122 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _LINUX_FSUIDGID_H +#define _LINUX_FSUIDGID_H + +#include <linux/uidgid.h> + +#ifdef CONFIG_USER_NS_FSID + +extern kuid_t make_kfsuid(struct user_namespace *from, uid_t fsuid); +extern kgid_t make_kfsgid(struct user_namespace *from, gid_t fsgid); +extern uid_t from_kfsuid(struct user_namespace *to, kuid_t kfsuid); +extern gid_t from_kfsgid(struct user_namespace *to, kgid_t kfsgid); +extern uid_t from_kfsuid_munged(struct user_namespace *to, kuid_t kfsuid); +extern gid_t from_kfsgid_munged(struct user_namespace *to, kgid_t kfsgid); + +static inline bool kfsuid_has_mapping(struct user_namespace *ns, kuid_t kfsuid) +{ + return from_kfsuid(ns, kfsuid) != (uid_t) -1; +} + +static inline bool kfsgid_has_mapping(struct user_namespace *ns, kgid_t kfsgid) +{ + return from_kfsgid(ns, kfsgid) != (gid_t) -1; +} + +static inline kuid_t kfsuid_to_kuid(struct user_namespace *to, kuid_t kfsuid) +{ + uid_t fsuid = from_kfsuid(to, kfsuid); + if (fsuid == (uid_t) -1) + return INVALID_UID; + return make_kuid(to, fsuid); +} + +static inline kgid_t kfsgid_to_kgid(struct user_namespace *to, kgid_t kfsgid) +{ + gid_t fsgid = from_kfsgid(to, kfsgid); + if (fsgid == (gid_t) -1) + return INVALID_GID; + return make_kgid(to, fsgid); +} + +static inline kuid_t kuid_to_kfsuid(struct user_namespace *to, kuid_t kuid) +{ + uid_t uid = from_kuid(to, kuid); + if (uid == (uid_t) -1) + return INVALID_UID; + return make_kfsuid(to, uid); +} + +static inline kgid_t kgid_to_kfsgid(struct user_namespace *to, kgid_t kgid) +{ + gid_t gid = from_kgid(to, kgid); + if (gid == (gid_t) -1) + return INVALID_GID; + return make_kfsgid(to, gid); +} + +#else + +static inline kuid_t make_kfsuid(struct user_namespace *from, uid_t fsuid) +{ + return make_kuid(from, fsuid); +} + +static inline kgid_t make_kfsgid(struct user_namespace *from, gid_t fsgid) +{ + return make_kgid(from, fsgid); +} + +static inline uid_t from_kfsuid(struct user_namespace *to, kuid_t kfsuid) +{ + return from_kuid(to, kfsuid); +} + +static inline gid_t from_kfsgid(struct user_namespace *to, kgid_t kfsgid) +{ + return from_kgid(to, kfsgid); +} + +static inline uid_t from_kfsuid_munged(struct user_namespace *to, kuid_t kfsuid) +{ + return from_kuid_munged(to, kfsuid); +} + +static inline gid_t from_kfsgid_munged(struct user_namespace *to, kgid_t kfsgid) +{ + return from_kgid_munged(to, kfsgid); +} + +static inline bool kfsuid_has_mapping(struct user_namespace *ns, kuid_t kfsuid) +{ + return kuid_has_mapping(ns, kfsuid); +} + +static inline bool kfsgid_has_mapping(struct user_namespace *ns, kgid_t kfsgid) +{ + return kgid_has_mapping(ns, kfsgid); +} + +static inline kuid_t kfsuid_to_kuid(struct user_namespace *to, kuid_t kfsuid) +{ + return kfsuid; +} + +static inline kgid_t kfsgid_to_kgid(struct user_namespace *to, kgid_t kfsgid) +{ + return kfsgid; +} + +static inline kuid_t kuid_to_kfsuid(struct user_namespace *to, kuid_t kuid) +{ + return kuid; +} + +static inline kgid_t kgid_to_kfsgid(struct user_namespace *to, kgid_t kgid) +{ + return kgid; +} + +#endif /* CONFIG_USER_NS_FSID */ + +#endif /* _LINUX_FSUIDGID_H */
diff --git a/kernel/user_namespace.c b/kernel/user_namespace.c
index cbdf456f95f0..2cfd1e519cc4 100644
--- a/kernel/user_namespace.c
+++ b/kernel/user_namespace.c@@ -20,6 +20,7 @@ #include <linux/fs_struct.h> #include <linux/bsearch.h> #include <linux/sort.h> +#include <linux/fsuidgid.h> static struct kmem_cache *user_ns_cachep __read_mostly; static DEFINE_MUTEX(userns_state_mutex);
@@ -583,6 +584,142 @@ projid_t from_kprojid_munged(struct user_namespace *targ, kprojid_t kprojid) } EXPORT_SYMBOL(from_kprojid_munged); +#ifdef CONFIG_USER_NS_FSID +/** + * make_kfsuid - Map a user-namespace fsuid pair into a kuid. + * @ns: User namespace that the fsuid is in + * @fsuid: User identifier + * + * Maps a user-namespace fsuid pair into a kernel internal kfsuid, + * and returns that kfsuid. + * + * When there is no mapping defined for the user-namespace kfsuid + * pair INVALID_UID is returned. Callers are expected to test + * for and handle INVALID_UID being returned. INVALID_UID + * may be tested for using uid_valid(). + */ +kuid_t make_kfsuid(struct user_namespace *ns, uid_t fsuid) +{ + /* Map the fsuid to a global kernel fsuid */ + return KUIDT_INIT(map_id_down(&ns->fsuid_map, fsuid)); +} +EXPORT_SYMBOL(make_kfsuid); + +/** + * from_kfsuid - Create a fsuid from a kfsuid user-namespace pair. + * @targ: The user namespace we want a fsuid in. + * @kfsuid: The kernel internal fsuid to start with. + * + * Map @kfsuid into the user-namespace specified by @targ and + * return the resulting fsuid. + * + * There is always a mapping into the initial user_namespace. + * + * If @kfsuid has no mapping in @targ (uid_t)-1 is returned. + */ +uid_t from_kfsuid(struct user_namespace *targ, kuid_t kfsuid) +{ + /* Map the fsuid from a global kernel fsuid */ + return map_id_up(&targ->fsuid_map, __kuid_val(kfsuid)); +} +EXPORT_SYMBOL(from_kfsuid); + +/** + * from_kfsuid_munged - Create a fsuid from a kfsuid user-namespace pair. + * @targ: The user namespace we want a fsuid in. + * @kfsuid: The kernel internal fsuid to start with. + * + * Map @kfsuid into the user-namespace specified by @targ and + * return the resulting fsuid. + * + * There is always a mapping into the initial user_namespace. + * + * Unlike from_kfsuid from_kfsuid_munged never fails and always + * returns a valid fsuid. This makes from_kfsuid_munged appropriate + * for use in syscalls like stat and getuid where failing the + * system call and failing to provide a valid fsuid are not an + * options. + * + * If @kfsuid has no mapping in @targ overflowuid is returned. + */ +uid_t from_kfsuid_munged(struct user_namespace *targ, kuid_t kfsuid) +{ + uid_t fsuid; + fsuid = from_kfsuid(targ, kfsuid); + + if (fsuid == (uid_t) -1) + fsuid = overflowuid; + return fsuid; +} +EXPORT_SYMBOL(from_kfsuid_munged); + +/** + * make_kfsgid - Map a user-namespace fsgid pair into a kfsgid. + * @ns: User namespace that the fsgid is in + * @fsgid: User identifier + * + * Maps a user-namespace fsgid pair into a kernel internal kfsgid, + * and returns that kfsgid. + * + * When there is no mapping defined for the user-namespace fsgid + * pair INVALID_GID is returned. Callers are expected to test + * for and handle INVALID_GID being returned. INVALID_GID + * may be tested for using gid_valid(). + */ +kgid_t make_kfsgid(struct user_namespace *ns, gid_t fsgid) +{ + /* Map the fsgid to a global kernel fsgid */ + return KGIDT_INIT(map_id_down(&ns->fsgid_map, fsgid)); +} +EXPORT_SYMBOL(make_kfsgid); + +/** + * from_kfsgid - Create a fsgid from a kfsgid user-namespace pair. + * @targ: The user namespace we want a fsgid in. + * @kfsgid: The kernel internal fsgid to start with. + * + * Map @kfsgid into the user-namespace specified by @targ and + * return the resulting fsgid. + * + * There is always a mapping into the initial user_namespace. + * + * If @kfsgid has no mapping in @targ (gid_t)-1 is returned. + */ +gid_t from_kfsgid(struct user_namespace *targ, kgid_t kfsgid) +{ + /* Map the fsgid from a global kernel fsgid */ + return map_id_up(&targ->fsgid_map, __kgid_val(kfsgid)); +} +EXPORT_SYMBOL(from_kfsgid); + +/** + * from_kfsgid_munged - Create a fsgid from a kfsgid user-namespace pair. + * @targ: The user namespace we want a fsgid in. + * @kfsgid: The kernel internal fsgid to start with. + * + * Map @kfsgid into the user-namespace specified by @targ and + * return the resulting fsgid. + * + * There is always a mapping into the initial user_namespace. + * + * Unlike from_kfsgid from_kfsgid_munged never fails and always + * returns a valid fsgid. This makes from_kfsgid_munged appropriate + * for use in syscalls like stat and getgid where failing the + * system call and failing to provide a valid fsgid are not options. + * + * If @kfsgid has no mapping in @targ overflowgid is returned. + */ +gid_t from_kfsgid_munged(struct user_namespace *targ, kgid_t kfsgid) +{ + gid_t fsgid; + fsgid = from_kfsgid(targ, kfsgid); + + if (fsgid == (gid_t) -1) + fsgid = overflowgid; + return fsgid; +} +EXPORT_SYMBOL(from_kfsgid_munged); +#endif /* CONFIG_USER_NS_FSID */ static int uid_m_show(struct seq_file *seq, void *v) {
@@ -659,7 +796,7 @@ static int fsuid_m_show(struct seq_file *seq, void *v) if ((lower_ns == ns) && lower_ns->parent) lower_ns = lower_ns->parent; - lower = from_kuid(lower_ns, KUIDT_INIT(extent->lower_first)); + lower = from_kfsuid(lower_ns, KUIDT_INIT(extent->lower_first)); seq_printf(seq, "%10u %10u %10u\n", extent->first,
@@ -680,7 +817,7 @@ static int fsgid_m_show(struct seq_file *seq, void *v) if ((lower_ns == ns) && lower_ns->parent) lower_ns = lower_ns->parent; - lower = from_kgid(lower_ns, KGIDT_INIT(extent->lower_first)); + lower = from_kfsgid(lower_ns, KGIDT_INIT(extent->lower_first)); seq_printf(seq, "%10u %10u %10u\n", extent->first,
--
2.25.0