4

Suppose I have user "myuser" with uid/gid "1001", and files "/etc/subuid" & "/etc/subgid" with the following content:

myuser:100000:65536

Can this setup somehow be used to allow user "myuser" to access files with owner "100000"?

A.B
  • 31,762
  • 2
  • 62
  • 101
Thunderbeef
  • 257
  • 1
  • 8

1 Answers1

4

Yes with the help of the setuid root utilities newuidmap and newgidmap working along with /etc/subuid and /etc/subgid.

Tools like podman, docker, or LXC all require these setuid root utilities installed to work in rootless/unprivileged mode with a normal user. Without these tools there are not enough rights available to bootstrap this.

All the lengthy explanations are in the user namespaces manpage that should be read to understand how the features can be used. In short, once the user namespace is created (with an unprivileged operation), the specific pseudo-files /proc/<PID>/{uid,gid}_map can be written once and only once to set the mappings. With correct (host) privileges, spare uid/gid ranges, as allowed in /etc/sub{uid,gid}, can then be mapped in the user namespace.

The user myuser itself (without the assistance of any special command) can write to those pseudo-files, but:

  • is allowed only to have itself (host uid 1001) mapped into one uid in the user namespace (the obvious possibly useful choices being itself again or 0 for root). This has to be done anyway from an other process, since the new process is temporarily without mapping in the newly created user namespace. As any non-mapped uid gets translated to the overflow uid (by default 65534 aka nobody) until "resolved", it thus can't write to its own /proc/<PID>/uid_map file. Even if it becomes root in this namespace, as it can affect only root which is host uid 1001, this is useless. System calls (and commands using them) are all made to not allow rights escalation. In addition to failing with EPERM etc. they can also fail with EINVAL when trying to alter a mapped uid (ie: host uid 1001) into a non-mapped uid.

  • same for groups, with the additional restriction that it must first forfeit the ability to use setgroups before being able to write in /proc/<PID>/gid_map (that's to avoid being able to remove oneself from a group to evade restrictions to files added by being part of this group).

So uid 100000 would of course not be reachable: setuid root tools are required.

The goal here is to map the normal user myuser with uid 1001 (allowed in mappings by newuidmap in addition to the subordinate ids) as the root user (uid 0), as well as also map at least the host uid 100000 inside the user namespace: this allows to change one to the other by (the user namespace's) root when needed. Here I'll just map just the additional 100000 to ... 100000 again. I'll do the same with gid, assuming /etc/subgid is a mirror of /etc/subuid.

Here's a practical example on how to do, using newuidmap and newgidmap, all from initial host user myuser. As explained above, this requires two processes, so here two shells in two terminals. The actions of these setuid root commands could be replaced with correctly crafted echo (or printf etc.) commands run from the (host) root user.

term1:

$ unshare --user sh
$ id -u; id -un ; id -g; id -gn; echo pid:$$
65534
nobody
65534
nogroup
pid:13273

term2 (reusing previous pid value in the commands):

$ newuidmap 13273 0 1001 1 100000 100000 1
$ newgidmap 13273 0 1001 1 100000 100000 1

term1 again (exec sh just to have the shell display the "resolved" and "upgraded" status):

$ id -u; id -un ; id -g; id -gn
0
root
0
root
$ exec sh
#

# touch mytest
# chown 100000:100000 mytest

term2 (which here displays the same as in term1, since the mapping was chosen with the same values):

$ ls -l mytest
-rw-r--r--. 1 100000 100000 0 Apr 21 18:00 mytest
$ touch mytest
touch: cannot touch 'mytest': Permission denied

term1:

# chown 100001:100001 mytest
chown: changing ownership of 'mytest': Invalid argument
# chown root:root mytest

term2:

$ ls -l mytest
-rw-r--r--. 1 myuser myuser 0 Apr 21 18:00 mytest

Any sub process from term1 will now have control over uid 100000, including filesystem operations.

Bigger ranges or smarter mappings could be used, as allowed in /etc/subuid to affect more things or things more easily (eg: mapping 100000 to myuser instead, with newuidmap 13273 0 1001 1 1001 100000 1 would have allowed to simply run su myuser in term1, which should succeed despite a few non-fatal errors). This would also allow for example to access/rescue/backup (at the filesystem level only, not other features like network) an user LXC instance which would refuse to start due to some unrelated issue.

A.B
  • 31,762
  • 2
  • 62
  • 101