It turns out that setting +i on the wrapper does not add the capability to the CAP_INHERITABLE
set for the wrapper process, thus it is not passed through exec
. I therefore had to manually add CAP_DAC_OVERRIDE
to CAP_INHERITABLE
before calling execl
:
#include <sys/capability.h>
#include <stdio.h>
#include <unistd.h>
int main(int argc, char **argv[]) {
cap_t caps = cap_get_proc();
printf("Capabilities: %s\n", cap_to_text(caps, NULL));
cap_value_t newcaps[1] = { CAP_DAC_OVERRIDE, };
cap_set_flag(caps, CAP_INHERITABLE, 1, newcaps, CAP_SET);
cap_set_proc(caps);
printf("Capabilities: %s\n", cap_to_text(caps, NULL));
cap_free(caps);
return execl("/usr/bin/net", "net", "ads", "dns", "register", "-P", NULL);
}
In addition, I had to add cap_dac_override
to the permitted file capabilities set on /usr/bin/net
and set the effective bit:
~ $ sudo setcap cap_dac_override=p ./registerdns
~ $ sudo setcap cap_dac_override=ei /usr/bin/net
~ $ ./registerdns
Capabilities = cap_dac_override+p
Capabilities = cap_dac_override+ip
Successfully registered hostname with DNS
I think I now fully understand what's happening:
- The wrapper needs
CAP_DAC_OVERRIDE
in its permitted set so it can add it to its inheritable set.
- The wrapper's process inheritable set is different than its file inheritable set, so setting +i on the file is useless; the wrapper must explicitly add
CAP_DAC_OVERRIDE
to CAP_INHERITABLE
using cap_set_flag
/cap_set_proc
.
- The
net
file needs to have CAP_DAC_OVERRIDE
in its inheritable set so that it can in fact inherit the capability from the wrapper into its CAP_PERMITTED
set. It also needs the effective bit to be set so that it will be automatically promoted to CAP_EFFECTIVE
.
And one last desperate syntax guess pays off:
# setcap cap_net_bind_service,cap_sys_boot=+ep /usr/bin/nodejs
# getcap /usr/bin/nodejs
/usr/bin/nodejs = cap_net_bind_service,cap_sys_boot+ep
Best Answer
The way capabilities work in Linux is documented in
man 7 capabilities
.Processes' capabilities in the effective set are against which permission checks are done. File capabilities are used during an execv call (which happens when you want to run another program1) to calculate the new capability sets for the process.
Files have two sets for capabilities, permitted and inheritable and effective bit.
Processes have three capability sets: effective, permitted and inheritable. There is also a bounding set, which limits which capabilities may be added later to a process' inherited set and affects how capabilities are calculated during a call to execv. Capabilities can only be dropped from the bounding set, not added.
Permissions checks for a process are checked against the process' effective set. A process can raise its capabilities from the permitted to the effective set (using capget and capset syscalls, the recommended APIs are respectively cap_get_proc and cap_set_proc).
Inheritable and bounding sets and file capabilities come into play during an execv syscall. During execv, new effective and permitted sets are calculated and the inherited and bounding sets stay unchanged. The algorithm is described in the
capabilities
man page:If a capability is in both processes' inheritable set and the file's inheritable set (intersection/logical AND), it is added to the permitted set. The file's permitted set is added (union/logical OR) to it (if it is within the bounding set).
If the effective bit in file capabilities is set, all permitted capabilities are raised to effective after execv.
Capabilities in kernel are actually set for threads, but regarding file capabilities this distinction is usually relevant only if the process alters its own capabilities.
In your example capabilities
cap_net_raw
,cap_net_admin
andcap_dac_override
are added to the inherited and permitted sets and the effective bit is set. When your binary is executed, the process will have those capabilities in the effective and permitted sets if they are not limited by a bounding set.[1] For the fork syscall, all the capabilities and the bounding set are copied from parent process. Changes in uid also have their own semantics for how capabilities are set in the effective and permitted sets.