Возможность наследования для вызова system() в C/C++

Возможность наследования для вызова system() в C/C++

В настоящее время я пытаюсь понять возможности Linux, читаяhttp://man7.org/linux/man-pages/man7/capabilities.7.html

Я создал небольшое приложение на C++ с возможностьюCAP_DAC_READ_SEARCH+eip

Возможность работает отлично для приложения. Но у меня есть system()вызов внутри

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

Как я могу унаследовать возможность этого вызова?

Редактировать:

Я знаю, что это system()управляется fork()+ execl(). В документации упоминается, что с fork()дочерним процессом получаются те же возможности, что и у родительского процесса. Но почему возможность чтения не наследуется?

решение1

Прежде всего, вам следует убраться system(3)с дороги; в отличие от того, что вы предлагаете, system(3)это не просто fork+exec, а что-то довольно сложное, включающее изменение расположения сигналов, ожидание дочернего объекта и использование /bin/shв качестве оболочки (которая может уменьшать или увеличивать возможности в зависимости от прихотей и предположений ее сопровождающего, возиться с переменными окружения, исходными скриптами инициализации и другими забавными вещами). Использование просто execv*(2)вместо system(3)уберет все эти ложные осложнения с пути.

Во-вторых, вам следует глубоко изучить execve()часть «Трансформация возможностей во время»capabilities(7)manpage. Я не буду копировать-вставлять это сюда, но в целом это сводится к следующему:Возможности НЕ наследуются через execve(), если только они не добавлены вокружающийнаборпотока (процесса), и они не могут быть добавлены туда, если они уже не находятся в наследуемом наборенить. («Наследуемые» возможности метаданных файла — это всего лишьмаска, ограничивая таковые в потоке).

Итак, для того, чтобы унаследовать способности, execve()вам следуета)скопируйте их изразрешенныйкнаследственныйнабор (который вы могли бы сделать с помощьюcapset(2)системный вызов [1]) иб)добавьте их вокружающийнабор (который вы могли бы сделать сprctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_RAISE)).

Собираем все вместе:

$ 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] В документации рекомендуется использовать библиотеку libcap; части этого примера были взяты из хака, который я написал для старой версии Android, где не было libcap и отсутствовали многие определения заголовков. Преобразование его для использования оболочек libcap остается в качестве упражнения для читателя.

решение2

Спасибо @mosvy. Я реализовал его решение с помощью libcap, и, похоже, оно работает так, как и ожидалось.

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

Связанный контент