5

Generally speaking, a unix (or specifically Linux) program can't do something like using ICMP_ECHO ("ping") to check the accessibility of a router unless either run by the superuser or setuid root or blessed with the appropriate POSIX capability. Obviously, on any competently-run system applying either setuid or a POSIX capability to a binary requires superuser intervention.

If a development environment has been blessed with the CAP_SETFCAP capability, then it should be able to set appropriate POSIX capabilities on programs it builds, at least as far as local operation is concerned.

With a nod to Ken Thompson's Reflections on Trusting Trust paper and assuming static linkage of all libraries it should, in principle, be possible to build a fingerprint into every program source module, to propagate that to object and binary files, and hence to provide an audit trail that demonstrates that a particular binary has been built from a particular collection of sources. As such, an administrator asked to bless a newly-built copy of the IDE should be able to satisfy herself that the IDE will only be able to set capabilities in programs it generates itself, and hasn't been modified by a malicious user so that he can use it as his personal copy of setcap by means of e.g. an undocumented startup option.

The problem here is that most mature development environments (e.g. the Lazarus IDE) can build themselves, and as such if the local administrator blessed a provably-clean copy with CAP_SETFCAP a malicious user could rebuild it to include malicious code and apply CAP_SETFCAP to it himself, breaking the local system security.

Is it possible to apply the POSIX CAP_SETFCAP capability to a binary, in such a way that the one thing it can't propagate to a newly-built program is another CAP_SETFCAP or one of its superset capabilities?

4
  • Is it that relevant for the development environment to be able to give out capabilities? In the end, you need the caps (or whatever permissions) available to the program in the system where it's used, which in general is different from the system it's developed on, so wouldn't the install step be where you need to worry about giving out the permissions?
    – ilkkachu
    Oct 6, 2022 at 18:51
  • I agree, particularly since executing via gdb etc. will typically not propagate the capabilities. However during development (particularly using an IDE) it does remove the requirement to regularly respond to a sudo prompt (e.g. issued by ssh-askpass)... I can assure you that having to break ones flow to do that manually when one actually wants to test a newly-built program is almost as irksome as realising one forgot to do it :-) The (in)ability of install etc. to set capabilities is obviously a different issue. Oct 6, 2022 at 19:02
  • Sudo has that timeout though, where you only need to enter the password every N seconds, and I think the N is configurable. For SSH, you could use keys and ssh-agent (possibly also with a timeout). So yah, development is a bit special, since you want to minimize the nuisance of the install step, but it should be possible to tell the IDE to run the program through sudo, or run a script instead that added the appropriate caps (under sudo)
    – ilkkachu
    Oct 6, 2022 at 19:08
  • Yes but even so... I've been-there-done-that (with a program that wanted to check its router among other things) and while modifying the IDE to prompt for a password undoubtedly helps having it able to set at least some capabilities would have made things far more comfortable (noting that I'm an authorised developer for the project, my development machine is physically secure and so on). Oct 6, 2022 at 19:15

2 Answers 2

1

If you want to reserve the right to use setcap to just sudo enabled users, then there is no need to add any capability to it. Just do that.

If you have some notion of trusted users, you have two options for using capabilities. I've also included a third option if you just want to act like you have a capability, and perhaps package files etc.

  1. Make a local copy of setcap, restrict its use to a common group (builders) of users and give it a permitted capability. This trusts user members of that group to behave well:
$ cp $(which setcap) ./builder-setcap
$ sudo chgrp builders ./builder-setcap
$ chmod 0750 ./builder-setcap
$ sudo ./builder-setcap cap_setfcap=p ./builder-setcap
  1. Use an Inheritable file capability to limit who can cause the builder-setcap to run with privilege. This will require another step to actually obtain that privilege at run time (some way for the running user to pre-obtain a process Inheritable capability, such as capsh or pam_cap.so). That mechanism might be a wrapper for the build system. With capsh it is something like this:
$ cp $(which setcap) ./builder-setcap
$ sudo chgrp builders ./builder-setcap
$ chmod 0750 ./builder-setcap
$ sudo ./builder-setcap cap_setfcap=i ./builder-setcap
$ sudo capsh --user=$(whoami) --inh=cap_setfcap --
... $ # in this shell cap_setfcap is available to ./builder-setcap
  1. The third mechanism is to use a user namespace container. In such a container, there is a fake notion of privilege and a fake root user as well. In that environment, the unprivileged user is transformed into root for this container, and can simply invoke setcap to grant it fake capabilities:
$ unshare -Ur
... $ id
uid=0(root) gid=0(root) groups=0(root),65534(nobody)
... $ cp $(which setcap) builder-setcap
... $ ./builder-setcap cap_setfcap=p ./builder-setcap

In this last case, while inside the container, that file capability works, but once you exit it. The capability enabled file is not really capable:

... $ exit
$ getcap -n ./builder-setcap 
./builder-setcap cap_setfcap=p [rootid=1000]

The -n argument here reveals the user namespace root identity. You can see that back outside the namespace, the file doesn't really have a file capability as follows:

$ ./builder-setcap -r builder-setcap 
unable to set CAP_SETFCAP effective capability: Operation not permitted

I think the most viable approach for what you are trying to do is to write yourself a build invocation wrapper that does the equivalent of sudo capsh --user=$(whoami) --inh=cap_setfcap -- -c 'exec builder', and prepare the builder-setcap binary as per method 2.

Also, of course, you can write your own version of builder-setcap that filters down the file capabilities you are willing to bestow, if you are trying to grant some but not others. The total code complexity of setcap is pretty minimal.

1
  • Thanks for those, I will work through them in detail presently. They don't entirely break the pernicious "Thompson Loop" but I suspect that they're as close to a solution as it's possible to get without kernel modification. Oct 16, 2022 at 18:27
0

I had a similar problem a few years ago.
An employee received the assignment to produce statistics about protocol usage on the local network. He would use python to read packets using PACKET_RAW.
But using PACKET require CAP_NET_RAW.
Giving CAP_NET_RAW to Python was a no-no because other people would benefit from that privilege. Giving sudo to the employee was also no-no because he was a junior.

My solution was to write a launcher specifically for that. The launcher had CAP_NET_RAW privilege and started python with CAP_NET_RAW in the Ambient set. The launcher owner was root, was only executable by the specific user and readable by nobody else.
The launcher work fine but we never audited the security of that design.

1
  • 1
    That does, of course, highlight one of IP's dirty little secrets: permission to improper traffic onto the network may be granted by the perpetrator to himself. Apr 2, 2023 at 7:19

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .