@@ -794,6 +794,24 @@ dnl
esac
fi
+dnl
+dnl Depend on libcap-ng on Linux
+dnl
+case "$host" in
+ *-*-linux*)
+ PKG_CHECK_MODULES([LIBCAPNG],
+ [libcap-ng],
+ [have_libcapng="yes"],
+ [AC_MSG_ERROR([libcap-ng package not found. Is the development package and pkg-config installed?])]
+ )
+
+ 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
@@ -1138,6 +1138,25 @@ possibly_become_daemon(const struct options *options)
return ret;
}
+/*
+ * 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.
+ */
+static int
+get_need_keep_caps(struct context *c)
+{
+ if (dco_enabled(&c->options))
+ {
+ return 1;
+ }
+
+#ifdef ENABLE_SITNL
+ return -1;
+#else
+ return 0;
+#endif
+}
+
/*
* Actually do UID/GID downgrade, chroot and SELinux context switching, if requested.
*/
@@ -1167,8 +1186,10 @@ 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);
+ int keep_caps = get_need_keep_caps(c);
+ platform_user_group_set(&c0->platform_state_user,
+ &c0->platform_state_group,
+ keep_caps);
}
else if (c->first_time)
{
@@ -43,6 +43,10 @@
#include <direct.h>
#endif
+#ifdef HAVE_LIBCAPNG
+#include <cap-ng.h>
+#endif
+
/* Redefine the top level directory of the filesystem
* to restrict access to files for security */
void
@@ -155,6 +159,81 @@ platform_group_set(const struct platform_state_group *state)
#endif
}
+void platform_user_group_set(const struct platform_state_user *user_state,
+ const struct platform_state_group *group_state,
+ int keep_caps)
+{
+ 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 */
+ res = capng_change_id(new_uid, new_gid, CAPNG_DROP_SUPP_GRP | CAPNG_CLEAR_BOUNDING);
+ if (res == -4 || res == -6)
+ {
+ msg(M_ERR, "capng_change_id('%s','%s') failed: %d",
+ user_state->username, group_state->groupname, res);
+ }
+ 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");
+
+ return;
+fallback:
+#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)
@@ -85,6 +85,11 @@ bool platform_group_get(const char *groupname, struct platform_state_group *stat
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,
+ int keep_caps);
+
+
/*
* Extract UID or GID
*/