is it possible in Linux to change capabilities of process that is run? Is it possible to change path to the opened files (for example to redirect output).
If yes, how to?
You can't change this kind of process' environment from outside, but if you really have to, you can ptrace(2)
the process (actually a thread, but I will continue saying process for simplicity) with an adequate tool to make changes from within the running process. Of course it would be weird to rely on ptrace
for production usage, but if it were the only choice left...
gdb
can do thisYou can attach to a process (or some threads with more gdb-fu) and make it alter its own behaviour and detach from it. If done fast enough (ie: scripted) the process will be none the wiser. Depending on the process running and its actions at initialization, you can't always ptrace
from the same user, then requiring root access or at least CAP_SYS_PTRACE
capability. What is done to the process is actually done by itself: if the process is already permitted to change its capabilities (usually requires CAP_SETPCAP
), then it's possible to make itself do it. If it's not permitted an action, then ptrace
won't help, even done by root. Likewise, it's easy to make it close a log file and reopen it elsewhere.
Examples with some UL Q/A, some of them mine:
How to find the connection between tap interface and its file descriptor?: workaroud because kernel didn't provide this information via /proc/pid/fdinfo/ before kernel 3.14.
how to unshare network for current process: running gdb to (permanently) change a process' network namespace and effectively isolate it from network.
Stop program from writing to a deleted file: make a program change its runaway logfile. That's answering the 2nd example in your question.
To address your question's first example...
The example will be done on a bash
shell running as root (thus having full capababilities), using gdb
(also running as root). It's quite convoluted because libcap's functions aren't known if the program wasn't linked to it, so I chose the simplier but more tedious (because it requires manipulating structures) method which doesn't require any development environment nor extra gdb-fu: directly using syscalls definitions which are always known in gdb.
Term1:
# echo test > /tmp/test
# chown nobody /tmp/test
# chmod 600 /tmp/test
# cat < /tmp/test
test
# cat /tmp/test
test
# echo $$
5237
# grep ^Cap /proc/$$/status
CapInh: 0000000000000000
CapPrm: 0000003fffffffff
CapEff: 0000003fffffffff
CapBnd: 0000003fffffffff
CapAmb: 0000000000000000
#
Term2:
# gdb -q -p 5237
Attaching to process 5237
Reading symbols from /bin/bash...(no debugging symbols found)...done.
[...]
0x00007fb774737681 in pselect () from /lib/x86_64-linux-gnu/libc.so.6
(gdb) set $malloc=(void *(*)(long long)) malloc
(gdb) print $malloc(4*(2+3*2))
$1 = (void *) 0xc4ecc8
(gdb) set *((unsigned int *)($1))=0x20080522
(gdb) set *((unsigned int *)($1)+1)=getpid()
(gdb) print capget($1, (unsigned int *)$1+2)
$2 = 0
(gdb) set *((unsigned int *)($1)+2) &= ~(1<<1|1<<2)
(gdb) print capset($1, (unsigned int *)$1+2)
$3 = 0
(gdb) call free($1)
$4 = 0
(gdb) quit
A debugging session is active.
Inferior 1 [process 5237] will be detached.
Quit anyway? (y or n) y
Detaching from program: /bin/bash, process 5237
#
So this allocated memory, 64bits workaround style, for the needed *cap_user_header_t
and cap_user_data_t[2]
structures, set some magic value, retrieved the current process capabilities and removed CAP_DAC_OVERRIDE
and CAP_DAC_READ_SEARCH
from the capability Effective set, thus preventing it to read /tmp/test
, and finally freed the allocated memory.
Back in Term1:
# grep ^Cap /proc/$$/status
CapInh: 0000000000000000
CapPrm: 0000003fffffffff
CapEff: 0000003ffffffff9
CapBnd: 0000003fffffffff
CapAmb: 0000000000000000
# cat < /tmp/test
bash: /tmp/test: Permission denied
# cat /tmp/test
test
#
What's the difference? The bash process can't access anymore the non-root file with its restricted access when using the redirection and fails (after forking but before exec-ing cat
). When (forking and) exec-ing a new process, root recovers all its capabilities unless they are removed from yet-an-other-set: the capability Bounding set which can be changed by a different method (prctl(PR_CAPBSET_DROP, ...)
). That's why running cat
without redirection still works normally (for root).
grep heap /proc/$$/maps
), while other processes or Debian buster's bash 5.0.2 don't behave like this and use an high address heap. I don't have the knowledge to understand why.
– A.B
Mar 31 '19 at 14:58
In order to change the path of an open file, you might be able to use a hard link, but it would have to remain on the same filesystem as the original file. Once an app has opened a file, only the file handle matters, and the inode of the file. A hard link just provides a different filename/paths for the same inode.
If you just want to rename/reorganize this might work. be sure to update any config files so if you do restart the new location is picked up. If you're looking to move to a new filesystem to free up space or something this method will not work.
Sometimes sending a specific single (often SIGHUP) and the app will close all file handles, and reopen them. In this case it would usually reload the conf and so might pick up a new file path too. This behavior is highly dependent on the application though. Depending on the type of data this could also be really risky, and result in data loss, or corruption.
While, I'm not positive, I don't think you can change capabilities on a process. You'd have to exit and restart.
man 7 capabilities
, i.e. the restrictions imposed onto programs run as superuser? If yes, a thread may only change its own capabilities. – lgeorget Mar 30 '19 at 22:04