Cap2

Cap2 is a Ruby library for managing the POSIX 1003.1e capabilities available in Linux kernels. These capabilities are a partitioning of the all powerful root privilege into a set of distinct privileges. See capabilities(7) for more information.

Installation

To install this library, you will need to be running Linux and have the libcap2 library and header files installed.

Install libcap2

Ubuntu

$ sudo apt-get install libcap2 libcap-dev

From source

$ curl -O http://ftp.de.debian.org/debian/pool/main/libc/libcap2/libcap2_2.22.orig.tar.gz
$ tar xzf libcap2_2.22.orig.tar.gz
$ cd libcap2-2.22
$ make
$ sudo make install

Install the gem

$ gem install cap2

Process and File Objects

Cap2 provides methods for querying and modifying capabilities for both processes and files.

Process capabilities are accessed via a Cap2::Process object (returned from Cap2.process) and file capabilities via a Cap2::File object (returned from Cap2.file):

Cap2.process              # Returns a Cap2::Process for the current process
Cap2.process(1000)        # Returns a Cap2::Process for the process with pid 1000
Cap2.file('/sbin/init')   # Returns a Cap2::File for the /sbin/init file

Querying Capabilities

Capabilities are referenced using lower cased symbols, and without the CAP_ prefix (e.g. :kill represents CAP_KILL).

There are three methods defined on both Cap2::Process and Cap2::File for querying capabilities:

  • permitted?
  • enabled?
  • inheritable?

Each of these methods, Cap2::File#enabled? being the exception, take a list of capability symbols and return true / false if the capabilities are in / not in the relevant set:

# the init daemon - all caps permitted & enabled but not inheritable
init = Cap2.process(1)            # => #<Cap2::Process @pid=1>

init.permitted?(:kill)            # => true
init.permitted?(:chown, :fowner)  # => true

init.enabled?(:fowner)            # => true
init.enabled?(:kill, :chown)      # => true

init.inheritable?(:chown)         # => false
init.inheritable?(:fowner, :kill) # => false
# assume /bin/ping is using file capabilities to enable CAP_NET_RAW on exec
ping = Cap2.file('/bin/ping')   # => #<Cap2::File @filename="/bin/ping">

ping.permitted?(:net_raw)       # => true
ping.permitted?(:mknod, :chown) # => false

ping.enabled?                   # => true

ping.inheritable?(:net_raw)     # => false

Why does Cap2::File#enabled? not take an arguement?

Under the hood, the file effective set is just a single bit. When enabled, any process which exec's the file will have the capabilities from it's resulting permitted set also in it's effective set.

Modifying File Capabilities

The file capability modification methods are:

  • permit(capabilities)
  • unpermit(capabilities)
  • allow_inherit(capabilities)
  • disallow_inherit(capabilities)
  • enable
  • disable

where capabilities can take any of the following forms:

# list of one or more symbols
(:chown)
(:mknod, :kill, :mac_admin)

# hash with an :only key
(:only => :fowner)
(:only => [:lease, :net_raw])

# hash with an :except key
(:except => :sys_time)
(:except => [:syslog, :fsetid])

Permitting capabilities

To modify the permitted capabilities of a file (i.e. the capabilities which will be permitted in any process that exec's the file), use Cap2::File#permit and Cap2::File#unpermit:

Cap2.process.enabled?(:setfcap)          # => true - needed to set file capabilities

file = Cap2.file('/tmp/file')            # => #<Cap2::File @filename="/tmp/file">

file.permitted?(:mknod, :chown, :fowner) # => false

file.permit(:mknod)                      # => true
file.permit(:chown, :fowner)             # => true
file.permitted?(:mknod, :chown, :fowner) # => true

file.unpermit(:mknod)                    # => true
file.unpermit(:chown, :fowner)           # => true
file.permitted?(:mknod, :chown, :fowner) # => false

Enabling capabilities

To modify the effective bit of a file (i.e. whether the resulting permitted capabilities of any process that exec's the file will also be enabled in it's effective set), use Cap2::File#enable and Cap2::File#disable:

Cap2.process.enabled?(:setfcap)     # => true - needed to set file capabilities

file = Cap2.file('/tmp/file')       # => #<Cap2::File @filename="/tmp/file">

file.permitted?(:net_raw)           # => true
file.enabled?                       # => false

file.enable                         # => true
file.enabled?                       # => true

file.disable                        # => true
file.enabled?                       # => false

Inheriting capabilities

To modify the inheritable capabilities of a file (i.e. the capabilities which will be ANDed with the inheritable set of any process that exec's the file to determine which capabilities are in the permitted set of the process), use Cap2::File#allow_inherit and Cap2::File#disallow_inherit:

Cap2.process.enabled?(:setfcap)     # => true - needed to set file capabilities

file = Cap2.file('/tmp/file')       # => #<Cap2::File @filename="/tmp/file">

file.inheritable?(:fowner)          # => false

file.allow_inherit(:fowner)         # => true
file.inheritable?(:fowner)          # => true

file.disallow_inherit(:fowner)      # => true
file.inheritable?(:fowner)          # => false

Clearing capabilities

To clear all capabilities for a file, use Cap2::File#clear:

Cap2.process.enabled?(:setfcap)     # => true - needed to set file capabilities

file = Cap2.file('/tmp/file')       # => #<Cap2::File @filename="/tmp/file">

file.permit(:kill, :mknod)          # => true
file.allow_inherit(:kill, :mknod)   # => true
file.enable                         # => true

file.permitted?(:kill, :mknod)      # => true
file.inheritable?(:kill, :mknod)    # => true
file.enabled?                       # => true

file.clear                          # => true

file.permitted?(:kill, :mknod)      # => false
file.inheritable?(:kill, :mknod)    # => false
file.enabled?                       # => false

Modifying Process Capabilities

Cap2 can be used to enable / disable capabilities of the current Ruby process.

Suppose the ruby binary file permits :kill, but does not enable it on exec:

ruby = Cap2.file('/usr/bin/ruby')   # => #<Cap2::File @filename="/usr/bin/ruby">
ruby.permitted?(:kill)              # => true
ruby.enabled?                       # => false

and we want to kill a process running as root with pid 1000:

Cap2.process.enabled?(:kill)    # => false
Process.kill("TERM", 1000)      # => Errno::EPERM: Operation not permitted

Cap2.process.enable(:kill)      # => true
Cap2.process.enabled?(:kill)    # => true
Process.kill("TERM", 1000)      # => 1

Notes

  • Cap2 cannot be used to modify capabilities of other processes. If you are curious as to why, see the Notes section of cap_set_proc(3) for an explanation.
  • File capabilities are not supported for shell scripts due to security implications, similar to the reasons setuid is disabled, see here for more information.