I (finally) tested it in Fedora Rawhide. It looks like my initial impression is correct: if you run seteuid(50) inside a namespace, but uid 50 inside the namespace doesn't correspond to a uid outside the namespace, then the call to seteuid simply fails.
In here, I'll use "kernel uid" to refer to a process's uid as seen by the kernel (and most processes outside of the user namespace), and "subjective uid" to refer to a process's uid as seen by itself (and other processes inside the namespace).
Test 1
First, let's see what happens when root calls seteuid(50). We open up Python, and see what its pid is:
[fedora@ip-0-0-0-0 ~]$ sudo python Python 2.7.6 (default, Feb 4 2014, 15:36:52) [GCC 4.8.2 20140120 (Red Hat 4.8.2-14)] on linux2 Type "help", "copyright", "credits" or "license" for more information. >>> import os >>> os.getpid() 1081
In a second terminal, we can see that python's kernel uid is, in fact, 0 (root):
[fedora@ip-0-0-0-0 ~]$ ls -ld /proc/1081 dr-xr-xr-x. 9 root root 0 Feb 7 03:27 /proc/1081
And the subjective uid is, of course, 0 as well:
>>> os.geteuid() 0
So now we change our uid to 50:
>>> os.seteuid(50)
In the second terminal, we see that the kernel uid has indeed changed to 50:
[fedora@ip-0-0-0-0 ~]$ ls -ld /proc/1081 dr-xr-xr-x. 9 50 root 0 Feb 7 03:27 /proc/1081
Test 2
Okay, now let's use "unshare" to create a new user namespace.
[fedora@ip-0-0-0-0 ~]$ unshare --user python Python 2.7.6 (default, Feb 4 2014, 15:36:52) [GCC 4.8.2 20140120 (Red Hat 4.8.2-14)] on linux2 Type "help", "copyright", "credits" or "license" for more information. >>> import os >>> os.getpid() 1084
In the second terminal, we see that this process's kernel uid is 1000 (fedora):
[fedora@ip-0-0-0-0 ~]$ ls -ld /proc/1084 dr-xr-xr-x. 9 fedora fedora 0 Feb 7 03:30 /proc/1084
But in the first terminal, we see that its subjective uid is 65534 (not mapped):
>>> os.geteuid() 65534
So, in the second terminal, we give python a mapping:
[fedora@ip-0-0-0-0 ~]$ echo '0 1000 1' > /proc/1084/uid_map
In the first terminal, we see that python's subjective uid is now 0:
>>> os.geteuid() 0
We try to set python's uid to 50, but this fails with "Invalid argument" (presumably EINVAL), because the subjective uid 50 has no mapping to a kernel uid:
>>> os.seteuid(50) Traceback (most recent call last): File "<stdin>", line 1, in <module> OSError: [Errno 22] Invalid argument
In the second terminal, we see that the process's kernel uid is unchanged:
[fedora@ip-0-0-0-0 ~]$ ls -ld /proc/1084 dr-xr-xr-x. 9 fedora fedora 0 Feb 7 03:30 /proc/1084
And in the first terminal, we see that the process's subjective uid is unchanged, too:
>>> os.geteuid() 0
Test 3
Okay, what happens if we define a mapping containing multiple uids? According to the LWN.net article "Namespaces in operation, part 5: User namespaces", we need to run python after mapping the uids. (I tried this without mapping the uids before running python, and the seteuid operation gave "Operation not permitted".) So first, we run a shell in a second new user namespace:
[fedora@ip-0-0-0-0 ~]$ unshare --user id: cannot find name for user ID 65534 id: cannot find name for group ID 65534 id: cannot find name for user ID 65534 [I have no name!@ip-10-239-133-144 ~]$ echo $$ 1798
Then, in the second terminal, we need to be root in order to define the mapping:
[root@ip-10-239-133-144 fedora]# echo '0 1000 2' > /proc/1798/uid_map
According to the whoami command, our subjective uid is root now, so let's open up Python and set our subjective uid to 1:
[I have no name!@ip-0-0-0-0 ~]$ whoami root [I have no name!@ip-0-0-0-0 ~]$ python Python 2.7.6 (default, Feb 4 2014, 15:36:52) [GCC 4.8.2 20140120 (Red Hat 4.8.2-14)] on linux2 Type "help", "copyright", "credits" or "license" for more information. >>> import os >>> os.seteuid(1) >>> os.getpid() 1837
In the second terminal, we see that our kernel uid is now 1001, as expected:
[root@ip-10-239-133-144 fedora]# ls -ld /proc/1837 dr-xr-xr-x. 9 1001 fedora 0 Feb 7 04:00 /proc/1837
Test 2 again?
In test 3, we needed to define the mapping before running Python in order for the call to seteuid() to succeed. In test 2, we didn't do this. So, the obvious question is, if we did define the mapping before running Python, would the call to seteuid() work? It turns out that the answer is no. First terminal:
[fedora@ip-0-0-0-0 ~]$ unshare --user id: cannot find name for user ID 65534 id: cannot find name for group ID 65534 id: cannot find name for user ID 65534 [I have no name!@ip-0-0-0-0 ~]$ echo $$ 1861
Second terminal:
[fedora@ip-0-0-0-0 ~]$ echo '0 1000 1' > /proc/1861/uid_map
First terminal:
[I have no name!@ip-0-0-0-0 ~]$ whoami root [I have no name!@ip-0-0-0-0 ~]$ python Python 2.7.6 (default, Feb 4 2014, 15:36:52) [GCC 4.8.2 20140120 (Red Hat 4.8.2-14)] on linux2 Type "help", "copyright", "credits" or "license" for more information. >>> import os >>> os.geteuid() 0 >>> os.seteuid(50) Traceback (most recent call last): File "<stdin>", line 1, in <module> OSError: [Errno 22] Invalid argument
Conclusion
In order for a call to seteuid within a user namespace to succeed, the uid passed to seteuid must have a mapping outside the user namespace. Otherwise, the call will fail with EINVAL.