Eu (finalmente) testei no Fedora Rawhide. Parece que minha impressão inicial está correta: se você executar seteuid(50)
dentro de um namespace, mas o uid 50 dentro do namespace não corresponder a um uid fora do namespace, a chamada para seteuid simplesmente falhará.
Aqui, usarei "kernel uid" para se referir ao uid de um processo como visto pelo kernel (e a maioria dos processos fora do namespace do usuário) e "uid subjetivo" para se referir ao uid de um processo como visto por próprio (e outros processos dentro do namespace).
Teste 1
Primeiro, vamos ver o que acontece quando as chamadas raiz seteuid(50)
. Nós abrimos o Python e vemos qual é o seu pid:
[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
Em um segundo terminal, podemos ver que o kernel do python é, na verdade, 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
E o uid subjetivo é, claro, 0 também:
>>> os.geteuid()
0
Então agora mudamos nosso uid para 50:
>>> os.seteuid(50)
No segundo terminal, vemos que o uid do kernel realmente mudou para 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
Teste 2
Ok, agora vamos usar "unshare" para criar um novo namespace de usuário.
[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
No segundo terminal, vemos que o kernel do processo é 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
Mas no primeiro terminal, vemos que o seu uid subjetivo é 65534 (não mapeado):
>>> os.geteuid()
65534
Então, no segundo terminal, damos um mapeamento ao python:
[fedora@ip-0-0-0-0 ~]$ echo '0 1000 1' > /proc/1084/uid_map
No primeiro terminal, vemos que o uid subjetivo do python agora é 0:
>>> os.geteuid()
0
Tentamos definir o uid do python para 50, mas isso falha com "Argumento inválido" (presumivelmente EINVAL), porque o uid subjetivo 50 não tem mapeamento para um uid do kernel:
>>> os.seteuid(50)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
OSError: [Errno 22] Invalid argument
No segundo terminal, vemos que o kernel do processo está inalterado:
[fedora@ip-0-0-0-0 ~]$ ls -ld /proc/1084
dr-xr-xr-x. 9 fedora fedora 0 Feb 7 03:30 /proc/1084
E no primeiro terminal, vemos que o fluxo subjetivo do processo também está inalterado:
>>> os.geteuid()
0
Teste 3
Ok, o que acontece se definirmos um mapeamento contendo vários uids? De acordo com o artigo do LWN.net " Namespaces in operation, parte 5: User namespace ", precisamos executar o python depois de mapear os uids. (Eu tentei isso sem mapear os uids antes de executar python, e a operação seteuid deu "Operation not allowed".) Então, primeiro, nós rodamos um shell em um segundo namespace de usuário:
[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
Então, no segundo terminal, precisamos ser o root para definir o mapeamento:
[root@ip-10-239-133-144 fedora]# echo '0 1000 2' > /proc/1798/uid_map
De acordo com o comando whoami
, nosso uid subjetivo é root agora, então vamos abrir o Python e definir nosso uid subjetivo para 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
No segundo terminal, vemos que o nosso kernel uid agora é 1001, como esperado:
[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
Teste 2 novamente?
No teste 3, precisávamos definir o mapeamento antes de executar o Python para que a chamada para seteuid () fosse bem-sucedida. No teste 2, não fizemos isso. Então, a pergunta óbvia é, se nós definimos o mapeamento antes de executar o Python, a chamada para seteuid () funcionaria? Acontece que a resposta é não. Primeiro 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
Segundo terminal:
[fedora@ip-0-0-0-0 ~]$ echo '0 1000 1' > /proc/1861/uid_map
Primeiro 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
Conclusão
Para que uma chamada para seteuid dentro de um namespace de usuário seja bem-sucedida, o uid passado para seteuid deve ter um mapeamento fora do namespace de usuário. Caso contrário, a chamada falhará com o EINVAL.