trogper
trogper

Reputation: 1693

Linux process capabilities empty despite executable has them set

I have a wrapper program, which is used only to add CAP_NET_RAW capability to a nodejs script. The binary has set capabilities to cap_net_raw+eip, but the process does not get them and setting them causes EPERM (Operation not permitted). The wrapper stopped working after upgrading from Debian 9 to 10. Adding the capability to nodejs binary works and the nodejs script runs fine, but it is not desired to allow raw access to network adapters to any nodejs script.

Here is the wrapper source code:

#include <sys/capability.h>
#include <unistd.h>

void main() {
        cap_t caps = cap_get_proc();
        cap_value_t newcaps[1] = { CAP_NET_RAW, };
        cap_set_flag(caps, CAP_INHERITABLE, 1, newcaps, CAP_SET);
        cap_set_proc(caps);
        cap_free(caps);
        execl("/usr/bin/node", "node", "/opt/sitemp/sitemp.js", NULL);
}

Running it under strace results in following:

capget({version=_LINUX_CAPABILITY_VERSION_3, pid=0}, NULL) = 0
capget({version=_LINUX_CAPABILITY_VERSION_3, pid=0}, {effective=0, permitted=0, inheritable=0}) = 0
capset({version=_LINUX_CAPABILITY_VERSION_3, pid=0}, {effective=0, permitted=0, inheritable=1<<CAP_NET_RAW}) = -1 EPERM (Operation not permitted)

Upvotes: 1

Views: 1090

Answers (1)

Tinkerer
Tinkerer

Reputation: 1068

Too long for a comment, here is a more complete explanation.

First, file capabilities only work on binaries.

Let's call your wrapper program wrapper.c (compiled as wrapper). What it is coded to do is raise an Inheritable capability. The way Inheritable capabilities can cause a subsequently executed program to have privilege is they AND with the 'file Inheritable' capabilities on the executable.

That is, you could do this:

$ sudo setcap cap_net_raw=p ./wrapper
$ sudo setcap cap_net_raw=ie /usr/bin/node

Now, when you execute ./wrapper it will raise an inheritable capability and when it execl()s /usr/bin/node the combination will inherit and raise the cap_net_raw capabilitiy in the permitted and effective flags of the running /usr/bin/node program.

If you don't use the ./wrapper, you won't have the process-inheritable capability and /usr/bin/node will not inherit any privilege.

Alternatively, you can clear those capabilities and modify your wrapper.c to look like this:

#include <stdio.h>
#include <stdlib.h>
#include <sys/capability.h>
#include <unistd.h>

void main() {
    cap_iab_t iab = cap_iab_from_text("^cap_net_raw");
    if (iab == NULL) {
        perror("iab not parsed");
        exit(1);
    }
    if (cap_iab_set_proc(iab)) {
        perror("unable to set iab");
        exit(1);
    }
    cap_free(iab);
    execl("/usr/bin/node", "node", "/opt/sitemp/sitemp.js", NULL);
    perror("execl failed");
}

You will need to compile and set this version of wrapper up as follows:

$ sudo setcap -r /usr/bin/node
$ cc -o wrapper wrapper.c -lcap
$ sudo setcap cap_net_raw,cap_setpcap=p ./wrapper

Now, when you run ./wrapper it will raise both the Ambient and Inheritable capabilities and the combination will cause /usr/bin/node to inherit some privilege directly from ./wrapper.

Upvotes: 0

Related Questions