From patchwork Tue Mar 29 08:29:34 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Timo Rothenpieler X-Patchwork-Id: 2354 Return-Path: Delivered-To: patchwork@openvpn.net Delivered-To: patchwork@openvpn.net Received: from director15.mail.ord1d.rsapps.net ([172.27.255.55]) by backend41.mail.ord1d.rsapps.net with LMTP id IOOnIn5eQ2JBCQAAqwncew (envelope-from ) for ; Tue, 29 Mar 2022 15:31:10 -0400 Received: from proxy2.mail.iad3a.rsapps.net ([172.27.255.55]) by director15.mail.ord1d.rsapps.net with LMTP id +JY/MX5eQ2KRIgAAIcMcQg (envelope-from ) for ; Tue, 29 Mar 2022 15:31:10 -0400 Received: from smtp35.gate.iad3a ([172.27.255.55]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) by proxy2.mail.iad3a.rsapps.net with LMTPS id kK7dKX5eQ2KnOgAABcWvHw (envelope-from ) for ; Tue, 29 Mar 2022 15:31:10 -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: smtp35.gate.iad3a.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: c8f18720-af96-11ec-9bc5-52540083445f-1-1 Received: from [216.105.38.7] ([216.105.38.7:58424] helo=lists.sourceforge.net) by smtp35.gate.iad3a.rsapps.net (envelope-from ) (ecelerity 4.2.38.62370 r(:)) with ESMTPS (cipher=DHE-RSA-AES256-GCM-SHA384) id 40/DE-02491-D7E53426; Tue, 29 Mar 2022 15:31:10 -0400 Received: from [127.0.0.1] (helo=sfs-ml-4.v29.lw.sourceforge.com) by sfs-ml-4.v29.lw.sourceforge.com with esmtp (Exim 4.94.2) (envelope-from ) id 1nZHPV-0000tq-Sn; Tue, 29 Mar 2022 19:29:57 +0000 Received: from [172.30.20.202] (helo=mx.sourceforge.net) by sfs-ml-4.v29.lw.sourceforge.com with esmtps (TLS1.2) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.94.2) (envelope-from ) id 1nZHPV-0000ti-6Q for openvpn-devel@lists.sourceforge.net; Tue, 29 Mar 2022 19:29:57 +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: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:In-Reply-To:References:List-Id:List-Help:List-Unsubscribe: List-Subscribe:List-Post:List-Owner:List-Archive; bh=a44BjKexxFuEOaG0dbBpXUbhVKkHLVDKrukLvXWbL50=; b=XZVTkFGxaSMj4/vnQ3oOYwzLhX L73XADN+txvvQl0YeinKjJjjm6zQdlI2uAVlKSPuKjfCvw0VBLKtnvdCXSg73+JWnflDwZDZ5F2Tg tHU4jUzyxUQP4dbxqm3A/IVhnsDTlinSSB1lKOo3mxfxrklrnD7bDNLF5PFZjqRO69tQ=; DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=sf.net; s=x ; h=Content-Transfer-Encoding:MIME-Version: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:In-Reply-To: References:List-Id:List-Help:List-Unsubscribe:List-Subscribe:List-Post: List-Owner:List-Archive; bh=a44BjKexxFuEOaG0dbBpXUbhVKkHLVDKrukLvXWbL50=; b=C IQlaEpuo24TureDuWWCJr8og8Msw6+tU8BOEQZbCIHKeJXF8qJxdF6FcN8UBLI+8Azuxf8mLfrJoH H8adTFP1rKJADoBB1iAy8Dolc6vEFE5+USSiebcio2tzWFBDnytTbkpnyJC9zfNNjm2bZKxR4SUf8 E+rdJJQ55lu+qq6g=; Received: from btbn.de ([136.243.74.85]) by sfi-mx-1.v28.lw.sourceforge.com with esmtps (TLS1.2:ECDHE-RSA-AES256-GCM-SHA384:256) (Exim 4.94.2) id 1nZHXM-00G8qK-KQ for openvpn-devel@lists.sourceforge.net; Tue, 29 Mar 2022 19:29:56 +0000 Received: from [authenticated] by btbn.de (Postfix) with ESMTPSA id 3910F2F1B09; Tue, 29 Mar 2022 21:29:46 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=rothenpieler.org; s=mail; t=1648582186; bh=a44BjKexxFuEOaG0dbBpXUbhVKkHLVDKrukLvXWbL50=; h=From:To:Cc:Subject:Date; b=WFoRl9HAqnk+2FPpMmUb9DqIjV8gTmbYoPfiwfwOhCa8qLK/sh8bu77zYS8CisnTQ qQFXF95Un+Gh+xXqQRrm17/a9UiUiezqkO+0opyqWGm5IJV/DSGitENLWWKXIWfAXt AMWZLx9An3WmMkfpTev9Lu5CJ0AlbBjlL5W16YDxS6GHIfM9KxHQ/OUq6WJU1UJGFw 1IuA8QVkDlEeMio9f8C8gPOTg/3hIg0aG3gl6uM5gB5gdFxz6ftSeqZPN1CG8p6yHz W4Xu1+zgnacn2VvkN0pvqsXEjSz8qhZbttkBUWLzCDKHpsmWpmqhJY/YkJiXjpzkhU R1c4wlAnuwXkA== From: Timo Rothenpieler To: openvpn-devel@lists.sourceforge.net Date: Tue, 29 Mar 2022 21:29:34 +0200 Message-Id: <20220329192934.1415-1-timo@rothenpieler.org> X-Mailer: git-send-email 2.25.1 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: --- This patch sits on top of the current dco branch, and will not apply to latest master. It solves the issue of dropping root privileges breaking dco and sitnl due to missing NET_ADMIN capabilities. 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: 1nZHXM-00G8qK-KQ Subject: [Openvpn-devel] [PATCH] 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 --- This patch sits on top of the current dco branch, and will not apply to latest master. It solves the issue of dropping root privileges breaking dco and sitnl due to missing NET_ADMIN capabilities. configure.ac | 3 ++ src/openvpn/init.c | 22 +++++++++++++- src/openvpn/platform.c | 65 +++++++++++++++++++++++++++++++++++++++++- src/openvpn/platform.h | 2 +- 4 files changed, 89 insertions(+), 3 deletions(-) diff --git a/configure.ac b/configure.ac index 7199483a..220c63e5 100644 --- a/configure.ac +++ b/configure.ac @@ -710,6 +710,9 @@ if test -z "${LIBPAM_LIBS}"; then ) fi +AC_CHECK_HEADERS([linux/capability.h sys/syscall.h sys/prctl.h], + [AC_DEFINE(HAVE_LINUX_CAPABILITIES, 1, [Linux capability support available])]) + case "${with_mem_check}" in valgrind) AC_CHECK_HEADERS( diff --git a/src/openvpn/init.c b/src/openvpn/init.c index 8818ba6f..13c07ff0 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,9 @@ do_uid_gid_chroot(struct context *c, bool no_delay) { if (no_delay) { + int keep_caps = get_need_keep_caps(c); platform_group_set(&c0->platform_state_group); - platform_user_set(&c0->platform_state_user); + platform_user_set(&c0->platform_state_user, keep_caps); } else if (c->first_time) { diff --git a/src/openvpn/platform.c b/src/openvpn/platform.c index 450f28ba..680ebb5f 100644 --- a/src/openvpn/platform.c +++ b/src/openvpn/platform.c @@ -43,6 +43,12 @@ #include #endif +#ifdef HAVE_LINUX_CAPABILITIES +#include +#include +#include +#endif + /* Redefine the top level directory of the filesystem * to restrict access to files for security */ void @@ -91,17 +97,74 @@ platform_user_get(const char *username, struct platform_state_user *state) return ret; } +#ifdef HAVE_LINUX_CAPABILITIES +#define SET_CAP_HELPER(data, set, cap) data[(cap)>>5].set |= 1<<((cap)&31) + +static bool +do_keep_caps(bool prepare) +{ + struct __user_cap_header_struct cap_hdr = { _LINUX_CAPABILITY_VERSION_3 }; + struct __user_cap_data_struct cap_data[_LINUX_CAPABILITY_U32S_3] = {}; + + if (syscall(SYS_capget, &cap_hdr, cap_data) < 0) + { + msg(M_NONFATAL | M_ERRNO, "failed getting capabilities"); + return false; + } + + if (prepare) + { + SET_CAP_HELPER(cap_data, permitted, CAP_NET_ADMIN); + } + else + { + SET_CAP_HELPER(cap_data, effective, CAP_NET_ADMIN); + } + + if (syscall(SYS_capset, &cap_hdr, cap_data) < 0) + { + msg(M_NONFATAL | M_ERRNO, "failed setting %s capabilities", prepare ? "permitted" : "effective"); + return false; + } + + if (prepare && prctl(PR_SET_KEEPCAPS, 1) < 0) + { + msg(M_NONFATAL | M_ERRNO, "failed setting keepcaps"); + return false; + } + + return true; +} +#else +static bool +do_keep_caps(bool prepare) +{ + return false; +} +#endif + void -platform_user_set(const struct platform_state_user *state) +platform_user_set(const struct platform_state_user *state, int keep_caps) { #if defined(HAVE_GETPWNAM) && defined(HAVE_SETUID) if (state->username && state->pw) { + bool caps_prepared = keep_caps && do_keep_caps(true); + if (setuid(state->pw->pw_uid)) { msg(M_ERR, "setuid('%s') failed", state->username); } msg(M_INFO, "UID set to %s", state->username); + + if (caps_prepared && do_keep_caps(false)) + { + msg(M_INFO, "Capabilities retained"); + } + else if (keep_caps > 0) + { + msg(M_FATAL, "Failed retaining capabilities"); + } } #endif } diff --git a/src/openvpn/platform.h b/src/openvpn/platform.h index a3eec298..ef06da93 100644 --- a/src/openvpn/platform.h +++ b/src/openvpn/platform.h @@ -79,7 +79,7 @@ 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); +void platform_user_set(const struct platform_state_user *state, int keep_caps); bool platform_group_get(const char *groupname, struct platform_state_group *state);