[Openvpn-devel,v5] platform: Retain CAP_NET_ADMIN when dropping privileges
Commit Message
On Linux, when dropping privileges, interaction with
the network configuration, such as tearing down routes
or ovpn-dco interfaces will fail when --user/--group are
used.
This patch sets the CAP_NET_ADMIN capability, which grants
the needed privileges during the lifetime of the OpenVPN
process when dropping root privileges.
Signed-off-by: Timo Rothenpieler <timo@rothenpieler.org>
Reviewed-By: David Sommerseth <davids@openvpn.net>
---
configure.ac | 19 +++
distro/systemd/openvpn-client@.service.in | 2 +-
distro/systemd/openvpn-server@.service.in | 2 +-
src/openvpn/init.c | 5 +-
src/openvpn/platform.c | 146 +++++++++++++++++++++-
src/openvpn/platform.h | 10 +-
6 files changed, 175 insertions(+), 9 deletions(-)
Comments
On 14/05/2022 12:37, Timo Rothenpieler wrote:
> On Linux, when dropping privileges, interaction with
> the network configuration, such as tearing down routes
> or ovpn-dco interfaces will fail when --user/--group are
> used.
>
> This patch sets the CAP_NET_ADMIN capability, which grants
> the needed privileges during the lifetime of the OpenVPN
> process when dropping root privileges.
>
> Signed-off-by: Timo Rothenpieler <timo@rothenpieler.org>
> Reviewed-By: David Sommerseth <davids@openvpn.net>
With Linux DCO now fully merged, this patch is ready to land.
It still applies cleanly for all I can tell.
On Sat, May 14, 2022 at 12:37:17PM +0200, Timo Rothenpieler wrote:
> On Linux, when dropping privileges, interaction with
> the network configuration, such as tearing down routes
> or ovpn-dco interfaces will fail when --user/--group are
> used.
>
> This patch sets the CAP_NET_ADMIN capability, which grants
> the needed privileges during the lifetime of the OpenVPN
> process when dropping root privileges.
>
> Signed-off-by: Timo Rothenpieler <timo@rothenpieler.org>
> Reviewed-By: David Sommerseth <davids@openvpn.net>
> ---
> configure.ac | 19 +++
> distro/systemd/openvpn-client@.service.in | 2 +-
> distro/systemd/openvpn-server@.service.in | 2 +-
> src/openvpn/init.c | 5 +-
> src/openvpn/platform.c | 146 +++++++++++++++++++++-
> src/openvpn/platform.h | 10 +-
> 6 files changed, 175 insertions(+), 9 deletions(-)
I ran several t_client test runs with --user nobody on a DCO-enabled system.
Without the patch:
- errors on teardown in all tests (sitnl)
- test 11 fails (which actually uses DCO, since no comp)
With the patch:
- errors on teardown gone
- test 11 passes
With the patch and --disable-dco --enable-iproute2:
- no cap retained
- errors on teardown (ip)
Looks to me like it does what it is supposed to do.
Acked-By: Frank Lichtenheld <frank@lichtenheld.com>
That said, maybe we should add some hint about this
behavior to the actual documentation? Maybe to
--user documentation? Or at least Changes?
Regards,
I have not tested this myself, but if I had, the test setup would have
been very similar to what Frank did (so, big thanks) - run a DCO
environment with "owner nobody", and see if things still work.
I will add this to my DCO server test environment - run one of the
iroute-using instances with "nobody", so it is continuously tested.
I did have a stare-at-code a few weeks ago, and we did discuss this
a few months ago, and the approach chosen seems to make sense.
Uncrustify complained about two lines with tabs -> fixed.
Your patch has been applied to the master branch.
commit 2e359a088226ab1e5ee41fbab27d38d8a8d192ac
Author: Timo Rothenpieler
Date: Sat May 14 12:37:17 2022 +0200
platform: Retain CAP_NET_ADMIN when dropping privileges
Signed-off-by: Timo Rothenpieler <timo@rothenpieler.org>
Acked-by: Frank Lichtenheld <frank@lichtenheld.com>
Message-Id: <20220514103717.235-1-timo@rothenpieler.org>
URL: https://www.mail-archive.com/openvpn-devel@lists.sourceforge.net/msg24360.html
Signed-off-by: Gert Doering <gert@greenie.muc.de>
--
kind regards,
Gert Doering
Hi,
On Thu, Aug 11, 2022 at 12:03:45PM +0200, Gert Doering wrote:
> Uncrustify complained about two lines with tabs -> fixed.
Turns out that it's actually 4 lines, and while I did "git apply $patch",
I forgot the "git commit --amend platform.c", so the whitespace errors
landed in the commit, and the *fix* was still sitting in my tree.
Since this is just whitespace, I'll merge the patch as follows,
into
commit 649874df9edb52a9d85bf9db690b6150fdb6dcc9 (HEAD -> master)
Author: Gert Doering <gert@greenie.muc.de>
Date: Thu Aug 11 13:26:58 2022 +0200
Apply uncrustify changes that forgotten in the last patch.
gert
HI,
On Thu, Aug 11, 2022 at 12:03:45PM +0200, Gert Doering wrote:
> I have not tested this myself, but if I had, the test setup would have
> been very similar to what Frank did (so, big thanks) - run a DCO
> environment with "owner nobody", and see if things still work.
>
> I will add this to my DCO server test environment - run one of the
> iroute-using instances with "nobody", so it is continuously tested.
[..]
> commit 2e359a088226ab1e5ee41fbab27d38d8a8d192ac
> Author: Timo Rothenpieler
> Date: Sat May 14 12:37:17 2022 +0200
>
> platform: Retain CAP_NET_ADMIN when dropping privileges
Unfortunately, it seems that our approach to "if SITNL is used, we hard
require that setting CAP_NET_ADMIN succeeds" is too strong for the twisted
ways that people use openvpn.
Namely, network-manager...
https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=1017379
... runs openvpn with --ifconfig-noexec / --route-noexec, and insists
on doing all that itself. I do not like NM's way of trying to control
everything (up to the point that it defaults to redirecting a default
route to OpenVPN even if config and server do not want that), but this
is what Linux people seem to be stuck with, so we need to handle it.
So I think we need to amend this patch twofold
- if --ifconfig-noexec && --route-noexec are set, do not mandate
success on CAP_NET_ADMIN (users might want it for using it in --up
scripts, but if it fails, *openvpn* is not missing functionality)
--> this should take care of the NM case
- also, we might want to think long and hard about mandating it if
--client (that is, --pull) is in use. We postpone dropping of privileges
until after the initial ifconfig/route setup has been done, and on
program end, closing tun/dco interface will (I hope) make the interface
go away without needing privileges, and the system will remove the
routes together with it.
--redirect-gateway will break, --redirect-gateway def1 will not.
Overlapping vpn routes with vpn gateway (= install a host route) will
also be unable to clean up at program end. But this is no worse than
2.5.x with --user nobody.
gert
On 15/08/2022 11:54, Gert Doering wrote:
> HI,
>
> On Thu, Aug 11, 2022 at 12:03:45PM +0200, Gert Doering wrote:
>> I have not tested this myself, but if I had, the test setup would have
>> been very similar to what Frank did (so, big thanks) - run a DCO
>> environment with "owner nobody", and see if things still work.
>>
>> I will add this to my DCO server test environment - run one of the
>> iroute-using instances with "nobody", so it is continuously tested.
> [..]
>> commit 2e359a088226ab1e5ee41fbab27d38d8a8d192ac
>> Author: Timo Rothenpieler
>> Date: Sat May 14 12:37:17 2022 +0200
>>
>> platform: Retain CAP_NET_ADMIN when dropping privileges
>
> Unfortunately, it seems that our approach to "if SITNL is used, we hard
> require that setting CAP_NET_ADMIN succeeds" is too strong for the twisted
> ways that people use openvpn.
That's not how the patch operates.
It only hard-requires the capability retention is dco_enabled() returns
true.
In all other cases, it will try to retain capabilities, but continue
with a warning if it fails.
Making the dco_enabled() case a "try but continue" would be a matter of
changing a 1 to a -1. But given that DCO can't really work then, I'm not
sure if that's desirable.
Hi,
On Mon, Aug 15, 2022 at 12:14:23PM +0200, Timo Rothenpieler wrote:
> > Unfortunately, it seems that our approach to "if SITNL is used, we hard
> > require that setting CAP_NET_ADMIN succeeds" is too strong for the twisted
> > ways that people use openvpn.
>
> That's not how the patch operates.
> It only hard-requires the capability retention is dco_enabled() returns
> true.
> In all other cases, it will try to retain capabilities, but continue
> with a warning if it fails.
Yes, but we do have DCO here, setting CAP_NET_ADMIN fails, and we abort,
instead of having a working client connect.
> Making the dco_enabled() case a "try but continue" would be a matter of
> changing a 1 to a -1. But given that DCO can't really work then, I'm not
> sure if that's desirable.
*DCO* can work fine, from the looks of that bug report (because all
the DCO open happens before capability drop on a --client config).
"Calling ifconfig and installing routes" cannot, but this is exactly what
we are not doing if --ifconfig-noexec + --route-noexec are set.
Please look closely at the log in the bug report, and keep in mind
that NM will do all the "SITNL" stuff for the user in that scenaro,
not OpenVPN itself.
gert
On 15/08/2022 12:29, Gert Doering wrote:
> Hi,
>
> On Mon, Aug 15, 2022 at 12:14:23PM +0200, Timo Rothenpieler wrote:
>>> Unfortunately, it seems that our approach to "if SITNL is used, we hard
>>> require that setting CAP_NET_ADMIN succeeds" is too strong for the twisted
>>> ways that people use openvpn.
>>
>> That's not how the patch operates.
>> It only hard-requires the capability retention is dco_enabled() returns
>> true.
>> In all other cases, it will try to retain capabilities, but continue
>> with a warning if it fails.
>
> Yes, but we do have DCO here, setting CAP_NET_ADMIN fails, and we abort,
> instead of having a working client connect.
>
>> Making the dco_enabled() case a "try but continue" would be a matter of
>> changing a 1 to a -1. But given that DCO can't really work then, I'm not
>> sure if that's desirable.
>
> *DCO* can work fine, from the looks of that bug report (because all
> the DCO open happens before capability drop on a --client config).
>
> "Calling ifconfig and installing routes" cannot, but this is exactly what
> we are not doing if --ifconfig-noexec + --route-noexec are set.
>
> Please look closely at the log in the bug report, and keep in mind
> that NM will do all the "SITNL" stuff for the user in that scenaro,
> not OpenVPN itself.
>
> gert
There's basically two options then:
Remove the dco_enabled() check entirely, and purely check for SITNL, but
always only warn if it fails and carry on, hoping it works fine.
Add checks for ifconfig-noexec + route-noexec being set, and either only
warn in that case, or don't even try to retain capabilities, since
they're not needed either way. I'd prefer the later, since fewer
capabilities is generally better.
The later of the two options seems nicer, will have a look at
implementing them. Shouldn't be that hard, given the config is already
there.
I don't have a NM setup to test that on though.
Hi,
On Mon, Aug 15, 2022 at 12:40:55PM +0200, Timo Rothenpieler wrote:
> Add checks for ifconfig-noexec + route-noexec being set, and either only
> warn in that case,
... this is what I suggested two mails upthread :-)
> or don't even try to retain capabilities, since
> they're not needed either way. I'd prefer the later, since fewer
> capabilities is generally better.
I could see arguments for "we want to do the ifconfig/route setup in
an --up script" - for example to do VRF/NetNS stuff that OpenVPN can not
do itself. So for these scenarios having the capability around would
be useful (= thus, try-and-warn)...
Different scenario, same options. We don't always know what users want.
> implementing them. Shouldn't be that hard, given the config is already
> there.
> I don't have a NM setup to test that on though.
Sending to the debian bug report referenced upthread and asking them
to test might be an option here. Or ask David.
I do not use NM either.
gert
Hi,
On Mon, 15 Aug 2022 at 12:50, Gert Doering <gert@greenie.muc.de> wrote:
> On Mon, Aug 15, 2022 at 12:40:55PM +0200, Timo Rothenpieler wrote:
> > or don't even try to retain capabilities, since
> > they're not needed either way. I'd prefer the later, since fewer
> > capabilities is generally better.
>
> I could see arguments for "we want to do the ifconfig/route setup in
> an --up script" - for example to do VRF/NetNS stuff that OpenVPN can not
> do itself. So for these scenarios having the capability around would
> be useful (= thus, try-and-warn)...
>
> Different scenario, same options. We don't always know what users want.
Let me just second the "fewer capabilities is generally better"
argument. CAP_NET_ADMIN is a broad set of effective capabilties and
has been a popular path for privilege escalation vulnerabilities in
the past. See for example these two recent CVEs:
CVE-2022-2586
A use-after-free in the Netfilter subsystem may result in local
privilege escalation for a user with the CAP_NET_ADMIN capability in
any user or network namespace.
CVE-2022-2588
Zhenpeng Lin discovered a use-after-free flaw in the cls_route
filter implementation which may result in local privilege escalation
for a user with the CAP_NET_ADMIN capability in any user or network
namespace.
So I'm really not in favour of retaining CAP_NET_ADMIN "just in case".
I would even like to be able to not retain it at all.
-Steffan
Hi,
On Mon, Aug 15, 2022 at 11:54:21AM +0200, Gert Doering wrote:
> [..]
> > commit 2e359a088226ab1e5ee41fbab27d38d8a8d192ac
> > Author: Timo Rothenpieler
> > Date: Sat May 14 12:37:17 2022 +0200
> >
> > platform: Retain CAP_NET_ADMIN when dropping privileges
>
> Unfortunately, it seems that our approach to "if SITNL is used, we hard
> require that setting CAP_NET_ADMIN succeeds" is too strong for the twisted
> ways that people use openvpn.
>
> Namely, network-manager...
>
> https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=1017379
>
> ... runs openvpn with --ifconfig-noexec / --route-noexec, and insists
> on doing all that itself. I do not like NM's way of trying to control
> everything (up to the point that it defaults to redirecting a default
> route to OpenVPN even if config and server do not want that), but this
> is what Linux people seem to be stuck with, so we need to handle it.
We've discussed this quite a bit in #openvpn-devel, and it's more complicated
than "just ifconfig and route".
Namely, there is more operations OpenVPN needs to do if running with
the DCO kernel module, which all needs CAP_NET_ADMIN
- renegotiate on TLS session expiry (control channel communication is
done through kernel module, not directly on socket)
- install / swap keys
- reconfigure the kernel peer on reconnect (peer-id / remote IP)
So, running in an environment that prevents use of CAP_NET_ADMIN (how
does it do that, in the first place?) *and* forces a non-root user
means "DCO will break". Not immediately, but openvpn will abort on
each of these envents - NM might hide that by just restarting it, but
it's still broken.
So the discussion seems to run towards two options
- disable DCO if CAP_NET_ADMIN can not be retained
- fix this in NM
Preferably, fix this in NM...
gert
Hi,
On Tue, Aug 16, 2022 at 11:16:50AM +0200, Steffan Karger wrote:
> So I'm really not in favour of retaining CAP_NET_ADMIN "just in case".
> I would even like to be able to not retain it at all.
I hear what you say, and I support that line of thought.
Alas, with current Linux-DCO, we need this for fiddling all the DCO bits
(keys / key renewal! / peer-id / install/update peers), as this is done
via netlink, and netlink requires CAP_NET_ADMIN.
To get around that we'd need to find a communication channel to
(Linux-)DCO that does not require privileges after initial setup
("hey, DCO, give me a socket that does not need privileges") and
rewrite quite a bit... I'm sure that Antonio will be thrilled by
that new challenge... :-)
gert
Hi,
On Mon, Aug 15, 2022 at 11:54:21AM +0200, Gert Doering wrote:
> [..]
> > commit 2e359a088226ab1e5ee41fbab27d38d8a8d192ac
> > Author: Timo Rothenpieler
> > Date: Sat May 14 12:37:17 2022 +0200
> >
> > platform: Retain CAP_NET_ADMIN when dropping privileges
>
> Unfortunately, it seems that our approach to "if SITNL is used, we hard
> require that setting CAP_NET_ADMIN succeeds" is too strong for the twisted
> ways that people use openvpn.
>
> Namely, network-manager...
>
> https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=1017379
For the sake of the list archives: *this* particular problem has been
solved by
commit da31c1654c8534658157cfe9c9de5750ee752608
Author: Timo Rothenpieler <timo@rothenpieler.org>
Date: Wed Aug 17 15:18:17 2022 +0200
dco: disable DCO if --user specified but unable to retain capabilities
so if we detect "the caller wants us to go to --user $notroot but we do
not have the necessary capabilities to retain CAP_NET_ADMIN, disable DCO".
This is basically the only thing we can do - if we have no CAP_NET_ADMIN,
DCO will be unable to function today.
Next steps are
- talk to the NM maintainers to get them to call OpenVPN with something
like "CAP_NET_ADMIN and uid != 0" (and no --user config) - so we can
just do our thing, without root privs. David :-)
- figure out if we can do Linux DCO without CAP_NET_ADMIN, at least
"after startup" (open with privs, get a ticket, continue without privs,
something mumble mumble). Antonio :-)
Thanks for all the enlightenment that happened here.
gert
@@ -794,6 +794,25 @@ dnl
esac
fi
+dnl
+dnl Depend on libcap-ng on Linux
+dnl
+case "$host" in
+ *-*-linux*)
+ PKG_CHECK_MODULES([LIBCAPNG],
+ [libcap-ng],
+ [],
+ [AC_MSG_ERROR([libcap-ng package not found. Is the development package and pkg-config installed?])]
+ )
+ AC_CHECK_HEADER([sys/prctl.h],,[AC_MSG_ERROR([sys/prctl.h not found!])])
+
+ CFLAGS="${CFLAGS} ${LIBCAPNG_CFALGS}"
+ LIBS="${LIBS} ${LIBCAPNG_LIBS}"
+ AC_DEFINE(HAVE_LIBCAPNG, 1, [Enable libcap-ng support])
+ ;;
+esac
+
+
if test "${with_crypto_library}" = "openssl"; then
AC_ARG_VAR([OPENSSL_CFLAGS], [C compiler flags for OpenSSL])
AC_ARG_VAR([OPENSSL_LIBS], [linker flags for OpenSSL])
@@ -11,7 +11,7 @@ Type=notify
PrivateTmp=true
WorkingDirectory=/etc/openvpn/client
ExecStart=@sbindir@/openvpn --suppress-timestamps --nobind --config %i.conf
-CapabilityBoundingSet=CAP_IPC_LOCK CAP_NET_ADMIN CAP_NET_RAW CAP_SETGID CAP_SETUID CAP_SYS_CHROOT CAP_DAC_OVERRIDE
+CapabilityBoundingSet=CAP_IPC_LOCK CAP_NET_ADMIN CAP_NET_RAW CAP_SETGID CAP_SETUID CAP_SETPCAP CAP_SYS_CHROOT CAP_DAC_OVERRIDE
LimitNPROC=10
DeviceAllow=/dev/null rw
DeviceAllow=/dev/net/tun rw
@@ -11,7 +11,7 @@ Type=notify
PrivateTmp=true
WorkingDirectory=/etc/openvpn/server
ExecStart=@sbindir@/openvpn --status %t/openvpn-server/status-%i.log --status-version 2 --suppress-timestamps --config %i.conf
-CapabilityBoundingSet=CAP_IPC_LOCK CAP_NET_ADMIN CAP_NET_BIND_SERVICE CAP_NET_RAW CAP_SETGID CAP_SETUID CAP_SYS_CHROOT CAP_DAC_OVERRIDE CAP_AUDIT_WRITE
+CapabilityBoundingSet=CAP_IPC_LOCK CAP_NET_ADMIN CAP_NET_BIND_SERVICE CAP_NET_RAW CAP_SETGID CAP_SETUID CAP_SETPCAP CAP_SYS_CHROOT CAP_DAC_OVERRIDE CAP_AUDIT_WRITE
LimitNPROC=10
DeviceAllow=/dev/null rw
DeviceAllow=/dev/net/tun rw
@@ -1163,8 +1163,9 @@ do_uid_gid_chroot(struct context *c, bool no_delay)
{
if (no_delay)
{
- platform_group_set(&c0->platform_state_group);
- platform_user_set(&c0->platform_state_user);
+ platform_user_group_set(&c0->platform_state_user,
+ &c0->platform_state_group,
+ c);
}
else if (c->first_time)
{
@@ -29,6 +29,9 @@
#include "syshead.h"
+#include "openvpn.h"
+#include "options.h"
+
#include "buffer.h"
#include "crypto.h"
#include "error.h"
@@ -43,6 +46,11 @@
#include <direct.h>
#endif
+#ifdef HAVE_LIBCAPNG
+#include <cap-ng.h>
+#include <sys/prctl.h>
+#endif
+
/* Redefine the top level directory of the filesystem
* to restrict access to files for security */
void
@@ -91,7 +99,7 @@ platform_user_get(const char *username, struct platform_state_user *state)
return ret;
}
-void
+static void
platform_user_set(const struct platform_state_user *state)
{
#if defined(HAVE_GETPWNAM) && defined(HAVE_SETUID)
@@ -130,7 +138,7 @@ platform_group_get(const char *groupname, struct platform_state_group *state)
return ret;
}
-void
+static void
platform_group_set(const struct platform_state_group *state)
{
#if defined(HAVE_GETGRNAM) && defined(HAVE_SETGID)
@@ -155,6 +163,140 @@ platform_group_set(const struct platform_state_group *state)
#endif
}
+/*
+ * Determine if we need to retain process capabilities. DCO and SITNL need it.
+ * Enforce it for DCO, but only try and soft-fail for SITNL to keep backwards compat.
+ *
+ * Returns the tri-state expected by platform_user_group_set.
+ * -1: try to keep caps, but continue if impossible
+ * 0: don't keep caps
+ * 1: keep caps, fail hard if impossible
+ */
+static int
+need_keep_caps(struct context *c)
+{
+ if (!c)
+ {
+ return -1;
+ }
+
+ if (dco_enabled(&c->options))
+ {
+#ifdef TARGET_LINUX
+ /* DCO on Linux does not work at all without CAP_NET_ADMIN */
+ return 1;
+#else
+ /* Windows/BSD/... has no equivalent capability mechanism */
+ return -1;
+#endif
+ }
+
+#ifdef ENABLE_SITNL
+ return -1;
+#else
+ return 0;
+#endif
+}
+
+/* Set user and group, retaining neccesary capabilities required by the platform.
+ *
+ * The keep_caps argument has 3 possible states:
+ * >0: Retain capabilities, and fail hard on failure to do so.
+ * ==0: Don't attempt to retain any capabilities, just sitch user/group.
+ * <0: Try to retain capabilities, but continue on failure.
+ */
+void platform_user_group_set(const struct platform_state_user *user_state,
+ const struct platform_state_group *group_state,
+ struct context *c)
+{
+ int keep_caps = need_keep_caps(c);
+ unsigned int err_flags = (keep_caps > 0) ? M_FATAL : M_NONFATAL;
+#ifdef HAVE_LIBCAPNG
+ int new_gid = -1, new_uid = -1;
+ int res;
+
+ if (keep_caps == 0)
+ {
+ goto fallback;
+ }
+
+ /*
+ * new_uid/new_gid defaults to -1, which will not make
+ * libcap-ng change the UID/GID unless configured
+ */
+ if (group_state->groupname && group_state->gr)
+ {
+ new_gid = group_state->gr->gr_gid;
+ }
+ if (user_state->username && user_state->pw)
+ {
+ new_uid = user_state->pw->pw_uid;
+ }
+
+ /* Prepare capabilities before dropping UID/GID */
+ capng_clear(CAPNG_SELECT_BOTH);
+ res = capng_update(CAPNG_ADD, CAPNG_EFFECTIVE | CAPNG_PERMITTED, CAP_NET_ADMIN);
+ if (res < 0)
+ {
+ msg(err_flags, "capng_update(CAP_NET_ADMIN) failed: %d", res);
+ goto fallback;
+ }
+
+ /* Change to new UID/GID.
+ * capng_change_id() internally calls capng_apply() to apply prepared capabilities.
+ */
+ res = capng_change_id(new_uid, new_gid, CAPNG_DROP_SUPP_GRP | CAPNG_CLEAR_BOUNDING);
+ if (res == -4 || res == -6)
+ {
+ /* -4 and -6 mean failure of setuid/gid respectively.
+ There is no point for us to continue if those failed. */
+ msg(M_ERR, "capng_change_id('%s','%s') failed: %d",
+ user_state->username, group_state->groupname, res);
+ }
+ else if (res == -3)
+ {
+ msg(M_NONFATAL | M_ERRNO, "capng_change_id() failed applying capabilities");
+ msg(err_flags, "NOTE: previous error likely due to missing capability CAP_SETPCAP.");
+ goto fallback;
+ }
+ else if (res < 0)
+ {
+ msg(err_flags | M_ERRNO, "capng_change_id('%s','%s') failed retaining capabilities: %d",
+ user_state->username, group_state->groupname, res);
+ goto fallback;
+ }
+
+ if (new_uid >= 0)
+ {
+ msg(M_INFO, "UID set to %s", user_state->username);
+ }
+ if (new_gid >= 0)
+ {
+ msg(M_INFO, "GID set to %s", group_state->groupname);
+ }
+
+ msg(M_INFO, "Capabilities retained: CAP_NET_ADMIN");
+ return;
+
+fallback:
+ /* capng_change_id() can leave this flag clobbered on failure
+ * This is working around a bug in libcap-ng, which can leave the flag set
+ * on failure: https://github.com/stevegrubb/libcap-ng/issues/33 */
+ if (prctl(PR_GET_KEEPCAPS) && prctl(PR_SET_KEEPCAPS, 0) < 0)
+ {
+ msg(M_ERR, "Clearing KEEPCAPS flag failed");
+ }
+#endif /* HAVE_LIBCAPNG */
+
+ if (keep_caps)
+ {
+ msg(err_flags, "Unable to retain capabilities");
+ }
+
+ platform_group_set(group_state);
+ platform_user_set(user_state);
+}
+
/* Change process priority */
void
platform_nice(int niceval)
@@ -55,6 +55,9 @@
#include "basic.h"
#include "buffer.h"
+/* forward declared to avoid large amounts of extra includes */
+struct context;
+
/* Get/Set UID of process */
struct platform_state_user {
@@ -79,11 +82,12 @@ struct platform_state_group {
bool platform_user_get(const char *username, struct platform_state_user *state);
-void platform_user_set(const struct platform_state_user *state);
-
bool platform_group_get(const char *groupname, struct platform_state_group *state);
-void platform_group_set(const struct platform_state_group *state);
+void platform_user_group_set(const struct platform_state_user *user_state,
+ const struct platform_state_group *group_state,
+ struct context *c);
+
/*
* Extract UID or GID