Capacidad heredable para la llamada system() en C/C++

Capacidad heredable para la llamada system() en C/C++

Actualmente estoy tratando de comprender las capacidades de Linux leyendohttp://man7.org/linux/man-pages/man7/capabilities.7.html

Creé una pequeña aplicación C++ con la capacidadCAP_DAC_READ_SEARCH+eip

La capacidad funciona bien para la aplicación. Pero tengo una system()llamada adentro

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

¿Cómo puedo heredar la capacidad de esta llamada?

Editar:

Sé que system()está impulsado por fork()+ execl(). En la documentación se menciona que con fork()el proceso hijo se obtienen las mismas capacidades que el proceso padre. Pero ¿por qué no se hereda la capacidad de lectura?

Respuesta1

En primer lugar, debes apartarte system(3)del camino; a diferencia de lo que estás sugiriendo, system(3)no es solo fork+exec, sino algo bastante complejo, que implica cambiar las disposiciones de las señales, esperar al niño y usarlo /bin/shcomo contenedor (que puede eliminar o agregar capacidades dependiendo de los caprichos y suposiciones de su mantenedor, alterar las variables de entorno, fuente scripts de inicialización y otras cosas divertidas). Usar just execv*(2)en lugar de system(3)eliminará todas esas complicaciones falsas.

En segundo lugar, deberías echar un vistazo profundo a la execve()parte "Transformación de capacidades durante" delcapabilities(7)página de manual. No voy a copiarlo y pegarlo aquí, pero básicamente se reduce a:Las capacidades NO se heredan a través de execve() a menos que se agreguen alambientecolocardel hilo (proceso), y no se pueden agregar allí, a menos que ya estén en el conjunto heredable delhilo. (Las capacidades "heredables" de los metadatos del archivo son sólo unamascarilla, limitando los del hilo).

Entonces, para poder heredar las capacidades, execve()debesa)copiarlos delpermitidohaciaheredableconjunto (que podrías hacer con elcapset(2)llamada al sistema [1]) yb)agregarlos alambienteconjunto (que podrías hacer conprctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_RAISE)).

Poniendolo todo junto:

$ 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] los documentos recomiendan usar la biblioteca libcap; partes de este ejemplo fueron canibalizadas de un truco que escribí para una versión anterior de Android, donde no había libcap y faltaban muchas definiciones de encabezado. convertirlo para usar los contenedores libcap se deja como ejercicio para el lector.

Respuesta2

Gracias a @mosvy, implementé su solución con libcap y parece funcionar como se esperaba.

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";
}

información relacionada