fork and execve to inherit unprivileged parent process' capabilities
In Linux system, an unprivileged user launches a program. The process created has the capabilities CAP_NET_RAW,CAP_NET_ADMIN
with mode as effective,permitted,inheritable
.
This process then creates a child process by calling fork
and execv
to invoke another program udhcpc, but the child process does not inherit the capabilities CAP_NET_RAW,CAP_NET_ADMIN
as expected. Even though before setting the capabilities I have called prctl(PR_SET_KEEPCAPS, 1)
.
Any suggestion on开发者_运维知识库 what to do to inherit unprivileged parent process' capabilities upon fork
followed by execve
?
On execve()
, the file capability sets of the file being executed (in this case, udhcpc
) are inspected and combined with the thread's capability sets. In particular, the file's Inheritable
set is AND
-ed with the thread's Inheritable
set to determine the new Permitted
set, and the file's Effective
bit must be set in order for the new Effective
set to be copied from the Permitted
set.
This implies that in your case you must use setcap cap_net_raw,cap_net_admin=ei /path/to/udhcpc
to obtain the effect you want (in addition to setting the capabilities in the parent process - the prctl()
is not necessary).
According to "The Linux Programming Interface" by Michael Kerrisk (No Starch Press, 2010):
Since kernel 2.6.24, it is possible to attach capabilities to a file. Various other features were added in kernels 2.6.25 and 2.6.26 in order to complete the capabilities implementation.
The tools sucap
and execcap
are what you should look up. However they are, if I recall limited to restricting, not granting capabilities. Look at :
http://www.linuxjournal.com/article/5737
and
http://lkml.indiana.edu/hypermail/linux/kernel/0503.1/2540.html
Extracted from the manual, There have been some changes. According to it fork
does not change capabilities. And now there is an ambient set, it seems that this is for what you are trying to do.
Ambient (since Linux 4.3):
This is a set of capabilities that are preserved across an execve(2) of a program that is not privileged. The ambient capability set obeys the invariant that no capability can ever
be ambient if it is not both permitted and inheritable.
The ambient capability set can be directly modified using
prctl(2). Ambient capabilities are automatically lowered if
either of the corresponding permitted or inheritable
capabilities is lowered.
Executing a program that changes UID or GID due to the set-
user-ID or set-group-ID bits or executing a program that has
any file capabilities set will clear the ambient set. Ambient
capabilities are added to the permitted set and assigned to
the effective set when execve(2) is called.
A child created via fork(2) inherits copies of its parent's
capability sets. See below for a discussion of the treatment of
capabilities during execve(2).
…
P'(ambient) = (file is privileged) ? 0 : P(ambient)
P'(permitted) = (P(inheritable) & F(inheritable)) |
(F(permitted) & cap_bset) | P'(ambient)
P'(effective) = F(effective) ? P'(permitted) : P'(ambient)
P'(inheritable) = P(inheritable) [i.e., unchanged]
where:
P denotes the value of a thread capability set before the
execve(2)
P' denotes the value of a thread capability set after the
execve(2)
F denotes a file capability set
cap_bset is the value of the capability bounding set (described
below).
It is useful to have a wrapper program that can execute any program with specific capabilities, without having to set capabilities on target programs. Such a wrapper is particularly useful to run software from a build directory (where setcap
would be cumbersome) or to run interpreters like Python (where it would be inappropriate).
As explained in other answers, ambient capabilities solve this, but they are only available since kernel 4.3. It is possible to work around this problem by having the wrapper load the target program directly instead of using exec
. By that, I mean open the executable, map relevant sections, set up the stack, etc., and jump to its code. This is a pretty complicated task, but luckily the wine-preloader program from the Wine project does exactly that (and some other things that are irrelevant for this purpose).
Run something like this as root to set up the wrapper:
cp /usr/bin/wine-preloader /path/to/wrapper
setcap cap_net_raw+ep /path/to/wrapper # set whatever capabilities you need
Now we have a copy of wine-preloader
that is able to run any program with those capabilities:
/path/to/wrapper /path/to/executable arguments...
This works but there are some pitfalls:
- The target program must be a path to an executable, it cannot find programs in
PATH
. - It does not work if the target program is a script with an interpreter (
#!
). - The
wine-preloader
prints a message about not being able to find something (but it still runs the program fine).
精彩评论