In my custom environment an interceptor library is preloaded which runs a special implementation of bind()
, connect()
, etc. calls.
The problem I see is whenever an application is explicitly enabled capabilities using command setcap
, executing the application fails to preload interceptor library and calls default libc connect()
.
Is it an expected behavior? If yes, what could be the reason to disable LD_PRELOAD
?
Is there any tweak or method one can use to successfully preload library with capabilities enabled?
Advertisement
Answer
Like Oliver Matthews answered, LD_PRELOAD
is disabled for both setuid binaries, and for binaries having file capabilities, for security reasons.
To preload a library while still enabling file capabilities, you have two options:
Set the preloaded library setuid root
(The Linux dynamic linker
ld.so
does preload libraries even for setuid/file-capability-enabled binaries, if the libraries are owned by root and marked set-uid.)Use a setuid root wrapper
The wrapper obtains full root privileges (both real and effective user and group IDs zero), and stores the original real user and group ID to e.g. environment variable(s).
The preloaded library has a constructor, e.g.
static void my_library_init(void) __attribute__((constructor)); static void my_library_init(void) { /* ... */ }
which is automatically run prior to
main()
(but possibly after other constructors in other preloaded libraries, or in libraries that the preloaded libraries depend on).This constructor obtains the desired capabilities, either designated via environment variables (
getenv()
,cap_from_text()
) or the binary executable file itself (cap_from_file("/proc/self/exe")
).The constructor must temporarily use
prctl(PR_SET_KEEPCAPS, 1)
to keep capabilities over an identity change, and retainCAP_SETUID
andCAP_SETGID
capabilities to be able to change identity from root to the user and group specified in the environment variables, before limiting itself to the final capability set.
Both options have obvious security considerations. I recommend sanity checking (and clearing LD_PRELOAD
) in the preloaded library constructor. If anything seems suspicious, use _exit()
to abort the process immediately.
In general, I recommend the first option for simplicity (both implementation and security issues), but if there is some reason it cannot be used, I can provide a proof of concept code for the second case too. (I have verified both options work on Ubuntu 12.04.2 LTS running a 3.8.0-27-generic x86-64 kernel, using ext4 file system.)
Hope this helps.