How to read file capabilities using Python?
linux, linux-capabilities, python
Solution
Python 3.3 comes with `os.getxattr`. If not, yeah... one way would be using `ctypes`, at least to get the raw stuff, or maybe use `pyxattr`
For `pyxattr`:
>>> import xattr
>>> xattr.listxattr("/bin/ping")
(u'security.capability',)
>>> xattr.getxattr("/bin/ping", "security.capability")
'\x00\x00\x00\x02\x00 \x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
For Python 3.3's version, it's essentially the same, just importing `os`, instead of `xattr`. `ctypes` is a bit more involved, though.
Now, we're getting the raw result, meaning that those two are most useful only retrieving textual attributes. But... we can use the same approach of `getcap`, through `libcap` itself [warning, this will work as-is only on Python 2, as written originally - read the note at the end for details]:
import ctypes
libcap = ctypes.cdll.LoadLibrary("libcap.so")
cap_t = libcap.cap_get_file('/bin/ping')
libcap.cap_to_text.restype = ctypes.c_char_p
libcap.cap_to_text(cap_t, None)
which gives me:
'= cap_net_raw+p'
probably more useful for you.
PS: note that `cap_to_text` returns a `malloc`ed string. It's your job to deallocate it using `cap_free`
Hint about the "binary gibberish":
>>> import struct
>>> caps = '\x00\x00\x00\x02\x00 \x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
>>> struct.unpack("<IIIII", caps)
(33554432, 8192, 0, 0, 0)
In that `8192`, the only active bit is the 13th. If you go to `linux/capability.h`, you'll see that `CAP_NET_RAW` is defined at `13`.
Now, if you want to write a module with all those constants, you can decode the info. But I'd say it's much more laborious than just using `ctypes` + `libcap`.
PS2: I notice another answer mentioning that there are problems with my invocation on the `ctype` solution. That's because I likely wrote that using Python 2.x back then in 2014 (as Python 3 offered `os.getxattr`).
The key here is reminding that back in Python 2.x there were no `bytes` objects, and `str` and `unicode` where separate entities. Python 3.x merged those two so that `str` is now basically `unicode`, and that may mess up with the C interface.
So, if using Python 3 (likely the case at this point), make sure to turn your strings into `bytes` (e.g., `foo_bar.encode('utf-8')`) before passing them to `libcap`, and the reverse operation with the results.
Problem
On Linux systems root privileges can be granted more selectively than adding the setuid bit using file capabilities. See `capabilities(7)` for details. These are attributes of files and can be read using the `getcap` program. How can these attributes be retrieved in Python? Even though running the `getcap` program using e.g. `subprocess` for answering such a question is possible it is not desirable when retrieving very many capabilities. It should be possible to devise a solution using `ctypes`. Are there alternatives to this approach or even libraries facilitating this task?