C/C++에서 system() 호출에 대해 상속 가능한 기능

C/C++에서 system() 호출에 대해 상속 가능한 기능

현재 나는 다음을 읽으면서 Linux의 기능을 이해하려고 노력하고 있습니다.http://man7.org/linux/man-pages/man7/capability.7.html

나는 다음과 같은 기능을 갖춘 작은 C++ 애플리케이션을 만들었습니다.CAP_DAC_READ_SEARCH+eip

이 기능은 애플리케이션에 적합하게 작동합니다. 그런데 system()안에서 전화 가 와요

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

이 호출에 대한 기능을 어떻게 상속받을 수 있나요?

편집하다:

나는 그것이 + system()에 의해 주도된다는 것을 알고 있습니다 . 문서에는 하위 프로세스가 상위 프로세스와 동일한 기능을 갖는다고 언급되어 있습니다. 그런데 읽기 기능이 상속되지 않는 이유는 무엇입니까?fork()execl()fork()

답변1

우선, 당신은 system(3)그 길에서 벗어나야 합니다. 당신이 제안하는 것과는 달리 신호 처리 변경, 자식 대기 및 래퍼로 사용(관리자의 변덕과 가정에 따라 기능을 삭제하거나 추가할 수 있음, 환경 변수를 엉망으로 만들 수 있음)과 관련된 매우 복잡한 것 system(3)입니다. 초기화 스크립트 및 기타 재미있는 것들). 대신에 just를 사용하면 모든 허위 합병증을 해결할 수 있습니다.fork+exec/bin/shexecv*(2)system(3)

execve()둘째, '능력의 전환 ' 부분을 깊이 살펴봐야 한다.capabilities(7)맨페이지. 여기에 복사하여 붙여넣지는 않겠지만 기본적으로 다음과 같이 요약됩니다.기능은 다음에 추가되지 않는 한 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 라이브러리 사용을 권장합니다. 이 예제의 일부는 libcap이 없고 많은 헤더 정의가 누락된 이전 버전의 Android용으로 작성한 해킹으로 인해 canibalized되었습니다. 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";
}

관련 정보