web-dev-qa-db-ja.com

C / C ++のsystem()呼び出しで継承可能な機能

現在、私は http://man7.org/linux/man-pages/man7/capabilities.7.html を読んでLinuxの機能を理解しようとしています。

_CAP_DAC_READ_SEARCH+eip_機能を備えた小さなC++アプリケーションを作成しました

この機能はアプリケーションに対して適切に機能します。しかし、私はsystem()呼び出しを内部に持っています

_system("cat /dev/mtdX > targetFile");
_

この呼び出しに機能を継承するにはどうすればよいですか?

編集:

system()fork() + execl()によって駆動されることを知っています。ドキュメントでは、fork()を使用すると、子プロセスが親プロセスと同じ機能を取得することが説明されています。しかし、なぜ読み取り機能が継承されないのですか?

3
svanschu

@mosvyへのThx私は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";
}
1
svanschu

まず、system(3)を邪魔にならないようにしてください。あなたが提案しているものとは異なり、system(3)は_fork+exec_だけではなく、信号の性質の変更、子の待機、およびラッパーとしての_/bin/sh_の使用を含む非常に複雑なものです(これにより、または、メンテナの気まぐれや仮定、環境変数の混乱、ソース初期化スクリプト、その他の面白いことに応じて機能を追加します)。 execv*(2)の代わりにsystem(3)だけを使用すると、これらすべての疑わしい複雑化が邪魔になりません。

次に、 execve() のマンページの「capabilities(7)中の機能の変換」の部分を詳しく確認する必要があります。ここにコピーアンドペーストするつもりはありませんが、基本的には次のようになります:ambientに追加しない限り、機能はexecve()を介して継承されません=スレッド(プロセス)のsetであり、それらがthreadの継承可能なセットに既に含まれていない限り、そこに追加することはできません。 (ファイルのメタデータからの「継承可能な」機能は単なるmaskであり、スレッドの機能を制限します)。

したがって、execve()を通じて機能を継承するには、a)permittedからそれらをコピーする必要があります= -inheritableセット( capset(2) システムコール[1]で実行できます)およびb )それらをambientセットに追加します(これは 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ラッパーを使用するように変換することは、読者への課題として残されています。

3
mosvy