Estou tentando entender como os recursos do Linux são passados para um processo que foi feito exec()
por outro. Pelo que li, para que um recurso seja mantido após exec, ele deve estar no conjunto herdável. O que não tenho certeza, porém, é como esse conjunto é preenchido.
Meu objetivo é ser capaz de executar um programa como um usuário normal que normalmente exigiria root. A capacidade necessária é cap_dac_override
para poder ler um arquivo privado. Não quero dar-lhe quaisquer outras capacidades.
Aqui está meu invólucro:
#include <unistd.h>
int main(int argc, char *argv[]) {
return execl("/usr/bin/net", "net", "ads", "dns", "register", "-P", NULL);
}
Isso funciona quando eu defino a permissão setuid no executável resultante:
~ $ sudo chown root: ./registerdns
~ $ sudo chmod u+s ./registerdns
~ $ ./registerdns
Successfully registered hostname with DNS
Eu gostaria de usar capacidades em vez de setuid. Tentei definir a cap_dac_override
capacidade no wrapper:
~ $ sudo setcap cap_dac_override=eip ./registerdns
~ $ ./registerdns
Failed to open /var/lib/samba/private/secrets.tdb
ERROR: Unable to open secrets database
Também tentei definir o sinalizador herdável na cap_dac_override
capacidade do net
próprio executável:
~ $ sudo setcap cap_dac_override=eip ./registerdns
~ $ sudo setcap cap_dac_override=i /usr/bin/net
~ $ ./registerdns
Failed to open /var/lib/samba/private/secrets.tdb
ERROR: Unable to open secrets database
Preciso usar o wrapper para garantir que o recurso esteja disponível apenas ao usar esse conjunto exato de argumentos; o net
programa faz várias outras coisas que podem ser perigosas para conceder aos usuários permissões muito amplas.
Obviamente estou entendendo mal como funciona a herança. Não consigo descobrir como configurar o wrapper para transmitir seus recursos ao processo de substituição para que ele possa usá-los. Eu li a página de manual e inúmeros outros documentos sobre como issodevetrabalho, e pensei que estava fazendo o que descreve.
Responder1
Acontece que definir +i no wrapper faznãoadicione a capacidade ao CAP_INHERITABLE
conjunto para o processo wrapper, portanto, ela não será transmitida exec
. Portanto, tive que adicionar manualmente CAP_DAC_OVERRIDE
antes CAP_INHERITABLE
de ligar execl
:
#include <sys/capability.h>
#include <stdio.h>
#include <unistd.h>
int main(int argc, char **argv[]) {
cap_t caps = cap_get_proc();
printf("Capabilities: %s\n", cap_to_text(caps, NULL));
cap_value_t newcaps[1] = { CAP_DAC_OVERRIDE, };
cap_set_flag(caps, CAP_INHERITABLE, 1, newcaps, CAP_SET);
cap_set_proc(caps);
printf("Capabilities: %s\n", cap_to_text(caps, NULL));
cap_free(caps);
return execl("/usr/bin/net", "net", "ads", "dns", "register", "-P", NULL);
}
Além disso, tive que adicionar cap_dac_override
recursos de arquivo permitidos /usr/bin/net
e definir o bit efetivo:
~ $ sudo setcap cap_dac_override=p ./registerdns
~ $ sudo setcap cap_dac_override=ei /usr/bin/net
~ $ ./registerdns
Capabilities = cap_dac_override+p
Capabilities = cap_dac_override+ip
Successfully registered hostname with DNS
Acho que agora entendo perfeitamente o que está acontecendo:
- O wrapper precisa
CAP_DAC_OVERRIDE
de seu conjunto permitido para poder adicioná-lo ao seu conjunto herdável. - O conjunto herdável do processo do wrapper é diferente do conjunto herdável do arquivo, portanto, definir +i no arquivo é inútil; o wrapper deve adicionar explicitamente
CAP_DAC_OVERRIDE
aoCAP_INHERITABLE
uso decap_set_flag
/cap_set_proc
. - O
net
arquivo precisa estarCAP_DAC_OVERRIDE
em seu conjunto herdável para que possa de fato herdar a capacidade do wrapper em seuCAP_PERMITTED
conjunto. Ele também precisa que o bit efetivo seja definido para que seja automaticamente promovido paraCAP_EFFECTIVE
.
Responder2
Acho que você precisa de ambos:
setcap cap_dac_override+pe ./registerdns
setcap cap_dac_override+i /usr/bin/net
Os pe
sinalizadores registerdns
indicam que a execução do programa adquire a capacidade. A i
bandeira net
indica que é permitido herdar a capacidade do programa de chamada.