3

I am trying to start a program with network permissions so that it can execute iptables without being the root user. I need CAP_NET_ADMIN to be inheritable and permitted. It seems that the inheritable flag is cleared when the executable is started, but not effective or permitted:

Script started on Thu 25 Oct 2018 11:09:45 PM UTC
[ec2-user@ip-172-31-16-197 cap_question]$ cat caps.c 
#include <stdio.h>
#include <stdlib.h>
#include <sys/capability.h>

int main(int argc, char **argv) {
    cap_t caps = cap_get_proc();
    printf("Inside the executable [%s]\n", argv[0]);
    char *cap_text = cap_to_text(caps, NULL);
    printf("capabilities %s\n", cap_text);
    cap_free(cap_text);
    cap_free(caps);
}
[ec2-user@ip-172-31-16-197 cap_question]$ cc caps.c -o caps -lcap
[ec2-user@ip-172-31-16-197 cap_question]$ sudo setcap cap_net_admin=eip caps
[ec2-user@ip-172-31-16-197 cap_question]$ getcap caps
caps = cap_net_admin+eip
[ec2-user@ip-172-31-16-197 cap_question]$ ./caps
Inside the executable [./caps]
capabilities = cap_net_admin+ep
[ec2-user@ip-172-31-16-197 cap_question]$ exit

Script done on Thu 25 Oct 2018 11:10:25 PM UTC

As you can see, the executable file as cap_net_admin=eip. But when I actually run it, the permission set is cap_net_admin=ep. I don't understand why the executable drops inheritable when it starts. If I were to fork/exec iptables, it would not receive those permissions.

I've read threads like these and man capabilities many times and the best explanation I can come up with is that my shell does not have cap_net_admin=i, and so the child process does not. How can I start a process with the inheritable flag set as desired?

Spencer
  • 33
  • 3

1 Answers1

2

According to capabilities(7)

During an execve(2), the kernel calculates the new capabilities of the process using the following algorithm:

   P'(permitted) = (P(inheritable) & F(inheritable)) |
                    (F(permitted) & cap_bset)

   P'(effective) = F(effective) ? P'(permitted) : 0

   P'(inheritable) = P(inheritable)    [i.e., unchanged]

P(inheritable) means the inheritable bit in the process, F(inheritable) is the inheritable bit of the file being executed. As you can see, F(inheritable) is only used to determine the permitted capabilities of the process, it's not used to determine the inheritable capabilities.

The inheritable capabilities of a file is ANDed with the inheritable capabilities of the process -- this allows you to use this flag to prevent some capabilities from being set in the new process, it's not used to add anything to the new process.

If you want to make the capablility inheritable from your process, you can call cap_set_proc(). When a capability is permitted, you're allowed to make it inheritable.

Barmar
  • 741,623
  • 53
  • 500
  • 612
  • I have tried this. If I set `cap_net_admin,cap_net_raw=p` on the binary, `cap_set_proc` will fail when it tries to set the capabilities as inheritable. It does work if I apply `cap_setpcap=ep`, but I may as well be root at that point. – Spencer Oct 26 '18 at 00:37
  • I'm not sure why. The man page says that the permitted set is the limit on what can be added to the inheritable set. So if you have `cap_net_admin=ep` you should be able to make it inheritable. – Barmar Oct 26 '18 at 00:49
  • 1
    Tested it again -- you are correct. The actual issue was passing the wrong length ncap to cap_set_flag. The last sentence of your answer helped immensely! – Spencer Oct 26 '18 at 17:28