The "User" config setting is not sufficient for
contrib/linux-tor-prio.sh's UID-based traffic prioritization since it
sets the UID after the sockets are created.  The UID is set after
sockets are created so that they can be bound to ports less than 1024.

Processes with Linux's CAP_NET_BIND_SERVICES capability can bind to
ports less than 1024 without having to run as root.

If possible, keep this capability and switch UID before opening the
sockets.  This allows both UID-based traffic prioritization to work
and binding to ports less than 1024.
---
 configure.ac        |    1 +
 src/common/compat.c |   37 +++++++++++++++++++++++++++++++++++++
 src/common/compat.h |    3 +++
 src/or/config.c     |   13 +++++++++++++
 4 files changed, 54 insertions(+)

diff --git a/configure.ac b/configure.ac
index 182cc1b..c1a791a 100644
--- a/configure.ac
+++ b/configure.ac
@@ -781,6 +781,7 @@ AC_CHECK_HEADERS(
         netinet/in6.h \
         pwd.h \
         stdint.h \
+        sys/capabilities.h \
         sys/file.h \
         sys/ioctl.h \
         sys/limits.h \
diff --git a/src/common/compat.c b/src/common/compat.c
index 3b15f8a..0d9d706 100644
--- a/src/common/compat.c
+++ b/src/common/compat.c
@@ -104,6 +104,9 @@
 /* Only use the linux prctl;  the IRIX prctl is totally different */
 #include <sys/prctl.h>
 #endif
+#ifdef HAVE_SYS_CAPABILITIES_H
+#include <sys/capabilities.h>
+#endif
 
 #include "torlog.h"
 #include "util.h"
@@ -1469,6 +1472,40 @@ log_credential_status(void)
 }
 #endif
 
+#ifdef HAVE_SYS_CAPABILITIES_H
+/** Keep the CAP_NET_BIND_SERVICE capability to allow ports less than 1024
+ * to be opened.  Return 0 on success.  On failure, log and return -1.
+ */
+int
+keep_cap_net_bind_service(void)
+{
+  cap_user_header_t hdr;
+  cap_user_data_t data;
+
+  /* Get the capabilities or bail out */
+  memset(&hdr, 0, sizeof(hdr));
+  hdr.version = _LINUX_CAPABILITY_VERSION;
+  if (capget(&hdr, &data)) {
+    log_warn("Error getting capabilities: %s", strerror(errno));
+    return -1;
+  }
+
+  /* Set the capabilities or bail out */
+  data.permitted &= ~CAP_TO_MASK(CAP_NET_BIND_SERVICE);
+  data.inheritable = 0;
+  if (capset(&hdr, &data)) {
+    log_warn("Error setting capabilities: %s", strerror(errno));
+    return -1;
+  }
+
+  /* Keep the capabilities or bail out */
+  if (prctl(PR_SET_KEEPCAPS, 1)) {
+    log_warn("Error keeping capabilities: %s", strerror(errno));
+    return -1;
+  }
+}
+#endif
+
 /** Call setuid and setgid to run as <b>user</b> and switch to their
  * primary group.  Return 0 on success.  On failure, log and return -1.
  */
diff --git a/src/common/compat.h b/src/common/compat.h
index d2944e6..0145d85 100644
--- a/src/common/compat.h
+++ b/src/common/compat.h
@@ -611,6 +611,9 @@ typedef unsigned long rlim_t;
 #endif
 int set_max_file_descriptors(rlim_t limit, int *max);
 int tor_disable_debugger_attach(void);
+#ifdef HAVE_SYS_CAPABILITIES_H
+int keep_cap_net_bind_service(void);
+#endif
 int switch_id(const char *user);
 #ifdef HAVE_PWD_H
 char *get_user_homedir(const char *username);
diff --git a/src/or/config.c b/src/or/config.c
index 31695ba..6e753dd 100644
--- a/src/or/config.c
+++ b/src/or/config.c
@@ -983,6 +983,19 @@ options_act_reversible(const or_options_t *old_options, 
char **msg)
   }
 #endif
 
+#ifdef HAVE_SYS_CAPABILITIES_H
+  /* Setuid/setgid as appropriate */
+  if (options->User) {
+    if (!keep_cap_net_bind_service ()) {
+      if (switch_id(options->User) != 0) {
+       /* No need to roll back, since you can't change the value. */
+       *msg = tor_strdup("Problem with User value. See logs for details.");
+       goto done;
+      }
+    }
+  }
+#endif
+
   if (running_tor) {
     int n_ports=0;
     /* We need to set the connection limit before we can open the listeners. */
-- 
1.7.10.4


-- 
To UNSUBSCRIBE, email to debian-bugs-dist-requ...@lists.debian.org
with a subject of "unsubscribe". Trouble? Contact listmas...@lists.debian.org

Reply via email to