1

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)
John Kugelman
  • 349,597
  • 67
  • 533
  • 578
trogper
  • 1,516
  • 1
  • 12
  • 14
  • 1
    I have solved my issue by assigning the capability in systemd.service instead of custom wrapper/binary. Systemd services have attribute `AmbientCapabilities` used for exactnyl this – trogper Jan 18 '21 at 14:07
  • The Linux kernel doesn't trust scripts with file privilege, only compiled binaries. The same issue will occur if you try making `sitemp.js` setuid-root. – Tinkerer May 26 '21 at 02:46
  • The privileges are set on the binary, not the script. This setup worked in Debian 9 but does not in 10. – trogper May 27 '21 at 06:53
  • Rather than try to fit an explanation here, I'll post a more complete answer... – Tinkerer May 28 '21 at 05:12

1 Answers1

0

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.

Tinkerer
  • 865
  • 7
  • 9
  • you have probably a typo in `have privilege is they AND with` – trogper May 30 '21 at 14:17
  • Can you explain how is it different from my old solution and why it did work on debian 9 and does not on debian 10? My binary did have the capability set, but `cap_set_proc` returned error. – trogper May 30 '21 at 14:19
  • No typo.. This is a logical _AND_. As to how what you were trying ever worked in an earlier version of Debian, I can't guess. Without an Inheritable capability on the node binary, a process inheritable capability cannot _raise_ any privilege. This has been true in the kernel since file capabilities were added. – Tinkerer May 30 '21 at 14:38
  • why does the wrapper, having `cap_net_raw=eip`, get `EPERM` when calling `cap_set_proc`? Even `cap_get_proc` returns no capabilities – trogper May 31 '21 at 18:50
  • 1
    If you run it under `strace`, its privilege is suppressed. The kernel does this to prevent a privilege escalation, since the kernel APIs needed to run `strace` can actually modify the behavior of the `./wrapper` binary being traced. – Tinkerer May 31 '21 at 19:50