From patchwork Wed Mar 30 09:55:37 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Timo Rothenpieler X-Patchwork-Id: 2356 Return-Path: Delivered-To: patchwork@openvpn.net Delivered-To: patchwork@openvpn.net Received: from director12.mail.ord1d.rsapps.net ([172.30.191.6]) by backend41.mail.ord1d.rsapps.net with LMTP id EGurLhfWRGIgEAAAqwncew (envelope-from ) for ; Wed, 30 Mar 2022 18:13:43 -0400 Received: from proxy1.mail.ord1d.rsapps.net ([172.30.191.6]) by director12.mail.ord1d.rsapps.net with LMTP id mGsQNxfWRGL9OQAAIasKDg (envelope-from ) for ; Wed, 30 Mar 2022 18:13:43 -0400 Received: from smtp24.gate.ord1c ([172.30.191.6]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) by proxy1.mail.ord1d.rsapps.net with LMTPS id KJ/FNhfWRGL1CwAAasrz9Q (envelope-from ) for ; Wed, 30 Mar 2022 18:13:43 -0400 X-Spam-Threshold: 95 X-Spam-Score: 0 X-Spam-Flag: NO X-Virus-Scanned: OK X-Orig-To: openvpnslackdevel@openvpn.net X-Originating-Ip: [216.105.38.7] Authentication-Results: smtp24.gate.ord1c.rsapps.net; iprev=pass policy.iprev="216.105.38.7"; spf=pass smtp.mailfrom="openvpn-devel-bounces@lists.sourceforge.net" smtp.helo="lists.sourceforge.net"; dkim=fail (signature verification failed) header.d=sourceforge.net; dkim=fail (signature verification failed) header.d=sf.net; dkim=fail (signature verification failed) header.d=rothenpieler.org; dmarc=fail (p=none; dis=none) header.from=rothenpieler.org X-Suspicious-Flag: YES X-Classification-ID: a902b886-b076-11ec-aab2-b8ca3a674470-1-1 Received: from [216.105.38.7] ([216.105.38.7:33434] helo=lists.sourceforge.net) by smtp24.gate.ord1c.rsapps.net (envelope-from ) (ecelerity 4.2.38.62370 r(:)) with ESMTPS (cipher=DHE-RSA-AES256-GCM-SHA384) id B9/23-36340-716D4426; Wed, 30 Mar 2022 18:13:43 -0400 Received: from [127.0.0.1] (helo=sfs-ml-2.v29.lw.sourceforge.com) by sfs-ml-2.v29.lw.sourceforge.com with esmtp (Exim 4.94.2) (envelope-from ) id 1nZgYX-0004jS-MX; Wed, 30 Mar 2022 22:12:44 +0000 Received: from [172.30.20.202] (helo=mx.sourceforge.net) by sfs-ml-2.v29.lw.sourceforge.com with esmtps (TLS1.2) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.94.2) (envelope-from ) id 1nZgYV-0004jH-7v for openvpn-devel@lists.sourceforge.net; Wed, 30 Mar 2022 22:12:42 +0000 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=sourceforge.net; s=x; h=Content-Transfer-Encoding:MIME-Version:References: In-Reply-To:Message-Id:Date:Subject:Cc:To:From:Sender:Reply-To:Content-Type: Content-ID:Content-Description:Resent-Date:Resent-From:Resent-Sender: Resent-To:Resent-Cc:Resent-Message-ID:List-Id:List-Help:List-Unsubscribe: List-Subscribe:List-Post:List-Owner:List-Archive; bh=MUnFdLpWP65tNg+X2EpsrbVDIRgBleFfyQLkqPa++F4=; b=dLL0KfMvFLynnWRV+vpw/vndvT 7mkjUjUsJjHpULHUMElNehp/EsF3N8576q+6yUs5E0zAbfZekk1ih1i8xP3P5YDkLcRQsJTFefA5y o816RtAWpSRSyVnEnSStYT1dNmU7vgr3venPiQU454gmQnbxuyVAsSnXUtv94ytQWzZE=; DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=sf.net; s=x ; h=Content-Transfer-Encoding:MIME-Version:References:In-Reply-To:Message-Id: Date:Subject:Cc:To:From:Sender:Reply-To:Content-Type:Content-ID: Content-Description:Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc :Resent-Message-ID:List-Id:List-Help:List-Unsubscribe:List-Subscribe: List-Post:List-Owner:List-Archive; bh=MUnFdLpWP65tNg+X2EpsrbVDIRgBleFfyQLkqPa++F4=; b=DQQCED/rjaFhX2V8lcEVvvNcg+ /La0R5wTzi+eXYFNWBrmI0DBE5swJ5j5aUL+PphvKZwMiwbxlO7vDAesu5hMxlD3GSxxfK8A9e8vB jwSU5W0FouvLwUBZEpqvk+GmHpIbaGBrUITOk5rVktvPIC9GW+UKzyUmC6aKIyYpvxBw=; Received: from btbn.de ([136.243.74.85]) by sfi-mx-2.v28.lw.sourceforge.com with esmtps (TLS1.2:ECDHE-RSA-AES256-GCM-SHA384:256) (Exim 4.94.2) id 1nZgYC-0005TG-Ko for openvpn-devel@lists.sourceforge.net; Wed, 30 Mar 2022 22:12:41 +0000 Received: from [authenticated] by btbn.de (Postfix) with ESMTPSA id 0B3262FCB93; Wed, 30 Mar 2022 22:56:04 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=rothenpieler.org; s=mail; t=1648673764; bh=MUnFdLpWP65tNg+X2EpsrbVDIRgBleFfyQLkqPa++F4=; h=From:To:Cc:Subject:Date:In-Reply-To:References; b=KFKAsRR2/IS2adKZ1i9OG3VrGr2dNpcIjIN0aZAoc2w67PshpfIb/QlsWhJ90jw1E VRqSMg9vZJJQTOWODPSt6c28UcGqzjpXSDRuradpcjRz79bfv8mdDLM1ADyInS3jeO 7grp2ml5YQdD3Eek1HGcBhQFddJgdPFSnbYOodzYhmlkBhL/+GMlZROxY0lygb5UKF 0S6Uoh3wiW0KKJywNoNPWHW14da+omnInPmK8kwMFI+fI+FThqtmllJQm9SuULhf2o wpPDgwvA452Al8SMkmHtNlYpPLblvud+tLtBeWjK1rzfopiQ6lsRYwaYevnUiiJ5f/ kUWxwZTLprkQg== From: Timo Rothenpieler To: openvpn-devel@lists.sourceforge.net Date: Wed, 30 Mar 2022 22:55:37 +0200 Message-Id: <20220330205536.139-1-timo@rothenpieler.org> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20220329192934.1415-1-timo@rothenpieler.org> References: <20220329192934.1415-1-timo@rothenpieler.org> MIME-Version: 1.0 X-Spam-Report: Spam detection software, running on the system "util-spamd-1.v13.lw.sourceforge.com", has NOT identified this incoming email as spam. The original message has been attached to this so you can view it or label similar future email. If you have any questions, see the administrator of that system for details. Content preview: --- Using libcap-ng now configure.ac | 19 +++++ distro/systemd/openvpn-client@.service.in | 2 +- distro/systemd/openvpn-server@.service.in | 2 +- src/openvpn/init.c | 25 ++++++- src/openvpn/platform.c | 91 +++++++++++++++++ [...] Content analysis details: (-0.2 points, 6.0 required) pts rule name description ---- ---------------------- -------------------------------------------------- -0.0 SPF_HELO_PASS SPF: HELO matches SPF record -0.0 SPF_PASS SPF: sender matches SPF record -0.1 DKIM_VALID Message has at least one valid DKIM or DK signature -0.1 DKIM_VALID_EF Message has a valid DKIM or DK signature from envelope-from domain 0.1 DKIM_SIGNED Message has a DKIM or DK signature, not necessarily valid -0.1 DKIM_VALID_AU Message has a valid DKIM or DK signature from author's domain X-Headers-End: 1nZgYC-0005TG-Ko Subject: [Openvpn-devel] [PATCH v2] Retain CAP_NET_ADMIN when dropping privileges X-BeenThere: openvpn-devel@lists.sourceforge.net X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: openvpn-devel-bounces@lists.sourceforge.net X-getmail-retrieved-from-mailbox: Inbox --- Using libcap-ng now configure.ac | 19 +++++ distro/systemd/openvpn-client@.service.in | 2 +- distro/systemd/openvpn-server@.service.in | 2 +- src/openvpn/init.c | 25 ++++++- src/openvpn/platform.c | 91 +++++++++++++++++++++++ src/openvpn/platform.h | 5 ++ 6 files changed, 140 insertions(+), 4 deletions(-) Signed-off-by: Timo Rothenpieler Reviewed-By: David Sommerseth diff --git a/configure.ac b/configure.ac index 7199483a..168360d4 100644 --- a/configure.ac +++ b/configure.ac @@ -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], + [have_libcapng="yes"], + [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]) diff --git a/distro/systemd/openvpn-client@.service.in b/distro/systemd/openvpn-client@.service.in index cbcef653..159fb4dc 100644 --- a/distro/systemd/openvpn-client@.service.in +++ b/distro/systemd/openvpn-client@.service.in @@ -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 diff --git a/distro/systemd/openvpn-server@.service.in b/distro/systemd/openvpn-server@.service.in index d1cc72cb..6e8e7d94 100644 --- a/distro/systemd/openvpn-server@.service.in +++ b/distro/systemd/openvpn-server@.service.in @@ -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 diff --git a/src/openvpn/init.c b/src/openvpn/init.c index 8818ba6f..705eb92e 100644 --- a/src/openvpn/init.c +++ b/src/openvpn/init.c @@ -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) { diff --git a/src/openvpn/platform.c b/src/openvpn/platform.c index 450f28ba..4fce5a83 100644 --- a/src/openvpn/platform.c +++ b/src/openvpn/platform.c @@ -43,6 +43,11 @@ #include #endif +#ifdef HAVE_LIBCAPNG +#include +#include +#endif + /* Redefine the top level directory of the filesystem * to restrict access to files for security */ void @@ -155,6 +160,92 @@ 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. + * 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) + { + msg(M_ERR, "capng_change_id('%s','%s') failed: %d", + user_state->username, group_state->groupname, res); + } + else if (res < 0) + { + if (res == -3) + { + msg(M_NONFATAL, "Following error likely due to missing capability CAP_SETPCAP."); + } + 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 */ + 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) diff --git a/src/openvpn/platform.h b/src/openvpn/platform.h index a3eec298..b163f093 100644 --- a/src/openvpn/platform.h +++ b/src/openvpn/platform.h @@ -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 */