[stunnel-users] [PATCH] Use libcap to keep CAP_NET_RAW so that IP_TRANSPARENT works after setuid
Simon Arlott
simon at arlott.org
Tue May 26 14:54:23 CEST 2020
Use libcap to keep CAP_NET_RAW so that IP_TRANSPARENT works after setuid
I'd like to be able to use "transparent = source" and "setuid" to drop
full root privileges.
It is possible to do this by keeping either the CAP_NET_ADMIN or
CAP_NET_RAW capability.
This is based on https://github.com/dlundquist/sniproxy/commit/0ab83f8f45f73ba18ea5f84ccccd9e8c9321c5af
The capability will not be inherited if "exec" is used:
Name: stunnel
Uid: 996 996 996 996
Gid: 988 988 988 988
...
CapInh: 0000000000000000
CapPrm: 0000000000002000
CapEff: 0000000000002000
---
It would be possible to keep CAP_NET_BIND_SERVICE too, to allow
reloading a configuration that binds to ports below 1024. I don't think
stunnel should keep this capability unless a service needs it at startup.
CREDITS.md | 25 ++++++++++
configure.ac | 33 ++++++++++++++
doc/stunnel.8.in | 2 +-
doc/stunnel.html.in | 2 +-
doc/stunnel.pod.in | 2 +-
src/stunnel.c | 109 ++++++++++++++++++++++++++++++++++++++++++++
6 files changed, 170 insertions(+), 3 deletions(-)
diff --git a/CREDITS.md b/CREDITS.md
index 6567392..5f12f92 100644
--- a/CREDITS.md
+++ b/CREDITS.md
@@ -27,6 +27,31 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+libcap usage in version 5.57 (from sniproxy):
+Copyright 2017 Dustin Lundquist <dustin at null-ptr.net>
+Copyright 2020 Simon Arlott
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE HOLDERS OR
+CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
Several bugfixes and improvements mostly in versions 3.xx:
* Brian Hatch <bri at stunnel.org>
diff --git a/configure.ac b/configure.ac
index 9c2b2b6..f454710 100644
--- a/configure.ac
+++ b/configure.ac
@@ -352,6 +352,39 @@ AC_ARG_ENABLE(systemd,
]
)
+# Disable POSIX capabilities support
+AC_MSG_CHECKING([whether to enable POSIX capabilities support])
+AC_ARG_ENABLE(libcap,
+[ --disable-libcap disable POSIX capabilities support],
+ [
+ case "$enableval" in
+ yes) AC_MSG_RESULT([yes])
+ AC_SEARCH_LIBS([cap_get_proc], [cap])
+ AC_DEFINE([USE_LIBCAP], [1],
+ [Define to 1 to enable POSIX capabilities])
+ ;;
+ no) AC_MSG_RESULT([no])
+ ;;
+ *) AC_MSG_RESULT([error])
+ AC_MSG_ERROR([Bad value \"${enableval}\"])
+ ;;
+ esac
+ ],
+ [
+ AC_MSG_RESULT([autodetecting])
+ AC_SEARCH_LIBS([cap_get_proc], [cap],
+ [ AC_CHECK_HEADERS([sys/capability.h], [
+ AC_DEFINE([USE_LIBCAP], [1],
+ [Define to 1 to enable POSIX capabilities])
+ AC_MSG_NOTICE([libcap support enabled])
+ ], [
+ AC_MSG_NOTICE([libcap header not found])
+ ]) ], [
+ AC_MSG_NOTICE([libcap library not found])
+ ])
+ ]
+)
+
# Disable use of libwrap (TCP wrappers)
# it should be the last check!
AC_MSG_CHECKING([whether to enable TCP wrappers support])
diff --git a/doc/stunnel.8.in b/doc/stunnel.8.in
index b3df016..4b121bc 100644
--- a/doc/stunnel.8.in
+++ b/doc/stunnel.8.in
@@ -1251,7 +1251,7 @@ setuid
.RS 4
.Sp
The use of the 'setuid' option will also prevent \fBstunnel\fR from binding to privileged
-(<1024) ports during configuration reloading.
+(<1024) ports during configuration reloading or enabling of 'transparent = source'.
.Sp
When the 'chroot' option is used, \fBstunnel\fR will look for all its files (including
the configuration file, certificates, the log file and the pid file) within the chroot
diff --git a/doc/stunnel.html.in b/doc/stunnel.html.in
index ee9e5d9..95a1b51 100644
--- a/doc/stunnel.html.in
+++ b/doc/stunnel.html.in
@@ -1506,7 +1506,7 @@
</li>
</ul>
-<p>The use of the 'setuid' option will also prevent <b>stunnel</b> from binding to privileged (<1024) ports during configuration reloading.</p>
+<p>The use of the 'setuid' option will also prevent <b>stunnel</b> from binding to privileged (<1024) ports during configuration reloading or enabling of 'transparent = source'.</p>
<p>When the 'chroot' option is used, <b>stunnel</b> will look for all its files (including the configuration file, certificates, the log file and the pid file) within the chroot jail.</p>
diff --git a/doc/stunnel.pod.in b/doc/stunnel.pod.in
index e9abd4f..8f14fd9 100644
--- a/doc/stunnel.pod.in
+++ b/doc/stunnel.pod.in
@@ -1360,7 +1360,7 @@ setuid
=back
The use of the 'setuid' option will also prevent B<stunnel> from binding to privileged
-(<1024) ports during configuration reloading.
+(<1024) ports during configuration reloading or enabling of 'transparent = source'.
When the 'chroot' option is used, B<stunnel> will look for all its files (including
the configuration file, certificates, the log file and the pid file) within the chroot
diff --git a/src/stunnel.c b/src/stunnel.c
index 8121e0c..b84ce72 100644
--- a/src/stunnel.c
+++ b/src/stunnel.c
@@ -58,6 +58,11 @@
#endif /* USE_WIN32 */
+#ifdef USE_LIBCAP
+#include <sys/prctl.h>
+#include <sys/capability.h>
+#endif
+
/**************************************** prototypes */
#ifdef __INNOTEK_LIBC__
@@ -191,6 +196,59 @@ int drop_privileges(int critical) {
#ifdef HAVE_SETGROUPS
gid_t gr_list[1];
#endif
+#ifdef USE_LIBCAP
+ SERVICE_OPTIONS *opt;
+ int need_caps = 0;
+ cap_t caps;
+ cap_value_t cap_list[1];
+ int keepcaps = 0;
+#endif
+
+#ifdef USE_LIBCAP
+ for (opt = service_options.next; opt; opt = opt->next) {
+ if (opt->option.transparent_src) { /* transparent_src is enabled for this service */
+ need_caps = 1;
+ break;
+ }
+ }
+
+ /* add permitted flag to capability needed for IP_TRANSPARENT */
+ if (CAP_IS_SUPPORTED(CAP_NET_RAW) && need_caps) {
+ const char *cap_failed = NULL;
+
+ caps = cap_get_proc();
+ if (caps != NULL) {
+ cap_list[0] = CAP_NET_RAW;
+ if (cap_set_flag(caps, CAP_PERMITTED, 1, cap_list, CAP_SET) == -1) {
+ cap_failed = "cap_set_flags[before]";
+ goto cap_cleanup_before;
+ }
+
+ if (cap_set_proc(caps) == -1) {
+ cap_failed = "cap_set_proc[before]";
+ goto cap_cleanup_before;
+ }
+
+ /* keep capabilities after setuid */
+ if (prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0) == -1) {
+ cap_failed = "prctl[before]";
+ goto cap_cleanup_before;
+ } else {
+ keepcaps = 1;
+ }
+
+cap_cleanup_before:
+ cap_free(caps);
+ if (cap_failed != NULL && critical) {
+ sockerror(cap_failed);
+ return 1;
+ }
+ } else if (critical) {
+ sockerror("cap_get_proc[before]");
+ return 1;
+ }
+ }
+#endif
/* set uid and gid */
if(service_options.gid) {
@@ -212,6 +270,57 @@ int drop_privileges(int critical) {
return 1;
}
}
+
+#ifdef USE_LIBCAP
+ /* enable capability needed for IP_TRANSPARENT */
+ if (CAP_IS_SUPPORTED(CAP_NET_RAW) && need_caps) {
+ const char *cap_failed = NULL;
+
+ caps = cap_get_proc();
+ if (caps != NULL) {
+ /* remove every capability from the list */
+ cap_clear(caps);
+ cap_list[0] = CAP_NET_RAW;
+
+ /* take back capability */
+ if (cap_set_flag(caps, CAP_PERMITTED, 1, cap_list, CAP_SET) == -1) {
+ cap_failed = "cap_set_flag[after]";
+ goto cap_cleanup_after;
+ }
+
+ if (cap_set_flag(caps, CAP_EFFECTIVE, 1, cap_list, CAP_SET) == -1) {
+ cap_failed = "cap_set_flag[after]";
+ goto cap_cleanup_after;
+ }
+
+ if (cap_set_proc(caps) == -1) {
+ cap_failed = "cap_set_proc[after]";
+ goto cap_cleanup_after;
+ }
+ /* from now on it the new capability list is active,
+ * no other capabilities may be enabled */
+
+cap_cleanup_after:
+ cap_free(caps);
+ if (cap_failed != NULL && critical) {
+ sockerror(cap_failed);
+ return 1;
+ }
+ } else if (critical) {
+ sockerror("cap_get_proc[after]");
+ return 1;
+ }
+ }
+
+ /* disable keeping capabilities across setuid */
+ if (keepcaps && prctl(PR_SET_KEEPCAPS, 0, 0, 0, 0) == -1) {
+ if (critical) {
+ sockerror("prctl");
+ return 1;
+ }
+ }
+#endif
+
#endif /* standard Unix */
return 0;
}
--
2.17.1
--
Simon Arlott
More information about the stunnel-users
mailing list