Em um namespace de UID, posso setuid para um UID não mapeado?

5

Suponha que eu seja o ID do usuário 1000. Eu executo um programa dentro de um namespace UID, mapeando o UID 0 dentro do namespace para o UID 1000 fora do namespace. Em seguida, o programa tenta alterar seu próprio UID para 50, que não tem um UID correspondente fora do namespace.

O programa modifica com sucesso seu próprio UID (kernel)? Em caso afirmativo, o que o seu UID parece ser de fora do namespace? Ainda é 1000, ou é outra coisa?

    
por Tanner Swett 21.01.2014 / 06:20

2 respostas

6

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.

    
por 07.02.2014 / 05:17
2

Você precisa definir as traduções depois de ter bifurcado.

Um trecho de um ótimo artigo no LWN.net: "Chamadas de sistema que retornam IDs de usuário e grupo do processo - por exemplo, getuid() e getgid() - sempre retornam as credenciais conforme aparecem dentro do namespace do usuário no qual a chamada Se um ID do usuário não tiver mapeamento dentro do namespace, as chamadas do sistema que retornam os IDs do usuário retornarão o valor definido no arquivo /proc/sys/kernel/overflowuid , que em um sistema padrão assume o valor 65534. Inicialmente, um namespace de usuário não possui mapeamento de ID de usuário, para que todos os IDs de usuário dentro do namespace sejam mapeados para esse valor .De igual modo, um novo namespace de usuário não possui mapeamentos para IDs de grupo e todos os IDs de grupo não mapeados são mapeados para /proc/sys/kernel/overflowgid (que possui o mesmo padrão que overflowuid ) "

Basicamente, uma vez que você tenha clone(2) 'de sua prole, você terá o PID da criança e poderá adicionar às tabelas em /proc/CHILDPID/uid-map e /proc/CHILDPID/gid-map para começar a definir as traduções.

Como eu entendo a situação, se você executar seteuid(50) dentro de seu novo namespace sem uma tradução para ele, você verá que seu euid ainda é 65534 até que a tradução seja definida. Como acima, se não houver tradução correspondente, overflowuid / overflowgid será substituído.

O artigo do LWN.net que explica a maior parte da situação de forma brilhante está aqui: link

    
por 21.01.2014 / 13:30