From: Christian Brauner <hidden> Date: 2020-02-11 17:00:32
Hey everyone,
This is the implementation of shiftfs which was cooked up during lunch at
Linux Plumbers 2019 the day after the container's microconference. The
idea is a design-stew from Stéphane, Aleksa, Eric, and myself. Back then
we all were quite busy with other work and couldn't really sit down and
implement it. But I took a few days last week to do this work, including
demos and performance testing.
This implementation does not require us to touch the vfs substantially
at all. Instead, we implement shiftfs via fsid mappings.
With this patch, it took me 20 mins to port both LXD and LXC to support
shiftfs via fsid mappings.
For anyone wanting to play with this the branch can be pulled from:
https://github.com/brauner/linux/tree/fsid_mappingshttps://gitlab.com/brauner/linux/-/tree/fsid_mappingshttps://git.kernel.org/pub/scm/linux/kernel/git/brauner/linux.git/log/?h=fsid_mappings
The main use case for shiftfs for us is in allowing shared writable
storage to multiple containers using non-overlapping id mappings.
In such a scenario you want the fsids to be valid and identical in both
containers for the shared mount. A demo for this exists in [3].
If you don't want to read on, go straight to the other demos below in
[1] and [2].
People not as familiar with user namespaces might not be aware that fsid
mappings already exist. Right now, fsid mappings are always identical to
id mappings. Specifically, the kernel will lookup fsuids in the uid
mappings and fsgids in the gid mappings of the relevant user namespace.
With this patch series we simply introduce the ability to create fsid
mappings that are different from the id mappings of a user namespace.
In the usual case of running an unprivileged container we will have
setup an id mapping, e.g. 0 100000 100000. The on-disk mapping will
correspond to this id mapping, i.e. all files which we want to appear as
0:0 inside the user namespace will be chowned to 100000:100000 on the
host. This works, because whenever the kernel needs to do a filesystem
access it will lookup the corresponding uid and gid in the idmapping
tables of the container.
Now think about the case where we want to have an id mapping of 0 100000
100000 but an on-disk mapping of 0 300000 100000 which is needed to e.g.
share a single on-disk mapping with multiple containers that all have
different id mappings.
This will be problematic. Whenever a filesystem access is requested, the
kernel will now try to lookup a mapping for 300000 in the id mapping
tables of the user namespace but since there is none the files will
appear to be owned by the overflow id, i.e. usually 65534:65534 or
nobody:nogroup.
With fsid mappings we can solve this by writing an id mapping of 0
100000 100000 and an fsid mapping of 0 300000 100000. On filesystem
access the kernel will now lookup the mapping for 300000 in the fsid
mapping tables of the user namespace. And since such a mapping exists,
the corresponding files will have correct ownership.
A note on proc (and sys), the proc filesystem is special in sofar as it
only has a single superblock that is (currently but might be about to
change) visible in all user namespaces (same goes for sys). This means
it has special semantics in many ways, including how file ownership and
access works. The fsid mapping implementation does not alter how proc
(and sys) ownership works. proc and sys will both continue to lookup
filesystem access in id mapping tables.
When Writing fsid mappings the same rules apply as when writing id
mappings so I won't reiterate them here. The limit of fs id mappings is
the same as for id mappings, i.e. 340 lines.
# Performance
Back when I extended the range of possible id mappings to 340 I did
performance testing by booting into single user mode, creating 1,000,000
files to fstat()ing them and calculated the mean fstat() time per file.
(Back when Linux was still fast. I won't mention that the stat
numbers have (thanks microcode!) doubled since then...)
I did the same test for this patchset: one vanilla kernel, one kernel
with my fsid mapping patches but CONFIG_USER_NS_FSID set to n and one
with fsid mappings patches enabled. I then ran the same test on all
three kernels and compared the numbers. The implementation does not
introduce overhead. That's all I can say. Here are the numbers:
| vanilla v5.5 | fsid mappings | fsid mappings | fsid mappings |
| | disabled in Kconfig | enabled in Kconfig | enabled in Kconfig |
| | | and unset for all | and set for all |
| | | test cases | test cases |
-------------|--------------|---------------------|--------------------|--------------------|
0 mappings | 367 ns | 365 ns | 365 ns | N/A |
1 mappings | 362 ns | 367 ns | 363 ns | 363 ns |
2 mappings | 361 ns | 369 ns | 363 ns | 364 ns |
3 mappings | 361 ns | 368 ns | 366 ns | 365 ns |
5 mappings | 365 ns | 368 ns | 363 ns | 365 ns |
10 mappings | 391 ns | 388 ns | 387 ns | 389 ns |
50 mappings | 395 ns | 398 ns | 401 ns | 397 ns |
100 mappings | 400 ns | 405 ns | 399 ns | 399 ns |
200 mappings | 404 ns | 407 ns | 430 ns | 404 ns |
300 mappings | 492 ns | 494 ns | 432 ns | 413 ns |
340 mappings | 495 ns | 497 ns | 500 ns | 484 ns |
# Demos
[1]: Create a container with different id and fsid mappings.
https://asciinema.org/a/300233
[2]: Create a container with id mappings but without fsid mappings.
https://asciinema.org/a/300234
[3]: Share storage between multiple containers with non-overlapping id
mappings.
https://asciinema.org/a/300235
Thanks!
Christian
Christian Brauner (24):
user_namespace: introduce fsid mappings infrastructure
proc: add /proc/<pid>/fsuid_map
proc: add /proc/<pid>/fsgid_map
fsuidgid: add fsid mapping helpers
proc: task_state(): use from_kfs{g,u}id_munged
fs: add is_userns_visible() helper
namei: may_{o_}create(): handle fsid mappings
inode: inode_owner_or_capable(): handle fsid mappings
capability: privileged_wrt_inode_uidgid(): handle fsid mappings
stat: handle fsid mappings
open: chown_common(): handle fsid mappings
posix_acl: handle fsid mappings
attr: notify_change(): handle fsid mappings
commoncap: cap_task_fix_setuid(): handle fsid mappings
commoncap:cap_bprm_set_creds(): handle fsid mappings
sys: __sys_setfsuid(): handle fsid mappings
sys: __sys_setfsgid(): handle fsid mappings
sys:__sys_setuid(): handle fsid mappings
sys:__sys_setgid(): handle fsid mappings
sys:__sys_setreuid(): handle fsid mappings
sys:__sys_setregid(): handle fsid mappings
sys:__sys_setresuid(): handle fsid mappings
sys:__sys_setresgid(): handle fsid mappings
devpts: handle fsid mappings
fs/attr.c | 23 ++-
fs/devpts/inode.c | 7 +-
fs/inode.c | 7 +-
fs/namei.c | 21 ++-
fs/open.c | 10 +-
fs/posix_acl.c | 21 +--
fs/proc/array.c | 5 +-
fs/proc/base.c | 34 ++++
fs/stat.c | 48 ++++--
include/linux/fs.h | 5 +
include/linux/fsuidgid.h | 70 ++++++++
include/linux/stat.h | 1 +
include/linux/user_namespace.h | 10 ++
init/Kconfig | 11 ++
kernel/capability.c | 13 +-
kernel/sys.c | 83 ++++++---
kernel/user.c | 22 +++
kernel/user_namespace.c | 303 ++++++++++++++++++++++++++++++++-
security/commoncap.c | 19 ++-
19 files changed, 638 insertions(+), 75 deletions(-)
create mode 100644 include/linux/fsuidgid.h
base-commit: d5226fa6dbae0569ee43ecfc08bdcd6770fc4755
--
2.25.0
From: Christian Brauner <hidden> Date: 2020-02-11 16:59:48
Switch setgid() to lookup fsids in the fsid mappings. If no fsid mappings are
setup the behavior is unchanged, i.e. fsids are looked up in the id mappings.
Signed-off-by: Christian Brauner <redacted>
---
kernel/sys.c | 19 +++++++++++++------
1 file changed, 13 insertions(+), 6 deletions(-)
From: Christian Brauner <hidden> Date: 2020-02-11 16:59:57
During exec the kfsids are currently reset to the effective kids. To retain the
same semantics with the introduction of fsid mappings, we lookup the userspace
effective id in the id mappings and translate the effective id into the
corresponding kfsid in the fsidmapping. This means, the behavior is unchanged
when no fsid mappings are setup and the semantics stay the same even when fsid
mappings are setup.
Signed-off-by: Christian Brauner <redacted>
---
security/commoncap.c | 16 +++++++++++++---
1 file changed, 13 insertions(+), 3 deletions(-)
From: Christian Brauner <hidden> Date: 2020-02-11 16:59:58
Switch setfsgid() to lookup fsids in the fsid mappings. If no fsid mappings are
setup the behavior is unchanged, i.e. fsids are looked up in the id mappings.
Signed-off-by: Christian Brauner <redacted>
---
kernel/sys.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
From: Christian Brauner <hidden> Date: 2020-02-11 17:00:00
Switch setfsuid() to lookup fsids in the fsid mappings. If no fsid mappings are
setup the behavior is unchanged, i.e. fsids are looked up in the id mappings.
Signed-off-by: Christian Brauner <redacted>
---
kernel/sys.c | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
From: Christian Brauner <hidden> Date: 2020-02-11 17:00:11
Switch setuid() to lookup fsids in the fsid mappings. If no fsid mappings are
setup the behavior is unchanged, i.e. fsids are looked up in the id mappings.
Signed-off-by: Christian Brauner <redacted>
---
kernel/sys.c | 8 +++++++-
1 file changed, 7 insertions(+), 1 deletion(-)
From: Christian Brauner <hidden> Date: 2020-02-11 17:00:12
Switch cap_task_fix_setuid() to lookup fsids in the fsid mappings. If no fsid
mappings are setup the behavior is unchanged, i.e. fsids are looked up in the
id mappings.
Signed-off-by: Christian Brauner <redacted>
---
security/commoncap.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
From: Christian Brauner <hidden> Date: 2020-02-11 17:00:14
Switch notify_change() to lookup fsids in the fsid mappings. If no fsid
mappings are setup the behavior is unchanged, i.e. fsids are looked up in the
id mappings.
Filesystems that share a superblock in all user namespaces they are mounted in
will retain their old semantics even with the introduction of fsidmappings.
Signed-off-by: Christian Brauner <redacted>
---
fs/attr.c | 23 +++++++++++++++++------
1 file changed, 17 insertions(+), 6 deletions(-)
From: Christian Brauner <hidden> Date: 2020-02-11 17:00:22
Switch chown_common() to lookup fsids in the fsid mappings. If no fsid
mappings are setup the behavior is unchanged, i.e. fsids are looked up in the
id mappings.
Filesystems that share a superblock in all user namespaces they are mounted in
will retain their old semantics even with the introduction of fsidmappings.
Signed-off-by: Christian Brauner <redacted>
---
fs/open.c | 10 ++++++++--
1 file changed, 8 insertions(+), 2 deletions(-)
From: Christian Brauner <hidden> Date: 2020-02-11 17:00:29
Switch posix_acls() to lookup fsids in the fsid mappings. If no fsid
mappings are setup the behavior is unchanged, i.e. fsids are looked up in the
id mappings.
Afaict, all filesystems that share a superblock in all user namespaces
currently do not support acls so this change should be safe to do
unconditionally.
Signed-off-by: Christian Brauner <redacted>
---
fs/posix_acl.c | 21 +++++++++++----------
1 file changed, 11 insertions(+), 10 deletions(-)
From: Christian Brauner <hidden> Date: 2020-02-11 17:00:34
This introduces the infrastructure to setup fsid mappings which will be used in
later patches.
All new code depends on CONFIG_USER_NS_FSID=y. It currently defaults to "N".
If CONFIG_USER_NS_FSID is not set, no new code is added.
In this patch fsuid_m_show() and fsgid_m_show() are introduced. They are
identical to uid_m_show() and gid_m_show() until we introduce from_kfsuid() and
from_kfsgid() in a follow-up patch.
Signed-off-by: Christian Brauner <redacted>
---
include/linux/user_namespace.h | 10 +++
init/Kconfig | 11 +++
kernel/user.c | 22 ++++++
kernel/user_namespace.c | 122 +++++++++++++++++++++++++++++++++
4 files changed, 165 insertions(+)
+ With this containers with different id mappings can still share
+ the same filesystem.
+
+ If unsure, say N.
+
config PID_NS
bool "PID Namespaces"
default y
From: Christian Brauner <hidden> Date: 2020-02-11 17:00:39
Switch setreuid() to lookup fsids in the fsid mappings. If no fsid mappings are
setup the behavior is unchanged, i.e. fsids are looked up in the id mappings.
During setreuid() the kfsuid is set to the keuid corresponding the euid that is
requested by userspace. If the requested euid is -1 the kfsuid is reset to the
current keuid. For the latter case this means we need to lookup the
corresponding userspace euid corresponding to the current keuid in the id
mappings and translate this euid into the corresponding kfsuid in the fsid
mappings.
Signed-off-by: Christian Brauner <redacted>
---
kernel/sys.c | 10 ++++++++--
1 file changed, 8 insertions(+), 2 deletions(-)
From: Christian Brauner <hidden> Date: 2020-02-11 17:00:40
Switch may_{o_}create() to lookup fsids in the fsid mappings. If no fsid
mappings are setup the behavior is unchanged, i.e. fsids are looked up in the
id mappings.
Filesystems that share a superblock in all user namespaces they are mounted in
will retain their old semantics even with the introduction of fsidmappings.
Signed-off-by: Christian Brauner <redacted>
---
fs/namei.c | 21 +++++++++++++++++----
1 file changed, 17 insertions(+), 4 deletions(-)
@@ -2771,6 +2772,20 @@ static int may_delete(struct inode *dir, struct dentry *victim, bool isdir)return0;}+staticboolfsid_has_mapping(structuser_namespace*ns,structsuper_block*sb)+{+if(is_userns_visible(sb->s_iflags)){+if(!kuid_has_mapping(ns,current_fsuid())||+!kgid_has_mapping(ns,current_fsgid()))+returnfalse;+}elseif(!kfsuid_has_mapping(ns,current_fsuid())||+!kfsgid_has_mapping(ns,current_fsgid())){+returnfalse;+}++returntrue;+}+/* Check whether we can create an object with dentry child in directory*dir.*1.Wecan'tdoitifchildalreadyexists(openhasspecialtreatmentfor
From: Christian Brauner <hidden> Date: 2020-02-11 17:00:42
Switch inode_owner_or_capable() to lookup fsids in the fsid mappings. If no
fsid mappings are setup the behavior is unchanged, i.e. fsids are looked up in
the id mappings.
Filesystems that share a superblock in all user namespaces they are mounted in
will retain their old semantics even with the introduction of fsidmappings.
Signed-off-by: Christian Brauner <redacted>
---
fs/inode.c | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
From: Christian Brauner <hidden> Date: 2020-02-11 17:00:48
Switch attribute functions looking up fsids to them up in the fsid mappings. If
no fsid mappings are setup the behavior is unchanged, i.e. fsids are looked up
in the id mappings.
Filesystems that share a superblock in all user namespaces they are mounted in
will retain their old semantics even with the introduction of fsidmappings.
Signed-off-by: Christian Brauner <redacted>
---
fs/stat.c | 48 +++++++++++++++++++++++++++++++++++---------
include/linux/stat.h | 1 +
2 files changed, 39 insertions(+), 10 deletions(-)
From: Christian Brauner <hidden> Date: 2020-02-11 17:01:01
The /proc/<pid>/fsuid_map file can be written to once to setup an fsuid mapping
for a user namespace. Writing to this file has the same restrictions as writing
to /proc/<pid>/fsuid_map:
root@e1-vm:/# cat /proc/13023/fsuid_map
0 300000 100000
Fsid mappings have always been around. They are currently always identical to
the id mappings for a user namespace. This means, currently whenever an fsid
needs to be looked up the kernel will use the id mapping of the user namespace.
With the introduction of fsid mappings the kernel will now lookup fsids in the
fsid mappings of the user namespace. If no fsid mapping exists the kernel will
continue looking up fsids in the id mappings of the user namespace. Hence, if a
system supports fsid mappings through /proc/<pid>/fs*id_map and a container
runtime is not aware of fsid mappings it or does not use them it will it will
continue to work just as before.
Signed-off-by: Christian Brauner <redacted>
---
fs/proc/base.c | 20 ++++++++++++++++++++
1 file changed, 20 insertions(+)
From: Christian Brauner <hidden> Date: 2020-02-11 17:01:03
The /proc/<pid>/fsgid_map file can be written to once to setup an fsgid mapping
for a user namespace. Writing to this file has the same restrictions as writing
to /proc/<pid>/fsgid_map.
root@e1-vm:/# cat /proc/13023/fsgid_map
0 300000 100000
Fsid mappings have always been around. They are currently always identical to
the id mappings for a user namespace. This means, currently whenever an fsid
needs to be looked up the kernel will use the id mapping of the user namespace.
With the introduction of fsid mappings the kernel will now lookup fsids in the
fsid mappings of the user namespace. If no fsid mapping exists the kernel will
continue looking up fsids in the id mappings of the user namespace. Hence, if a
system supports fsid mappings through /proc/<pid>/fs*id_map and a container
runtime is not aware of fsid mappings it or does not use them it will it will
continue to work just as before.
Signed-off-by: Christian Brauner <redacted>
---
fs/proc/base.c | 14 ++++++++++++++
1 file changed, 14 insertions(+)
From: Christian Brauner <hidden> Date: 2020-02-11 17:01:05
When a uid or gid mount option is specified with devpts have it lookup the
corresponding kfsids in the fsid mappings. If no fsid mappings are setup the
behavior is unchanged, i.e. fsids are looked up in the id mappings.
Signed-off-by: Christian Brauner <redacted>
---
fs/devpts/inode.c | 7 ++++---
1 file changed, 4 insertions(+), 3 deletions(-)
From: Christian Brauner <hidden> Date: 2020-02-11 17:01:11
Switch privileged_wrt_inode_uidgid() to lookup fsids in the fsid mappings. If
no fsid mappings are setup the behavior is unchanged, i.e. fsids are looked up
in the id mappings.
Filesystems that share a superblock in all user namespaces they are mounted in
will retain their old semantics even with the introduction of fsidmappings.
Signed-off-by: Christian Brauner <redacted>
---
kernel/capability.c | 13 ++++++++++---
1 file changed, 10 insertions(+), 3 deletions(-)
From: Christian Brauner <hidden> Date: 2020-02-11 17:01:17
Switch setresuid() to lookup fsids in the fsid mappings. If no fsid mappings
are setup the behavior is unchanged, i.e. fsids are looked up in the id
mappings.
During setresuid() the kfsuid is set to the keuid corresponding the euid that is
requested by userspace. If the requested euid is -1 the kfsuid is reset to the
current keuid. For the latter case this means we need to lookup the
corresponding userspace euid corresponding to the current keuid in the id
mappings and translate this euid into the corresponding kfsuid in the fsid
mappings.
Signed-off-by: Christian Brauner <redacted>
---
kernel/sys.c | 14 +++++++++++---
1 file changed, 11 insertions(+), 3 deletions(-)
From: Christian Brauner <hidden> Date: 2020-02-11 17:01:22
Introduce a helper which makes it possible to detect fileystems whose
superblock is visible in multiple user namespace. This currently only
means proc and sys. Such filesystems usually have special semantics so their
behavior will not be changed with the introduction of fsid mappings.
Signed-off-by: Christian Brauner <redacted>
---
include/linux/fs.h | 5 +++++
1 file changed, 5 insertions(+)
From: Christian Brauner <hidden> Date: 2020-02-11 17:01:23
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.
Signed-off-by: Christian Brauner <redacted>
---
include/linux/fsuidgid.h | 70 +++++++++++++++
kernel/user_namespace.c | 189 ++++++++++++++++++++++++++++++++++++---
2 files changed, 246 insertions(+), 13 deletions(-)
create mode 100644 include/linux/fsuidgid.h
@@ -583,6 +584,166 @@ projid_t from_kprojid_munged(struct user_namespace *targ, kprojid_t kprojid)}EXPORT_SYMBOL(from_kprojid_munged);+#ifdef CONFIG_USER_NS_FSID+/**+*make_kfsuid-Mapauser-namespacefsuidpairintoakuid.+*@ns:Usernamespacethatthefsuidisin+*@fsuid:Useridentifier+*+*Mapsauser-namespacefsuidpairintoakernelinternalkfsuid,+*andreturnsthatkfsuid.+*+*Whenthereisnomappingdefinedfortheuser-namespacekfsuid+*pairINVALID_UIDisreturned.Callersareexpectedtotest+*forandhandleINVALID_UIDbeingreturned.INVALID_UID+*maybetestedforusinguid_valid().+*/+kuid_tmake_kfsuid(structuser_namespace*ns,uid_tfsuid)+{+unsignedextents=ns->fsuid_map.nr_extents;+smp_rmb();++/* Map the fsuid to a global kernel fsuid */+if(extents==0)+returnKUIDT_INIT(map_id_down(&ns->uid_map,fsuid));++returnKUIDT_INIT(map_id_down(&ns->fsuid_map,fsuid));+}+EXPORT_SYMBOL(make_kfsuid);++/**+*from_kfsuid-Createafsuidfromakfsuiduser-namespacepair.+*@targ:Theusernamespacewewantafsuidin.+*@kfsuid:Thekernelinternalfsuidtostartwith.+*+*Map@kfsuidintotheuser-namespacespecifiedby@targand+*returntheresultingfsuid.+*+*Thereisalwaysamappingintotheinitialuser_namespace.+*+*If@kfsuidhasnomappingin@targ(uid_t)-1isreturned.+*/+uid_tfrom_kfsuid(structuser_namespace*targ,kuid_tkfsuid)+{+unsignedextents=targ->fsuid_map.nr_extents;+smp_rmb();++/* Map the fsuid from a global kernel fsuid */+if(extents==0)+returnmap_id_up(&targ->uid_map,__kuid_val(kfsuid));++returnmap_id_up(&targ->fsuid_map,__kuid_val(kfsuid));+}+EXPORT_SYMBOL(from_kfsuid);++/**+*from_kfsuid_munged-Createafsuidfromakfsuiduser-namespacepair.+*@targ:Theusernamespacewewantafsuidin.+*@kfsuid:Thekernelinternalfsuidtostartwith.+*+*Map@kfsuidintotheuser-namespacespecifiedby@targand+*returntheresultingfsuid.+*+*Thereisalwaysamappingintotheinitialuser_namespace.+*+*Unlikefrom_kfsuidfrom_kfsuid_mungedneverfailsandalways+*returnsavalidfsuid.Thismakesfrom_kfsuid_mungedappropriate+*foruseinsyscallslikestatandgetuidwherefailingthe+*systemcallandfailingtoprovideavalidfsuidarenotan+*options.+*+*If@kfsuidhasnomappingin@targoverflowuidisreturned.+*/+uid_tfrom_kfsuid_munged(structuser_namespace*targ,kuid_tkfsuid)+{+uid_tfsuid;+fsuid=from_kfsuid(targ,kfsuid);++if(fsuid==(uid_t)-1)+fsuid=overflowuid;+returnfsuid;+}+EXPORT_SYMBOL(from_kfsuid_munged);++/**+*make_kfsgid-Mapauser-namespacefsgidpairintoakfsgid.+*@ns:Usernamespacethatthefsgidisin+*@fsgid:Useridentifier+*+*Mapsauser-namespacefsgidpairintoakernelinternalkfsgid,+*andreturnsthatkfsgid.+*+*Whenthereisnomappingdefinedfortheuser-namespacefsgid+*pairINVALID_GIDisreturned.Callersareexpectedtotest+*forandhandleINVALID_GIDbeingreturned.INVALID_GID+*maybetestedforusinggid_valid().+*/+kgid_tmake_kfsgid(structuser_namespace*ns,gid_tfsgid)+{+unsignedextents=ns->fsgid_map.nr_extents;+smp_rmb();++/* Map the fsgid to a global kernel fsgid */+if(extents==0)+returnKGIDT_INIT(map_id_down(&ns->gid_map,fsgid));++returnKGIDT_INIT(map_id_down(&ns->fsgid_map,fsgid));+}+EXPORT_SYMBOL(make_kfsgid);++/**+*from_kfsgid-Createafsgidfromakfsgiduser-namespacepair.+*@targ:Theusernamespacewewantafsgidin.+*@kfsgid:Thekernelinternalfsgidtostartwith.+*+*Map@kfsgidintotheuser-namespacespecifiedby@targand+*returntheresultingfsgid.+*+*Thereisalwaysamappingintotheinitialuser_namespace.+*+*If@kfsgidhasnomappingin@targ(gid_t)-1isreturned.+*/+gid_tfrom_kfsgid(structuser_namespace*targ,kgid_tkfsgid)+{+unsignedextents=targ->fsgid_map.nr_extents;+smp_rmb();++/* Map the fsgid from a global kernel fsgid */+if(extents==0)+returnmap_id_up(&targ->gid_map,__kgid_val(kfsgid));++returnmap_id_up(&targ->fsgid_map,__kgid_val(kfsgid));+}+EXPORT_SYMBOL(from_kfsgid);++/**+*from_kfsgid_munged-Createafsgidfromakfsgiduser-namespacepair.+*@targ:Theusernamespacewewantafsgidin.+*@kfsgid:Thekernelinternalfsgidtostartwith.+*+*Map@kfsgidintotheuser-namespacespecifiedby@targand+*returntheresultingfsgid.+*+*Thereisalwaysamappingintotheinitialuser_namespace.+*+*Unlikefrom_kfsgidfrom_kfsgid_mungedneverfailsandalways+*returnsavalidfsgid.Thismakesfrom_kfsgid_mungedappropriate+*foruseinsyscallslikestatandgetgidwherefailingthe+*systemcallandfailingtoprovideavalidfsgidarenotoptions.+*+*If@kfsgidhasnomappingin@targoverflowgidisreturned.+*/+gid_tfrom_kfsgid_munged(structuser_namespace*targ,kgid_tkfsgid)+{+gid_tfsgid;+fsgid=from_kfsgid(targ,kfsgid);++if(fsgid==(gid_t)-1)+fsgid=overflowgid;+returnfsgid;+}+EXPORT_SYMBOL(from_kfsgid_munged);+#endif /* CONFIG_USER_NS_FSID */staticintuid_m_show(structseq_file*seq,void*v){
@@ -1051,7 +1212,7 @@ static ssize_t map_write(struct file *file, const char __user *buf,ret=-EPERM;/* Validate the user is allowed to use user id's mapped to. */-if(!new_idmap_permitted(file,ns,cap_setid,&new_map))+if(!new_idmap_permitted(file,ns,cap_setid,&new_map,map_fsid))gotoout;ret=-EPERM;
@@ -1164,7 +1325,7 @@ ssize_t proc_projid_map_write(struct file *file, const char __user *buf,/* Anyone can set any valid project id no capability needed */returnmap_write(file,buf,size,ppos,-1,-&ns->projid_map,&ns->parent->projid_map);+&ns->projid_map,&ns->parent->projid_map,false);}#ifdef CONFIG_USER_NS_FSID
From: Christian Brauner <hidden> Date: 2020-02-11 17:01:26
If fsid mappings have been written, this will cause proc to look at fsid
mappings for the user namespace. If no fsid mappings have been written the
behavior is as before.
Here is part of the output from /proc/<pid>/status from the initial user
namespace for systemd running in an unprivileged container as user namespace
root with id mapping 0 100000 100000 and fsid mapping 0 300000 100000:
Name: systemd
Umask: 0000
State: S (sleeping)
Tgid: 13023
Ngid: 0
Pid: 13023
PPid: 13008
TracerPid: 0
Uid: 100000 100000 100000 300000
Gid: 100000 100000 100000 300000
FDSize: 64
Groups:
Signed-off-by: Christian Brauner <redacted>
---
fs/proc/array.c | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
From: Christian Brauner <hidden> Date: 2020-02-11 17:02:27
Switch setresgid() to lookup fsids in the fsid mappings. If no fsid mappings are
setup the behavior is unchanged, i.e. fsids are looked up in the id mappings.
During setresgid() the kfsgid is set to the kegid corresponding the egid that is
requested by userspace. If the requested egid is -1 the kfsgid is reset to the
current kegid. For the latter case this means we need to lookup the
corresponding userspace egid corresponding to the current kegid in the id
mappings and translate this egid into the corresponding kfsgid in the fsid
mappings.
Signed-off-by: Christian Brauner <redacted>
---
kernel/sys.c | 13 ++++++++++---
1 file changed, 10 insertions(+), 3 deletions(-)
From: Christian Brauner <hidden> Date: 2020-02-11 17:02:52
Switch setregid() to lookup fsids in the fsid mappings. If no fsid mappings are
setup the behavior is unchanged, i.e. fsids are looked up in the id mappings.
During setregid() the kfsgid is set to the kegid corresponding the egid that is
requested by userspace. If the requested egid is -1 the kfsgid is reset to the
current kegid. For the latter case this means we need to lookup the
corresponding userspace egid corresponding to the current kegid in the id
mappings and translate this egid into the corresponding kfsgid in the fsid
mappings.
Signed-off-by: Christian Brauner <redacted>
---
kernel/sys.c | 10 ++++++++--
1 file changed, 8 insertions(+), 2 deletions(-)
On Tue, Feb 11, 2020 at 5:59 PM Christian Brauner
[off-list ref] wrote:
This is the implementation of shiftfs which was cooked up during lunch at
Linux Plumbers 2019 the day after the container's microconference. The
idea is a design-stew from Stéphane, Aleksa, Eric, and myself. Back then
we all were quite busy with other work and couldn't really sit down and
implement it. But I took a few days last week to do this work, including
demos and performance testing.
This implementation does not require us to touch the vfs substantially
at all. Instead, we implement shiftfs via fsid mappings.
With this patch, it took me 20 mins to port both LXD and LXC to support
shiftfs via fsid mappings.
For anyone wanting to play with this the branch can be pulled from:
https://github.com/brauner/linux/tree/fsid_mappingshttps://gitlab.com/brauner/linux/-/tree/fsid_mappingshttps://git.kernel.org/pub/scm/linux/kernel/git/brauner/linux.git/log/?h=fsid_mappings
The main use case for shiftfs for us is in allowing shared writable
storage to multiple containers using non-overlapping id mappings.
In such a scenario you want the fsids to be valid and identical in both
containers for the shared mount. A demo for this exists in [3].
If you don't want to read on, go straight to the other demos below in
[1] and [2].
I guess essentially this means that you want to have UID separation
between containers to prevent the containers - or their owners - from
interfering between each other, but for filesystem access, you don't
want to isolate them from each other using DAC controls on the files
and folders inside the containers' directory hierarchies, instead
relying on mode-0700 parent directories to restrict access to the
container owner? Or would you still have separate UIDs for e.g. the
container's UID range 0-65535, and then map the shared UID range at
100000, or something like that?
People not as familiar with user namespaces might not be aware that fsid
mappings already exist. Right now, fsid mappings are always identical to
id mappings. Specifically, the kernel will lookup fsuids in the uid
mappings and fsgids in the gid mappings of the relevant user namespace.
That's a bit like saying that a kernel without CONFIG_USER_NS still
has user ID mappings, they just happen to be identity mappings. :P
With this patch series we simply introduce the ability to create fsid
mappings that are different from the id mappings of a user namespace.
In the usual case of running an unprivileged container we will have
setup an id mapping, e.g. 0 100000 100000. The on-disk mapping will
correspond to this id mapping, i.e. all files which we want to appear as
0:0 inside the user namespace will be chowned to 100000:100000 on the
host. This works, because whenever the kernel needs to do a filesystem
access it will lookup the corresponding uid and gid in the idmapping
tables of the container.
Now think about the case where we want to have an id mapping of 0 100000
100000 but an on-disk mapping of 0 300000 100000 which is needed to e.g.
share a single on-disk mapping with multiple containers that all have
different id mappings.
This will be problematic. Whenever a filesystem access is requested, the
kernel will now try to lookup a mapping for 300000 in the id mapping
tables of the user namespace but since there is none the files will
appear to be owned by the overflow id, i.e. usually 65534:65534 or
nobody:nogroup.
With fsid mappings we can solve this by writing an id mapping of 0
100000 100000 and an fsid mapping of 0 300000 100000. On filesystem
access the kernel will now lookup the mapping for 300000 in the fsid
mapping tables of the user namespace. And since such a mapping exists,
the corresponding files will have correct ownership.
Sorry to bring up something as disgusting as setuid execution, but:
What happens when there's a setuid root file with ->i_uid==300000? I
guess the only way to make that work inside the containers would be
something like make_kuid(current_user_ns(),
from_kfsuid(current_user_ns(), inode->i_uid)) in the setuid execve
path?
A note on proc (and sys), the proc filesystem is special in sofar as it
only has a single superblock that is (currently but might be about to
change) visible in all user namespaces (same goes for sys). This means
it has special semantics in many ways, including how file ownership and
access works. The fsid mapping implementation does not alter how proc
(and sys) ownership works. proc and sys will both continue to lookup
filesystem access in id mapping tables.
In your example, a process with namespaced UID set (0, 0, 0, 0) will
have kernel UIDs (100000, 100000, 100000, 300000), right? And then if
I want to open /proc/$pid/personality of another process with the same
UIDs, may_open() will call inode_permission() -> do_inode_permission()
-> generic_permission() -> acl_permission_check(), which will compare
current_fsuid() (which is 300000) against inode->i_uid. But
inode->i_uid was filled by proc_pid_make_inode()->task_dump_owner(),
which set inode->i_uid to 100000, right?
Also, e.g. __ptrace_may_access() uses cred->fsuid for a comparison
with another task's real/effective/saved UID.
[...]
(I really dislike this asciinema thing; if you want to quickly glance
through the output instead of reading at the same speed as it was
typed, a simple pastebin works much better unless you absolutely have
to show things that use stuff like ncurses UI.)
From: Christian Brauner <hidden> Date: 2020-02-12 14:51:58
On Tue, Feb 11, 2020 at 09:55:46PM +0100, Jann Horn via Containers wrote:
On Tue, Feb 11, 2020 at 5:59 PM Christian Brauner
[off-list ref] wrote:
quoted
This is the implementation of shiftfs which was cooked up during lunch at
Linux Plumbers 2019 the day after the container's microconference. The
idea is a design-stew from Stéphane, Aleksa, Eric, and myself. Back then
we all were quite busy with other work and couldn't really sit down and
implement it. But I took a few days last week to do this work, including
demos and performance testing.
This implementation does not require us to touch the vfs substantially
at all. Instead, we implement shiftfs via fsid mappings.
With this patch, it took me 20 mins to port both LXD and LXC to support
shiftfs via fsid mappings.
For anyone wanting to play with this the branch can be pulled from:
https://github.com/brauner/linux/tree/fsid_mappingshttps://gitlab.com/brauner/linux/-/tree/fsid_mappingshttps://git.kernel.org/pub/scm/linux/kernel/git/brauner/linux.git/log/?h=fsid_mappings
The main use case for shiftfs for us is in allowing shared writable
storage to multiple containers using non-overlapping id mappings.
In such a scenario you want the fsids to be valid and identical in both
containers for the shared mount. A demo for this exists in [3].
If you don't want to read on, go straight to the other demos below in
[1] and [2].
I guess essentially this means that you want to have UID separation
between containers to prevent the containers - or their owners - from
interfering between each other, but for filesystem access, you don't
want to isolate them from each other using DAC controls on the files
and folders inside the containers' directory hierarchies, instead
relying on mode-0700 parent directories to restrict access to the
container owner? Or would you still have separate UIDs for e.g. the
container's UID range 0-65535, and then map the shared UID range at
100000, or something like that?
Yes.
So if you look at the permissions right now for the directory under
which the rootfs for the container and other stuff resides we have
root@wittgenstein|/var/lib/lxd/storage-pools/zfs/containers
perms *
d--x------ 100 alp1
d--x------ 100 f1
d--x------ 100 f2
We don't really share the rootfs between containers right now since we
treat them as standalone systems but with fsid mappings that's possible
too. Layer-sharing-centric runtimes very much will want something like
that.
quoted
People not as familiar with user namespaces might not be aware that fsid
mappings already exist. Right now, fsid mappings are always identical to
id mappings. Specifically, the kernel will lookup fsuids in the uid
mappings and fsgids in the gid mappings of the relevant user namespace.
That's a bit like saying that a kernel without CONFIG_USER_NS still
has user ID mappings, they just happen to be identity mappings. :P
If you have CONFIG_USER_NS=n then you have (as you're well aware)
[<0, 0>, <1,1>, ..., <n,n>] so yeah that's true and analyzing it like
that makes sense. :P
quoted
With this patch series we simply introduce the ability to create fsid
mappings that are different from the id mappings of a user namespace.
In the usual case of running an unprivileged container we will have
setup an id mapping, e.g. 0 100000 100000. The on-disk mapping will
correspond to this id mapping, i.e. all files which we want to appear as
0:0 inside the user namespace will be chowned to 100000:100000 on the
host. This works, because whenever the kernel needs to do a filesystem
access it will lookup the corresponding uid and gid in the idmapping
tables of the container.
Now think about the case where we want to have an id mapping of 0 100000
100000 but an on-disk mapping of 0 300000 100000 which is needed to e.g.
share a single on-disk mapping with multiple containers that all have
different id mappings.
This will be problematic. Whenever a filesystem access is requested, the
kernel will now try to lookup a mapping for 300000 in the id mapping
tables of the user namespace but since there is none the files will
appear to be owned by the overflow id, i.e. usually 65534:65534 or
nobody:nogroup.
With fsid mappings we can solve this by writing an id mapping of 0
100000 100000 and an fsid mapping of 0 300000 100000. On filesystem
access the kernel will now lookup the mapping for 300000 in the fsid
mapping tables of the user namespace. And since such a mapping exists,
the corresponding files will have correct ownership.
Sorry to bring up something as disgusting as setuid execution, but:
No that's exactly what this needs. :)
What happens when there's a setuid root file with ->i_uid==300000? I
guess the only way to make that work inside the containers would be
something like make_kuid(current_user_ns(),
from_kfsuid(current_user_ns(), inode->i_uid)) in the setuid execve
path?
What's the specific callpath you're thinking about?
So if you look at patch
https://lore.kernel.org/lkml/20200211165753.356508-16-christian.brauner@ubuntu.com/
it does
- new->suid = new->fsuid = new->euid;
- new->sgid = new->fsgid = new->egid;
+ fsuid = from_kuid_munged(new->user_ns, new->euid);
+ kfsuid = make_kfsuid(new->user_ns, fsuid);
+ new->suid = new->euid;
+ new->fsuid = kfsuid;
+
+ fsgid = from_kgid_munged(new->user_ns, new->egid);
+ kfsgid = make_kfsgid(new->user_ns, fsgid);
+ new->sgid = new->egid;
+ new->fsgid = kfsgid;
One thing I definitely missed though in the setuid path is to adapt
fs/exec.c:bprm_fill_uid():
@@ -1547,8 +1547,8 @@ static void bprm_fill_uid(struct linux_binprm *bprm)inode_unlock(inode);/* We ignore suid/sgid if there are no mappings for them in the ns */-if(!kuid_has_mapping(bprm->cred->user_ns,uid)||-!kgid_has_mapping(bprm->cred->user_ns,gid))+if(!kfsuid_has_mapping(bprm->cred->user_ns,uid)||+!kfsgid_has_mapping(bprm->cred->user_ns,gid))return;if(mode&S_ISUID){
quoted
A note on proc (and sys), the proc filesystem is special in sofar as it
only has a single superblock that is (currently but might be about to
change) visible in all user namespaces (same goes for sys). This means
it has special semantics in many ways, including how file ownership and
access works. The fsid mapping implementation does not alter how proc
(and sys) ownership works. proc and sys will both continue to lookup
filesystem access in id mapping tables.
In your example, a process with namespaced UID set (0, 0, 0, 0) will
have kernel UIDs (100000, 100000, 100000, 300000), right? And then if
Yes.
I want to open /proc/$pid/personality of another process with the same
UIDs, may_open() will call inode_permission() -> do_inode_permission()
-> generic_permission() -> acl_permission_check(), which will compare
current_fsuid() (which is 300000) against inode->i_uid. But
inode->i_uid was filled by proc_pid_make_inode()->task_dump_owner(),
which set inode->i_uid to 100000, right?
Yes. That should be fixable by something like below, I think. (And we can
probably shortcut this by adding a helper that does tell us whether there's
been any fsid mapping setup or not for this user namespace.)
static int acl_permission_check(struct inode *inode, int mask)
{
+ kuid_t kuid;
unsigned int mode = inode->i_mode;
- if (likely(uid_eq(current_fsuid(), inode->i_uid)))
+ if (!is_userns_visible(inode->i_sb->s_iflags)) {
+ kuid = inode->i_uid;
+ } else {
+ kuid = make_kuid(current_user_ns(),
+ from_kfsuid(current_user_ns(), inode->i_uid));
+ }
+
+ if (likely(uid_eq(current_fsuid(), kuid)))
mode >>= 6;
else {&& (mode & S_IRWXG)) {
Also, e.g. __ptrace_may_access() uses cred->fsuid for a comparison
with another task's real/effective/saved UID.
Right, you even introduced this check in 2015 iirc.
Both of your points make me think that it'd be easiest to introduce
cred->{kfsuid,kfsgid} and whenever an access decision on a
is_userns_visible() filesystem has to be made those will be used. This avoids
having to do on-the fly translations and ptrace_may_access() can just grow a
flag indicating what fscreds it's supposed to look at?
(I really dislike this asciinema thing; if you want to quickly glance
through the output instead of reading at the same speed as it was
typed, a simple pastebin works much better unless you absolutely have
to show things that use stuff like ncurses UI.)
On Wed, Feb 12, 2020 at 3:51 PM Christian Brauner
[off-list ref] wrote:
On Tue, Feb 11, 2020 at 09:55:46PM +0100, Jann Horn via Containers wrote:
quoted
On Tue, Feb 11, 2020 at 5:59 PM Christian Brauner
[off-list ref] wrote:
quoted
This is the implementation of shiftfs which was cooked up during lunch at
Linux Plumbers 2019 the day after the container's microconference. The
idea is a design-stew from Stéphane, Aleksa, Eric, and myself. Back then
we all were quite busy with other work and couldn't really sit down and
implement it. But I took a few days last week to do this work, including
demos and performance testing.
This implementation does not require us to touch the vfs substantially
at all. Instead, we implement shiftfs via fsid mappings.
With this patch, it took me 20 mins to port both LXD and LXC to support
shiftfs via fsid mappings.
For anyone wanting to play with this the branch can be pulled from:
https://github.com/brauner/linux/tree/fsid_mappingshttps://gitlab.com/brauner/linux/-/tree/fsid_mappingshttps://git.kernel.org/pub/scm/linux/kernel/git/brauner/linux.git/log/?h=fsid_mappings
The main use case for shiftfs for us is in allowing shared writable
storage to multiple containers using non-overlapping id mappings.
In such a scenario you want the fsids to be valid and identical in both
containers for the shared mount. A demo for this exists in [3].
If you don't want to read on, go straight to the other demos below in
[1] and [2].
I guess essentially this means that you want to have UID separation
between containers to prevent the containers - or their owners - from
interfering between each other, but for filesystem access, you don't
want to isolate them from each other using DAC controls on the files
and folders inside the containers' directory hierarchies, instead
relying on mode-0700 parent directories to restrict access to the
container owner? Or would you still have separate UIDs for e.g. the
container's UID range 0-65535, and then map the shared UID range at
100000, or something like that?
Yes.
So if you look at the permissions right now for the directory under
which the rootfs for the container and other stuff resides we have
root@wittgenstein|/var/lib/lxd/storage-pools/zfs/containers
quoted
perms *
d--x------ 100 alp1
d--x------ 100 f1
d--x------ 100 f2
We don't really share the rootfs between containers right now since we
treat them as standalone systems but with fsid mappings that's possible
too. Layer-sharing-centric runtimes very much will want something like
that.
[...]
quoted
quoted
With this patch series we simply introduce the ability to create fsid
mappings that are different from the id mappings of a user namespace.
In the usual case of running an unprivileged container we will have
setup an id mapping, e.g. 0 100000 100000. The on-disk mapping will
correspond to this id mapping, i.e. all files which we want to appear as
0:0 inside the user namespace will be chowned to 100000:100000 on the
host. This works, because whenever the kernel needs to do a filesystem
access it will lookup the corresponding uid and gid in the idmapping
tables of the container.
Now think about the case where we want to have an id mapping of 0 100000
100000 but an on-disk mapping of 0 300000 100000 which is needed to e.g.
share a single on-disk mapping with multiple containers that all have
different id mappings.
This will be problematic. Whenever a filesystem access is requested, the
kernel will now try to lookup a mapping for 300000 in the id mapping
tables of the user namespace but since there is none the files will
appear to be owned by the overflow id, i.e. usually 65534:65534 or
nobody:nogroup.
With fsid mappings we can solve this by writing an id mapping of 0
100000 100000 and an fsid mapping of 0 300000 100000. On filesystem
access the kernel will now lookup the mapping for 300000 in the fsid
mapping tables of the user namespace. And since such a mapping exists,
the corresponding files will have correct ownership.
Sorry to bring up something as disgusting as setuid execution, but:
No that's exactly what this needs. :)
quoted
What happens when there's a setuid root file with ->i_uid==300000? I
guess the only way to make that work inside the containers would be
something like make_kuid(current_user_ns(),
from_kfsuid(current_user_ns(), inode->i_uid)) in the setuid execve
path?
What's the specific callpath you're thinking about?
So if you look at patch
https://lore.kernel.org/lkml/20200211165753.356508-16-christian.brauner@ubuntu.com/
it does
- new->suid = new->fsuid = new->euid;
- new->sgid = new->fsgid = new->egid;
+ fsuid = from_kuid_munged(new->user_ns, new->euid);
+ kfsuid = make_kfsuid(new->user_ns, fsuid);
+ new->suid = new->euid;
+ new->fsuid = kfsuid;
+
+ fsgid = from_kgid_munged(new->user_ns, new->egid);
+ kfsgid = make_kfsgid(new->user_ns, fsgid);
+ new->sgid = new->egid;
+ new->fsgid = kfsgid;
Aaah, okay, I missed that.
quoted hunk
One thing I definitely missed though in the setuid path is to adapt
fs/exec.c:bprm_fill_uid():
@@ -1547,8 +1547,8 @@ static void bprm_fill_uid(struct linux_binprm *bprm)inode_unlock(inode);/* We ignore suid/sgid if there are no mappings for them in the ns */-if(!kuid_has_mapping(bprm->cred->user_ns,uid)||-!kgid_has_mapping(bprm->cred->user_ns,gid))+if(!kfsuid_has_mapping(bprm->cred->user_ns,uid)||+!kfsgid_has_mapping(bprm->cred->user_ns,gid))return;if(mode&S_ISUID){
[...]
quoted
I want to open /proc/$pid/personality of another process with the same
UIDs, may_open() will call inode_permission() -> do_inode_permission()
-> generic_permission() -> acl_permission_check(), which will compare
current_fsuid() (which is 300000) against inode->i_uid. But
inode->i_uid was filled by proc_pid_make_inode()->task_dump_owner(),
which set inode->i_uid to 100000, right?
Yes. That should be fixable by something like below, I think. (And we can
probably shortcut this by adding a helper that does tell us whether there's
been any fsid mapping setup or not for this user namespace.)
static int acl_permission_check(struct inode *inode, int mask)
{
+ kuid_t kuid;
unsigned int mode = inode->i_mode;
- if (likely(uid_eq(current_fsuid(), inode->i_uid)))
+ if (!is_userns_visible(inode->i_sb->s_iflags)) {
+ kuid = inode->i_uid;
+ } else {
+ kuid = make_kuid(current_user_ns(),
+ from_kfsuid(current_user_ns(), inode->i_uid));
+ }
+
+ if (likely(uid_eq(current_fsuid(), kuid)))
mode >>= 6;
else {&& (mode & S_IRWXG)) {
quoted
Also, e.g. __ptrace_may_access() uses cred->fsuid for a comparison
with another task's real/effective/saved UID.
Right, you even introduced this check in 2015 iirc.
Both of your points make me think that it'd be easiest to introduce
cred->{kfsuid,kfsgid} and whenever an access decision on a
is_userns_visible() filesystem has to be made those will be used. This avoids
having to do on-the fly translations
I guess that might be less ugly.
and ptrace_may_access() can just grow a
flag indicating what fscreds it's supposed to look at?
Wouldn't you always end up using the "real" fsuid there?