Capacidade herdável para chamada system() em C/C++

Capacidade herdável para chamada system() em C/C++

Atualmente estou tentando entender os recursos do Linux lendohttp://man7.org/linux/man-pages/man7/capabilities.7.html

Eu criei um pequeno aplicativo C++ com a capacidadeCAP_DAC_READ_SEARCH+eip

O recurso funciona bem para o aplicativo. Mas eu tenho uma system()ligação lá dentro

system("cat /dev/mtdX > targetFile");

Como posso herdar a capacidade desta chamada?

Editar:

Eu sei que isso system()é impulsionado por fork()+ execl(). Na documentação é mencionado que fork()o processo filho obtém os mesmos recursos do processo pai. Mas por que a capacidade de leitura não é herdada?

Responder1

Primeiro de tudo, você deveria sair system(3)do caminho; ao contrário do que você está sugerindo, system(3)não é apenas fork+exec, mas algo bastante complexo, envolvendo alteração da disposição do sinal, espera pelo filho e uso /bin/shcomo wrapper (que pode descartar ou adicionar recursos dependendo dos caprichos e suposições de seu mantenedor, mexer com variáveis ​​de ambiente, fonte scripts de inicialização e outras coisas engraçadas). Usar apenas execv*(2)em vez de system(3)eliminará todas essas complicações espúrias.

Em segundo lugar, você deve analisar profundamente a execve()parte "Transformação de capacidades durante" docapabilities(7)página de manual. Não vou copiar e colar aqui, mas basicamente se resume a:As capacidades NÃO são herdadas através de execve() a menos que sejam adicionadas aoambientedefinirdo thread (processo), e eles não podem ser adicionados lá, a menos que já estejam no conjunto herdável dofio. (Os recursos "herdáveis" dos metadados do arquivo são apenas ummascarar, limitando os do thread).

Portanto, para que você tenha os recursos herdados, execve()você devea)copie-os dopermitidopara oherdávelset (o que você poderia fazer com ocapset(2)chamada de sistema [1]) eb)adicione-os aoambienteset (o que você poderia fazer comprctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_RAISE)).

Juntando tudo:

$ cat capexec.c
#include <sys/prctl.h>
#include <unistd.h>
#include <sys/syscall.h>
#include <linux/capability.h>
#include <err.h>
int main(int ac, char **av){
        static char *dav[] = { "/bin/bash", 0 };

        struct __user_cap_header_struct hs;
        struct __user_cap_data_struct ds[2];
        hs.version = 0x20080522; /*_LINUX_CAPABILITY_VERSION_3;*/
        hs.pid = getpid();
        if(syscall(SYS_capget, &hs, ds)) err(1, "capget");
        ds[0].inheritable = ds[0].permitted;
        if(syscall(SYS_capset, &hs, ds)) err(1, "capset");

        if(prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_RAISE, CAP_DAC_READ_SEARCH, 0, 0)) err(1, "prctl(pr_cap_ambient_raise)");

        av = ac < 2 ? dav : av + 1;
        execvp(*av, av);
        err(1, "execvp %s", *av);
}
$ cc -Wall capexec.c -o capexec

   # as root
# setcap cap_dac_read_search+ip /tmp/capexec

$ ./capexec dd if=/dev/sda of=/dev/null count=1
1+0 records in
1+0 records out
512 bytes copied, 0.000299173 s, 1.7 MB/s

[1] os documentos recomendam o uso da biblioteca libcap; partes deste exemplo foram canibalizadas a partir de um hack que escrevi para uma versão antiga do Android, onde não havia libcap e muitas definições de cabeçalho estavam faltando. convertê-lo para usar os wrappers libcap é deixado como um exercício para o leitor.

Responder2

Obrigado a @mosvy, implementei sua solução com libcap e parece funcionar conforme o esperado.

void inheritCapabilities()
{
    cap_t caps;
    caps = cap_get_proc();
    if (caps == NULL)
        throw "Failed to load capabilities";
    printf("DEBUG: Loaded Capabilities: %s\n", cap_to_text(caps, NULL));
    cap_value_t cap_list[1];
    cap_list[0] = CAP_DAC_READ_SEARCH;
    if (cap_set_flag(caps, CAP_INHERITABLE, 1, cap_list, CAP_SET) == -1)
        throw "Failed to set inheritable";
    printf("DEBUG: Loaded Capabilities: %s\n", cap_to_text(caps, NULL));
    if (cap_set_proc(caps) == -1)
        throw "Failed to set proc";
    printf("DEBUG: Loaded Capabilities: %s\n", cap_to_text(caps, NULL));
    caps = cap_get_proc();
    if (caps == NULL)
        throw "Failed to load capabilities";
    printf("DEBUG: Loaded Capabilities: %s\n", cap_to_text(caps, NULL));

    if (prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_RAISE, CAP_DAC_READ_SEARCH, 0, 0) == -1)
        throw "Failed to pr_cap_ambient_raise!    Error: " + errno;
}

main() {
    inheritCapabilities();

    char *catargv[5];
    catargv[0] = (char *)"cmd";
    catargv[1] = (char *)"arg1";
    catargv[2] = (char *)"arg2";
    catargv[3] = (char *)"arg3";
    catargv[4] = NULL;

    if (execvp(catargv[0], catargv) == -1)
        throw "Failed! command";
}

informação relacionada