3

Currently I'm trying to understand capabilities in Linux by reading http://man7.org/linux/man-pages/man7/capabilities.7.html

I created a small C++ application with the capability CAP_DAC_READ_SEARCH+eip

The capability works fine for the application. But I have a system() call inside

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

How I can inherit the capability to this call?

Edit:

I know that system() is driven by fork() + execl(). In the documentation is mentioned, that with fork() the child process get the same capabilities as the parent process. But why is the read capability not inherited?

3

First of all, you should get system(3) out of the way; unlike what you're suggesting system(3) is not just fork+exec, but something quite complex, involving changing signal dispositions, waiting for the child and using /bin/sh as a wrapper (which may drop or add capabilities depending on its maintainer's whims and assumptions, mess with environment variables, source initialization scripts, and other funny things). Using just execv*(2) instead of system(3) will get all those spurious complications out of the way.

Second, you should have a deep look at the "Transformation of capabilities during execve()" part of the capabilities(7) manpage. I'm not going to copy-paste it here, but it basically boils down to: Capabilities are NOT inherited through execve() unless they're added to the ambient set of the thread (process), and they cannot be added there, unless they're already in the inheritable set of the thread. (The "inheritable" capabilities from the file's metadata are just a mask, limiting those of the thread).

So, in order the have the capabilities inherited through execve() you should a) copy them from the permitted to the inheritable set (which you could do with the capset(2) system call [1]) and b) add them to the ambient set (which you could do with prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_RAISE)).

Putting it all together:

$ 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] the docs recommend using the libcap library; parts of this example were canibalized from a hack I've written for an old version of android, where there was no libcap, and many header definitions were missing. converting it to use the libcap wrappers is left as an exercise to the reader.

| improve this answer | |
  • Thx @mosvy for the good explanation. I started implementing it with libcap. If I add the filecap with CAP_DAC_READ_SEARCH+eip, does the i not mean, that it can be inherit? Does it still need to be added to the thread inheritable list? – svanschu Apr 20 at 13:06
  • 1
    Yes, as I already mentioned, the inheritable caps from the binary's metadata are only a mask. Look at the formula from "Transformation of capabilities during execve()" in the linked manpage how F(inheritable) is used -- it does not factor into P'(inheritable) (the thread's capabilities after the execve(). – mosvy Apr 20 at 13:56
  • Thx @mosvy, I posted the answer based on your input below. – svanschu Apr 23 at 7:47
1

Thx to @mosvy I implemented his solution with libcap and it seems to work as expected.

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";
}
| improve this answer | |

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.