Home > OS >  Integrate granting of capabilities into the build process?
Integrate granting of capabilities into the build process?

Time:04-27

When developing a program that uses/requires specific capabilities (e.g. cap_net_raw), what is the recommended method for integrating the process of granting capabilities into the build process?

If I've understood the capabilities inheritance correctly, in order for the build system (e.g. CMake) to be able to invoke setcap to modify the capabilities of the build outputs, it would have to have the cap_setfcap capability itself, or be ran with sudo. And same for the parent context, be it a shell (e.g. bash) or an IDE (e.g. VS Code).

The scheme above works nicely for debugging, e.g. to debug a program that requires cap_X, I needed to give the same capability to gdb and configure it to NOT start the program in a shell (to avoid having to give the same capability to bash; I.e. the method outlined in this excellent thread: gdb appears to ignore executable capabilities)

Now, my obvious first choice would be to give the build system ( CMake) the cap_setfcap capability so that it can give the required capabilities to the build targets. This doesn't feel like proper solution, but more like an attempt to circumvent the whole capabilities system/framework, instead of operating within it.

Invoking setcap using sudo from the build also sounds like a bad idea, because it doesn't work nicely when invoking builds from within an IDE.

I figured I could add the following into sudo configuration:

<my-username-here> ALL=(ALL) NOPASSWD: /sbin/setcap

But this (use of sudo) also feels like a workaround and not a proper solution.

CodePudding user response:

Make a copy of the setcap program. Make it executable only by you (or your build user) and give it the CAP_SETFCAP capability.

$ cp `which setcap` ./build-setcap
$ chmod go-wrx ./build-setcap
$ sudo setcap cap_setfcap=p ./build-setcap

This binary is dangerous to have around, but you seem to be contemplating sudo with no password so this is equivalent.

Another method is to use an inheritable file capability:

$ cp `which setcap` ./build-setcap
$ chmod go-wrx ./build-setcap
$ sudo setcap cap_setfcap=i ./build-setcap
$ sudo capsh --inh=cap_setfcap --user=`whoami` --

which will place you in a shell where ./build-setcap appears to be privileged. The last line causes a shell to start that has been granted an inheritable process capability. This is what unlocks the file-inheritable capability.

CodePudding user response:

what is the recommended method for integrating the process of granting capabilities into the build process?

My general philosophy for writing CMake is that only firm requirements (i.e. those that are necessary for producing a correct binary) belong in the CMakeLists.txt. If your program is useless without some capability, I think it's reasonable to consider setting it a "firm" requirement, even though it will technically build.

Thus, as far as CMake is concerned, a post-build custom command is most appropriate:

cmake_minimum_required(VERSION 3.22)
project(test)

##
# Find setcap and create an IMPORTED target for it

find_program(SETCAP_PROGRAM setcap REQUIRED)

#!! see below

add_executable(setcap::setcap IMPORTED)
set_target_properties(
  setcap::setcap
  PROPERTIES
  IMPORTED_LOCATION "${SETCAP_PROGRAM}"
)

##
# Create privileged program with capability post-build step.

add_executable(privileged main.c)
add_custom_command(
  TARGET privileged
  POST_BUILD
  COMMAND setcap::setcap cap_net_raw=p "$<TARGET_FILE:privileged>"
  VERBATIM
)

Then you just need to build in an environment where you have the permissions to run ${SETCAP_PROGRAM}, which is the system setcap by default.

For instance, you could do as Tinkerer suggests and create your special build-setcap binary somewhere and then set -DSETCAP_PROGRAM=/path/to/build-setcap.

Alternatively, you could create (provide) a simple sudo-setcap.sh script that prepends sudo to the command line, like so:

#!/bin/sh
sudo -n setcap "$@"

Again, you'd set SETCAP_PROGRAM to it. This would play nicely with your sudoers file approach. If you always build from the same directory, you could even put the exact command CMake runs in the sudoers file to limit its scope, as in /sbin/setcap cap_net_raw=p /home/user/project/build/privileged


As an elaboration on the sudo-setcap.sh script idea, you could provide an option to users to automate this. Add the following code above in place of the #!! see below comment:

option(SETCAP_USE_SUDO "When enabled, call setcap with sudo")
if (SETCAP_USE_SUDO)
  find_program(SUDO_PROGRAM sudo REQUIRED)
  file(CONFIGURE OUTPUT sudo-setcap.sh
       CONTENT [[
#!/bin/sh
@SUDO_PROGRAM@ -n @SETCAP_PROGRAM@ "$@"
]]
       @ONLY)
  set(SETCAP_PROGRAM "${CMAKE_CURRENT_BINARY_DIR}/sudo-setcap.sh")
  file(CHMOD "${SETCAP_PROGRAM}"
       PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE)
endif ()

Then users can simply set -DSETCAP_USE_SUDO=YES at the command line to wrap their system SETCAP_PROGRAM with the same sudo call I detailed above. This code overwrites SETCAP_PROGRAM for the setcap::setcap target to point at the script later, but does not touch the cache, which will still hold the path to the actual setcap program.

  • Related