[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