TS-3103: improve privilege elevation

Remove duplicate user credential routines in favor of a single
ImpersonateUser function. This correctly dealt with real and effective
credentials, supplementary groups and preserving additional process
flags.

Set and preserve PR_SET_PDEATHSIG on Linux so that killing traffic_cop
correctly brings down the rest of the system.

Log and ignore proxy.config.ssl.cert.load_elevated and
proxy.config.plugin.load_elevated unless POSIX capabilities are
enabled.


Project: http://git-wip-us.apache.org/repos/asf/trafficserver/repo
Commit: http://git-wip-us.apache.org/repos/asf/trafficserver/commit/33f651c9
Tree: http://git-wip-us.apache.org/repos/asf/trafficserver/tree/33f651c9
Diff: http://git-wip-us.apache.org/repos/asf/trafficserver/diff/33f651c9

Branch: refs/heads/master
Commit: 33f651c90f1832188408d859ab59c03ad2b3de01
Parents: 0f0c163
Author: James Peach <[email protected]>
Authored: Tue Sep 30 13:19:47 2014 -0700
Committer: James Peach <[email protected]>
Committed: Thu Oct 2 16:51:45 2014 -0700

----------------------------------------------------------------------
 CHANGES                                         |   2 +
 cmd/traffic_cop/traffic_cop.cc                  |   3 +
 cmd/traffic_manager/traffic_manager.cc          |  67 +----
 configure.ac                                    |   1 +
 .../configuration/records.config.en.rst         |   6 +-
 iocore/net/SSLUtils.cc                          |   2 +
 lib/ts/ink_cap.cc                               | 301 ++++++++++++++-----
 lib/ts/ink_cap.h                                |  20 +-
 mgmt/LocalManager.cc                            |   2 +
 mgmt/RecordsConfig.cc                           |   4 +-
 proxy/Main.cc                                   | 109 +++----
 proxy/Plugin.cc                                 |   3 +
 proxy/http/remap/RemapConfig.cc                 |   4 +-
 13 files changed, 301 insertions(+), 223 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/trafficserver/blob/33f651c9/CHANGES
----------------------------------------------------------------------
diff --git a/CHANGES b/CHANGES
index 2184733..2747e8f 100644
--- a/CHANGES
+++ b/CHANGES
@@ -1,6 +1,8 @@
                                                          -*- coding: utf-8 -*-
 Changes with Apache Traffic Server 5.2.0
 
+  *) [TS-3103] Improve privilege elevation.
+
   *) [TS-3044] Use eventfd in AIO_MODE_NATIVE if available.
 
   *) [TS-3108] Add port matching condition to header_rewrite.

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/33f651c9/cmd/traffic_cop/traffic_cop.cc
----------------------------------------------------------------------
diff --git a/cmd/traffic_cop/traffic_cop.cc b/cmd/traffic_cop/traffic_cop.cc
index 4d711c7..d93a0a3 100644
--- a/cmd/traffic_cop/traffic_cop.cc
+++ b/cmd/traffic_cop/traffic_cop.cc
@@ -27,6 +27,7 @@
 #include "I_RecCore.h"
 #include "mgmtapi.h"
 #include "ClusterCom.h"
+#include "ink_cap.h"
 
 #include <string>
 #include <map>
@@ -732,6 +733,8 @@ spawn_manager()
       close(log_fd);
     }
 
+    EnableDeathSignal(SIGTERM);
+
     err = execv(prog, options);
     cop_log_trace("Somehow execv(%s, options, NULL) failed (%d)!\n", prog, 
err);
     exit(1);

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/33f651c9/cmd/traffic_manager/traffic_manager.cc
----------------------------------------------------------------------
diff --git a/cmd/traffic_manager/traffic_manager.cc 
b/cmd/traffic_manager/traffic_manager.cc
index d3870f5..11227fb 100644
--- a/cmd/traffic_manager/traffic_manager.cc
+++ b/cmd/traffic_manager/traffic_manager.cc
@@ -60,7 +60,7 @@ LocalManager *lmgmt = NULL;
 FileManager *configFiles;
 
 static void fileUpdated(char *fname, bool incVersion);
-static void runAsUser(char *userName);
+static void runAsUser(const char *userName);
 static void printUsage(void);
 
 #if defined(freebsd)
@@ -1056,69 +1056,10 @@ restoreCapabilities() {
 //  If we are not root, do nothing
 //
 void
-runAsUser(char *userName)
+runAsUser(const char * userName)
 {
-  uid_t uid, euid;
-  struct passwd *result;
-  const int bufSize = 1024;
-  char buf[bufSize];
-
-  uid = getuid();
-  euid = geteuid();
-
-  if (uid == 0 || euid == 0) {
-
-    /* Figure out what user we should run as */
-
-    Debug("lm", "[runAsUser] Attempting to run as user '%s'\n", userName);
-
-    if (userName == NULL || userName[0] == '\0') {
-      mgmt_elog(stderr, 0, "[runAsUser] Fatal Error: 
proxy.config.admin.user_id is not set\n");
-      _exit(1);
-    }
-
-    struct passwd passwdInfo;
-    struct passwd *ppasswd = NULL;
-    result = NULL;
-    int res;
-    if (*userName == '#') {
-      int uuid = atoi(userName + 1);
-      if (uuid == -1)
-        uuid = (int)uid;
-      res = getpwuid_r((uid_t)uuid, &passwdInfo, buf, bufSize, &ppasswd);
-    }
-    else {
-      res = getpwnam_r(&userName[0], &passwdInfo, buf, bufSize, &ppasswd);
-    }
-
-    if (!res && ppasswd) {
-      result = ppasswd;
-    }
-
-    if (result == NULL) {
-      mgmt_elog(stderr, 0, "[runAsUser] Fatal Error: Unable to get info about 
user %s : %s\n", userName, strerror(errno));
-      _exit(1);
-    }
-
-    if (setegid(result->pw_gid) != 0 || seteuid(result->pw_uid) != 0) {
-      mgmt_elog(stderr, 0, "[runAsUser] Fatal Error: Unable to switch to user 
%s : %s\n", userName, strerror(errno));
-      _exit(1);
-    }
-
-    uid = getuid();
-    euid = geteuid();
-
-    Debug("lm", "[runAsUser] Running with uid: '%d' euid: '%d'\n", uid, euid);
-
-    if (uid != result->pw_uid && euid != result->pw_uid) {
-      mgmt_elog(stderr, 0, "[runAsUser] Fatal Error: Failed to switch to user 
%s\n", userName);
-      _exit(1);
-    }
-
-    // setup supplementary groups if it is not set.
-    if (0 == getgroups(0, NULL)) {
-      initgroups(&userName[0],result->pw_gid);
-    }
+  if (getuid() == 0 || geteuid() == 0) {
+    ImpersonateUser(userName, IMPERSONATE_EFFECTIVE);
 
 #if TS_USE_POSIX_CAP
     if (0 != restoreCapabilities()) {

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/33f651c9/configure.ac
----------------------------------------------------------------------
diff --git a/configure.ac b/configure.ac
index 3c30f2f..ed54039 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1163,6 +1163,7 @@ fi
 
 AC_CHECK_FUNCS([clock_gettime kqueue epoll_ctl posix_memalign posix_fadvise 
posix_madvise posix_fallocate inotify_init])
 AC_CHECK_FUNCS([lrand48_r srand48_r port_create strlcpy strlcat sysconf 
getpagesize])
+AC_CHECK_FUNCS([getreuid getresuid getresgid setreuid setresuid])
 
 # Check for eventfd() and sys/eventfd.h (both must exist ...)
 AC_CHECK_HEADERS([sys/eventfd.h], [

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/33f651c9/doc/reference/configuration/records.config.en.rst
----------------------------------------------------------------------
diff --git a/doc/reference/configuration/records.config.en.rst 
b/doc/reference/configuration/records.config.en.rst
index 7776e6e..f0d8f26 100644
--- a/doc/reference/configuration/records.config.en.rst
+++ b/doc/reference/configuration/records.config.en.rst
@@ -1984,9 +1984,10 @@ Diagnostic Logging Configuration
 ============  =====================================================
 Tag           Subsytem usage
 ============  =====================================================
-ssl           TLS termination and certificate processing
 dns           DNS query resolution
 http_hdrs     Logs the headers for HTTP requests and responses
+privileges    Privilege elevation
+ssl           TLS termination and certificate processing
 ============  =====================================================
 
   Traffic Server plugins will typically log debug messages using
@@ -2197,6 +2198,9 @@ SSL Termination
   certificate files' access rights can be restricted to help reduce the
   vulnerability of certificates.
 
+  This feature requires Traffic Server to be built with POSIX
+  capabilities enabled.
+
 Client-Related Configuration
 ----------------------------
 

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/33f651c9/iocore/net/SSLUtils.cc
----------------------------------------------------------------------
diff --git a/iocore/net/SSLUtils.cc b/iocore/net/SSLUtils.cc
index c399be7..7428816 100644
--- a/iocore/net/SSLUtils.cc
+++ b/iocore/net/SSLUtils.cc
@@ -1541,10 +1541,12 @@ SSLParseCertificateConfiguration(
     return false;
   }
 
+#if TS_USE_POSIX_CAP
   // elevate/allow file access to root read only files/certs
   uint32_t elevate_setting = 0;
   REC_ReadConfigInteger(elevate_setting, 
"proxy.config.ssl.cert.load_elevated");
   ElevateAccess elevate_access(elevate_setting != 0); // destructor will 
demote for us
+#endif /* TS_USE_POSIX_CAP */
 
   line = tokLine(file_buf, &tok_state);
   while (line != NULL) {

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/33f651c9/lib/ts/ink_cap.cc
----------------------------------------------------------------------
diff --git a/lib/ts/ink_cap.cc b/lib/ts/ink_cap.cc
index 2666f61..228d027 100644
--- a/lib/ts/ink_cap.cc
+++ b/lib/ts/ink_cap.cc
@@ -26,6 +26,8 @@
 # include "ink_cap.h"
 # include "ink_thread.h"
 
+#include <grp.h>
+
 #if HAVE_SYS_CAPABILITY_H
 #include <sys/capability.h>
 #endif
@@ -34,10 +36,92 @@
 #include <sys/prctl.h>
 #endif
 
+// NOTE: Failing to acquire or release privileges is a fatal error. This is 
because that should never happen
+// and if it does, it is likely that some fundamental security assumption has 
been violated. In that case
+// it is dangerous to continue.
+
 # if !TS_USE_POSIX_CAP
 ink_mutex ElevateAccess::lock = INK_MUTEX_INIT;
 #endif
 
+#define DEBUG_CREDENTIALS(tag)  do { \
+  if (is_debug_tag_set(tag)) { \
+    uid_t uid = -1, euid = -1, suid = -1; \
+    gid_t gid = -1, egid = -1, sgid = -1; \
+    getresuid(&uid, &euid, &suid); \
+    getresgid(&gid, &egid, &sgid); \
+    Debug(tag, "uid=%ld, gid=%ld, euid=%ld, egid=%ld, suid=%ld, sgid=%ld", \
+        static_cast<long>(uid), \
+        static_cast<long>(gid), \
+        static_cast<long>(euid), \
+        static_cast<long>(egid), \
+        static_cast<long>(suid), \
+        static_cast<long>(sgid) ); \
+  } \
+} while (0)
+
+#if TS_USE_POSIX_CAP
+
+#define DEBUG_PRIVILEGES(tag)  do { \
+  if (is_debug_tag_set(tag)) { \
+    cap_t caps = cap_get_proc(); \
+    char* caps_text = cap_to_text(caps, NULL); \
+    Debug(tag, "caps='%s', core=%s, death signal=%d, thread=0x%llx", \
+        caps_text, \
+        is_dumpable(), \
+        death_signal(), \
+        (unsigned long long)pthread_self() ); \
+    cap_free(caps_text); \
+    cap_free(caps); \
+  } \
+} while (0)
+
+#else /* TS_USE_POSIX_CAP */
+
+#define DEBUG_PRIVILEGES(tag)  do { \
+  if (is_debug_tag_set(tag)) { \
+    Debug(tag, "caps='', core=%s, death signal=%d, thread=0x%llx", \
+        is_dumpable(), \
+        death_signal(), \
+        (unsigned long long)pthread_self() ); \
+  } \
+} while(0)
+
+#endif /* TS_USE_POSIX_CAP */
+
+#if !HAVE_GETRESUID
+static int
+getresuid(uid_t * uid, uid_t * euid, uid_t * suid)
+{
+  *uid = getuid();
+  *euid = geteuid();
+  return 0;
+}
+#endif /* !HAVE_GETRESUID */
+
+#if !HAVE_GETRESGID
+static int
+getresgid(gid_t * gid, gid_t * egid, gid_t * sgid)
+{
+  *gid = getgid();
+  *egid = getegid();
+  return 0;
+}
+#endif /* !HAVE_GETRESGID */
+
+static unsigned
+max_passwd_size()
+{
+#if defined(_SC_GETPW_R_SIZE_MAX)
+  long val = sysconf(_SC_GETPW_R_SIZE_MAX);
+  if (val > 0) {
+    return (unsigned)val;
+  }
+#endif
+
+  return 4096;
+}
+
 static const char *
 is_dumpable()
 {
@@ -63,48 +147,120 @@ death_signal()
 void
 DebugCapabilities(char const* tag)
 {
-  if (is_debug_tag_set(tag)) {
-#if TS_USE_POSIX_CAP
-    cap_t caps = cap_get_proc();
-    char* caps_text = cap_to_text(caps, 0);
-
-    Debug(tag, "uid=%u, gid=%u, euid=%u, egid=%u, caps=%s, core=%s, death 
signal=%d, thread=0x%llx",
-        static_cast<unsigned int>(getuid()),
-        static_cast<unsigned int>(getgid()),
-        static_cast<unsigned int>(geteuid()),
-        static_cast<unsigned int>(getegid()),
-        caps_text,
-        is_dumpable(),
-        death_signal(),
-        (unsigned long long)pthread_self() );
-    cap_free(caps_text);
-    cap_free(caps);
-#else
-    Debug(tag, "uid=%u, gid=%u, euid=%u, egid=%u, core=%s, death signal=%d, 
thread=0x%llx",
-        static_cast<unsigned int>(getuid()),
-        static_cast<unsigned int>(getgid()),
-        static_cast<unsigned int>(geteuid()),
-        static_cast<unsigned int>(getegid()),
-        is_dumpable(),
-        death_signal(),
-        (unsigned long long)pthread_self() );
+  DEBUG_CREDENTIALS(tag);
+  DEBUG_PRIVILEGES(tag);
+}
+
+static void
+impersonate(const struct passwd * pwd, ImpersonationLevel level)
+{
+  int deathsig = death_signal();
+  bool dumpable = false;
+
+  DEBUG_CREDENTIALS("privileges");
+  DEBUG_PRIVILEGES("privileges");
+
+#if defined(PR_GET_DUMPABLE)
+  dumpable = (prctl(PR_GET_DUMPABLE) == 1);
 #endif
+
+  // Always repopulate the supplementary group list for the new user.
+  initgroups(pwd->pw_name, pwd->pw_gid);
+
+  switch (level) {
+  case IMPERSONATE_PERMANENT:
+    if (setregid(pwd->pw_gid, pwd->pw_gid) != 0) {
+      Fatal("switching to user %s, failed to set group ID %ld", pwd->pw_name, 
(long)pwd->pw_gid);
+    }
+
+    if (setreuid(pwd->pw_uid, pwd->pw_uid) != 0) {
+      Fatal("switching to user %s, failed to set user ID %ld", pwd->pw_name, 
(long)pwd->pw_uid);
+    }
+    break;
+
+  case IMPERSONATE_EFFECTIVE:
+    if (setegid(pwd->pw_gid) != 0) {
+      Fatal("switching to user %s, failed to set group ID %ld", pwd->pw_name, 
(long)pwd->pw_gid);
+    }
+
+    if (seteuid(pwd->pw_uid) != 0) {
+      Fatal("switching to user %s, failed to set effective user ID %ld", 
pwd->pw_name, (long)pwd->pw_gid);
+    }
+    break;
+  }
+
+  // Reset process flags if necessary. Elevating privilege using capabilities 
does not reset process
+  // flags, so we don't have to bother with this in elevateFileAccess().
+
+  EnableCoreFile(dumpable);
+
+  if (deathsig > 0) {
+    EnableDeathSignal(deathsig);
+  }
+
+  DEBUG_CREDENTIALS("privileges");
+  DEBUG_PRIVILEGES("privileges");
+}
+
+void
+ImpersonateUserID(uid_t uid, ImpersonationLevel level)
+{
+  struct passwd * pwd;
+  struct passwd   pbuf;
+  char            buf[max_passwd_size()];
+
+  if (getpwuid_r(uid, &pbuf, buf, sizeof(buf), &pwd) != 0) {
+    Fatal("missing password database entry for UID %ld: %s", (long)uid, 
strerror(errno));
+  }
+
+  if (pwd == NULL) {
+    // Password entry not found ...
+    Fatal("missing password database entry for UID %ld", (long)uid);
+  }
+
+  impersonate(pwd, level);
+}
+
+void
+ImpersonateUser(const char * user, ImpersonationLevel level)
+{
+  struct passwd * pwd;
+  struct passwd   pbuf;
+  char            buf[max_passwd_size()];
+
+  if (*user == '#') {
+    // Numeric user notation.
+    uid_t uid = (uid_t)atoi(&user[1]);
+    if (getpwuid_r(uid, &pbuf, buf, sizeof(buf), &pwd) != 0) {
+      Fatal("missing password database entry for UID %ld: %s", (long)uid, 
strerror(errno));
+    }
+  } else {
+    if (getpwnam_r(user, &pbuf, buf, sizeof(buf), &pwd) != 0) {
+      Fatal("missing password database entry for username '%s': %s", user, 
strerror(errno));
+    }
   }
+
+  if (pwd == NULL) {
+    // Password entry not found ...
+    Fatal("missing password database entry for '%s'", user);
+  }
+
+  impersonate(pwd, level);
 }
 
-int
+bool
 PreserveCapabilities()
 {
   int zret = 0;
 # if TS_USE_POSIX_CAP
-    zret = prctl(PR_SET_KEEPCAPS, 1);
+  zret = prctl(PR_SET_KEEPCAPS, 1);
 # endif
-  Debug("proxy_priv", "[PreserveCapabilities] zret : %d\n", zret);
-  return zret;
+  Debug("privileges", "[PreserveCapabilities] zret : %d\n", zret);
+  return zret == 0;
 }
 
 // Adjust the capabilities to only those needed.
-int
+bool
 RestrictCapabilities()
 {
   int zret = 0; // return value.
@@ -121,11 +277,11 @@ RestrictCapabilities()
     zret = cap_set_proc(caps);
     cap_free(caps);
 #  endif
-  Debug("proxy_priv", "[RestrictCapabilities] zret : %d\n", zret);
-  return zret;
+  Debug("privileges", "[RestrictCapabilities] zret : %d\n", zret);
+  return zret == 0;
 }
 
-int
+bool
 EnableCoreFile(bool flag)
 {
   int zret = 0;
@@ -140,8 +296,20 @@ EnableCoreFile(bool flag)
   }
 # endif  // linux check
 
-  Debug("proxy_priv", "[EnableCoreFile] zret : %d\n", zret);
-  return zret;
+  Debug("privileges", "[EnableCoreFile] zret : %d\n", zret);
+  return zret == 0;
+}
+
+void
+EnableDeathSignal(int signum)
+{
+  (void)signum;
+
+#if defined(PR_SET_PDEATHSIG)
+  if (prctl(PR_SET_PDEATHSIG, signum, 0, 0, 0) != 0) {
+    Debug("privileges", "prctl(PR_SET_PDEATHSIG) failed: %s", strerror(errno));
+  }
+#endif
 }
 
 #if TS_USE_POSIX_CAP
@@ -158,65 +326,34 @@ EnableCoreFile(bool flag)
     current enable this feature so it's not actually called. Still,
     best to program defensively and have it available.
  */
-static bool
+static void
 elevateFileAccess(bool state)
 {
-  Debug("proxy_priv", "[elevateFileAccess] state : %d\n", state);
+  Debug("privileges", "[elevateFileAccess] state : %d\n", state);
 
-  bool zret = false; // return value.
   cap_t cap_state = cap_get_proc(); // current capabilities
   // Make a list of the capabilities we changed.
   cap_value_t cap_list[] = { CAP_DAC_OVERRIDE };
   static int const CAP_COUNT = sizeof(cap_list)/sizeof(*cap_list);
 
   cap_set_flag(cap_state, CAP_EFFECTIVE, CAP_COUNT, cap_list, state ? CAP_SET 
: CAP_CLEAR);
-  zret = (0 == cap_set_proc(cap_state));
-  cap_free(cap_state);
-  Debug("proxy_priv", "[elevateFileAccess] zret : %d\n", zret);
-  return zret;
-}
-#else
-//  bool removeRootPriv()
-//
-//    - Returns true on success
-//      and false on failure
-static bool
-removeRootPriv(uid_t euid)
-{
-  if (seteuid(euid) < 0) {
-    Debug("proxy_priv", "[removeRootPriv] seteuid failed : %s\n", 
strerror(errno));
-    return false;
+  if (cap_set_proc(cap_state) != 0) {
+    Fatal("failed to %s privileged capabilities: %s", state ? "acquire" : 
"release", strerror(errno));
   }
 
-  Debug("proxy_priv", "[removeRootPriv] removed root privileges.  Euid is 
%d\n", euid);
-  return true;
-}
-
-//  bool restoreRootPriv()
-//
-//    - Returns true on success
-//      and false on failure
-static bool
-restoreRootPriv(uid_t *old_euid)
-{
-  if (old_euid)
-    *old_euid = geteuid();
-  if (seteuid(0) < 0) {
-    Debug("proxy_priv", "[restoreRootPriv] seteuid root failed : %s\n", 
strerror(errno));
-    return false;
-  }
-
-  Debug("proxy_priv", "[restoreRootPriv] restored root privileges.  Euid is 
%d\n", 0);
-
-  return true;
+  cap_free(cap_state);
 }
 #endif
 
 ElevateAccess::ElevateAccess(const bool state)
-  : elevated(false), saved_uid(0)
+  : elevated(false), saved_uid(geteuid())
 {
   if (state == true) {
     elevate();
+#if !TS_USE_POSIX_CAP
+    DEBUG_CREDENTIALS("privileges");
+#endif
+    DEBUG_PRIVILEGES("privileges");
   }
 }
 
@@ -224,6 +361,10 @@ ElevateAccess::~ElevateAccess()
 {
   if (elevated == true) {
     demote();
+#if !TS_USE_POSIX_CAP
+    DEBUG_CREDENTIALS("privileges");
+#endif
+    DEBUG_PRIVILEGES("privileges");
   }
 }
 
@@ -231,12 +372,12 @@ void
 ElevateAccess::elevate()
 {
 #if TS_USE_POSIX_CAP
-  ink_release_assert(elevateFileAccess(true));
+  elevateFileAccess(true);
 #else
   // Since we are setting a process-wide credential, we have to block any 
other thread
   // attempting to elevate until this one demotes.
   ink_mutex_acquire(&lock);
-  restoreRootPriv(&saved_uid);
+  ImpersonateUserID(0, IMPERSONATE_EFFECTIVE);
 #endif
   elevated = true;
 }
@@ -245,9 +386,9 @@ void
 ElevateAccess::demote()
 {
 #if TS_USE_POSIX_CAP
-  ink_release_assert(elevateFileAccess(false));
+  elevateFileAccess(false);
 #else
-  removeRootPriv(saved_uid);
+  ImpersonateUserID(saved_uid, IMPERSONATE_EFFECTIVE);
   ink_mutex_release(&lock);
 #endif
   elevated = false;

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/33f651c9/lib/ts/ink_cap.h
----------------------------------------------------------------------
diff --git a/lib/ts/ink_cap.h b/lib/ts/ink_cap.h
index e162030..344c5fe 100644
--- a/lib/ts/ink_cap.h
+++ b/lib/ts/ink_cap.h
@@ -30,21 +30,29 @@ extern void DebugCapabilities(
   char const* tag ///< Debug message tag.
 );
 /// Set capabilities to persist across change of user id.
-/// @return 0 on success, non-zero otherwise.
-extern int PreserveCapabilities();
+/// @return true on success
+extern bool PreserveCapabilities();
 /// Initialize and restrict the capabilities of a thread.
-/// @return 0 on success, non-zero otherwise.
-extern int RestrictCapabilities();
+/// @return true on success
+extern bool RestrictCapabilities();
 
 /** Control generate of core file on crash.
     @a flag sets whether core files are enabled on crash.
-    @return 0 on success, @c errno on failre.
+    @return true on success
  */
-extern int EnableCoreFile(
+extern bool EnableCoreFile(
   bool flag ///< New enable state.
 );
 
+void EnableDeathSignal(int signum);
 
+enum ImpersonationLevel {
+  IMPERSONATE_EFFECTIVE,  // Set the effective credential set.
+  IMPERSONATE_PERMANENT   // Set the real credential (permanently).
+};
+
+void ImpersonateUser(const char * user, ImpersonationLevel level);
+void ImpersonateUserID(uid_t user, ImpersonationLevel level);
 
 class ElevateAccess {
 public:

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/33f651c9/mgmt/LocalManager.cc
----------------------------------------------------------------------
diff --git a/mgmt/LocalManager.cc b/mgmt/LocalManager.cc
index 936e5ea..4806673 100644
--- a/mgmt/LocalManager.cc
+++ b/mgmt/LocalManager.cc
@@ -1014,6 +1014,8 @@ LocalManager::startProxy()
       mgmt_fatal(stderr, 0, "[LocalManager::startProxy] ts options must 
contain -M");
     }
 
+    EnableDeathSignal(SIGTERM);
+
     res = execv(absolute_proxy_binary, options);
     mgmt_elog(stderr, errno, "[LocalManager::startProxy] Exec of %s failed\n", 
absolute_proxy_binary);
     _exit(res);

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/33f651c9/mgmt/RecordsConfig.cc
----------------------------------------------------------------------
diff --git a/mgmt/RecordsConfig.cc b/mgmt/RecordsConfig.cc
index 0146cf9..6455831 100644
--- a/mgmt/RecordsConfig.cc
+++ b/mgmt/RecordsConfig.cc
@@ -1344,7 +1344,7 @@ RecordElement RecordsConfig[] = {
   ,
   {RECT_CONFIG, "proxy.config.icp.default_reply_port", RECD_INT, "0", 
RECU_DYNAMIC, RR_NULL, RECC_NULL, NULL, RECA_NULL}
   ,
-  {RECT_CONFIG, "proxy.config.ssl.cert.load_elevated", RECD_INT, "0", 
RECU_NULL, RR_NULL, RECC_NULL, "[0-1]", RECA_NULL}
+  {RECT_CONFIG, "proxy.config.ssl.cert.load_elevated", RECD_INT, "0", 
RECU_RESTART_TS, RR_NULL, RECC_INT, "[0-1]", RECA_READ_ONLY}
   ,
 
   
//############################################################################
@@ -1385,7 +1385,7 @@ RecordElement RecordsConfig[] = {
   ,
   {RECT_CONFIG, "proxy.config.plugin.plugin_mgmt_dir", RECD_STRING, 
TS_BUILD_SYSCONFDIR "/plugins_mgmt", RECU_NULL, RR_NULL, RECC_NULL, NULL, 
RECA_NULL}
   ,
-  {RECT_CONFIG, "proxy.config.plugin.load_elevated", RECD_INT, "0", RECU_NULL, 
RR_NULL, RECC_NULL, "[0-1]", RECA_NULL}
+  {RECT_CONFIG, "proxy.config.plugin.load_elevated", RECD_INT, "0", 
RECU_RESTART_TS, RR_NULL, RECC_INT, "[0-1]", RECA_READ_ONLY}
   ,
 
 

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/33f651c9/proxy/Main.cc
----------------------------------------------------------------------
diff --git a/proxy/Main.cc b/proxy/Main.cc
index c91969b..9bb465e 100644
--- a/proxy/Main.cc
+++ b/proxy/Main.cc
@@ -302,6 +302,11 @@ initialize_process_manager()
     remote_management_flag = true;
   }
 
+  if (remote_management_flag) {
+    // We are being managed by traffic_manager, TERM ourselves if it goes away.
+    EnableDeathSignal(SIGTERM);
+  }
+
   RecProcessInit(remote_management_flag ? RECM_CLIENT : RECM_STAND_ALONE, 
diags);
 
   if (!remote_management_flag) {
@@ -1167,77 +1172,46 @@ adjust_num_of_net_threads(int nthreads)
  * @param user User name in the passwd file to change the uid and gid to.
  */
 static void
-change_uid_gid(const char *user)
+change_uid_gid(const char * user)
 {
-  struct passwd pwbuf;
-  struct passwd *pwbufp = NULL;
-#if defined(freebsd) // TODO: investigate sysconf(_SC_GETPW_R_SIZE_MAX)) 
failure
-  long buflen = 1024; // or 4096?
-#else
-  long buflen = sysconf(_SC_GETPW_R_SIZE_MAX);
-#endif
-  if (buflen < 0) {
-    ink_fatal_die("sysconf() failed for _SC_GETPW_R_SIZE_MAX");
+#if !TS_USE_POSIX_CAP
+  RecInt enabled;
+
+  if (RecGetRecordInt("proxy.config.ssl.cert.load_elevated", &enabled) == 
REC_ERR_OKAY && enabled) {
+    Warning("ignoring proxy.config.ssl.cert.load_elevated because Traffic 
Server was built without POSIX capabilities support");
   }
 
-  char *buf = (char *)ats_malloc(buflen);
+  if (RecGetRecordInt("proxy.config.plugin.load_elevated", &enabled) == 
REC_ERR_OKAY && enabled) {
+    Warning("ignoring proxy.config.plugin.load_elevated because Traffic Server 
was built without POSIX capabilities support");
+  }
+#endif /* TS_USE_POSIX_CAP */
+
+  // This is primarily for regression tests, where people just run 
"traffic_server -R1" as a regular user. Dropping
+  // privilege is never going to succeed unless we were privileged in the 
first place. I guess we ought to check
+  // capabilities as well :-/
+  if (getuid() != 0 && geteuid() != 0) {
+    Note("Traffic Server is running unprivileged, not switching to user '%s'", 
user);
+    return;
+  }
 
-  if (0 != geteuid() && 0 == getuid())
-    ATS_UNUSED_RETURN(seteuid(0)); // revert euid if possible.
-  if (0 != geteuid()) {
-    // Not root so can't change user ID. Logging isn't operational yet so
-    // we have to write directly to stderr. Perhaps this should be fatal?
-    fprintf(stderr,
-          "Can't change user to '%s' because running with effective uid=%d\n",
-          user, geteuid());
+  Debug("privileges", "switching to unprivileged user '%s'", user);
+  ImpersonateUser(user, IMPERSONATE_PERMANENT);
+
+#if !defined(BIG_SECURITY_HOLE) || (BIG_SECURITY_HOLE != 0)
+  if (getuid() == 0 || geteuid() == 0) {
+    ink_fatal_die(
+      "Trafficserver has not been designed to serve pages while\n"
+      "\trunning as root. There are known race conditions that\n"
+      "\twill allow any local user to read any file on the system.\n"
+      "\tIf you still desire to serve pages as root then\n"
+      "\tadd -DBIG_SECURITY_HOLE to the CFLAGS env variable\n"
+      "\tand then rebuild the server.\n"
+      "\tIt is strongly suggested that you instead modify the\n"
+      "\tproxy.config.admin.user_id directive in your\n"
+      "\trecords.config file to list a non-root user.\n");
   }
-  else {
-    if (user[0] == '#') {
-      // numeric user notation
-      uid_t uid = (uid_t)atoi(&user[1]);
-      getpwuid_r(uid, &pwbuf, buf, buflen, &pwbufp);
-    }
-    else {
-      // read the entry from the passwd file
-      getpwnam_r(user, &pwbuf, buf, buflen, &pwbufp);
-    }
-    // check to see if we found an entry
-    if (pwbufp == NULL) {
-      ink_fatal_die("Can't find entry in password file for user: %s", user);
-    }
-#if !defined (BIG_SECURITY_HOLE)
-    if (pwbuf.pw_uid == 0) {
-      ink_fatal_die("Trafficserver has not been designed to serve pages 
while\n"
-        "\trunning as root.  There are known race conditions that\n"
-        "\twill allow any local user to read any file on the system.\n"
-        "\tIf you still desire to serve pages as root then\n"
-        "\tadd -DBIG_SECURITY_HOLE to the CFLAGS env variable\n"
-        "\tand then rebuild the server.\n"
-        "\tIt is strongly suggested that you instead modify the\n"
-        "\tproxy.config.admin.user_id  directive in your\n"
-        "\trecords.config file to list a non-root user.\n");
-    }
 #endif
-    // change the gid to passwd entry if we are not already running as that gid
-    if (getgid() != pwbuf.pw_gid) {
-      if (setgid(pwbuf.pw_gid) != 0) {
-        ink_fatal_die("Can't change group to user: %s, gid: %d",
-                      user, pwbuf.pw_gid);
-      }
-    }
-    // change the uid to passwd entry if we are not already running as that uid
-    if (getuid() != pwbuf.pw_uid) {
-      if (setuid(pwbuf.pw_uid) != 0) {
-        ink_fatal_die("Can't change uid to user: %s, uid: %d",
-                      user, pwbuf.pw_uid);
-      }
-    }
-  }
-  ats_free(buf);
 
-  // Ugly but this gets reset when the process user ID is changed so
-  // it must be udpated here.
-  EnableCoreFile(enable_core_file_p);
 }
 
 //
@@ -1366,10 +1340,8 @@ main(int /* argc ATS_UNUSED */, char **argv)
   diags->prefix_str = "Server ";
   if (is_debug_tag_set("diags"))
     diags->dump();
-# if TS_USE_POSIX_CAP
-  if (is_debug_tag_set("server"))
-    DebugCapabilities("server"); // Can do this now, logging is up.
-# endif
+
+  DebugCapabilities("privileges"); // Can do this now, logging is up.
 
   // Check if we should do mlockall()
 #if defined(MCL_FUTURE)
@@ -1661,7 +1633,6 @@ main(int /* argc ATS_UNUSED */, char **argv)
 # if ! TS_USE_POSIX_CAP
   if (admin_user_p) {
     change_uid_gid(user);
-    DebugCapabilities("server");
   }
 # endif
 

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/33f651c9/proxy/Plugin.cc
----------------------------------------------------------------------
diff --git a/proxy/Plugin.cc b/proxy/Plugin.cc
index ef4c5f8..5a2412a 100644
--- a/proxy/Plugin.cc
+++ b/proxy/Plugin.cc
@@ -82,9 +82,12 @@ plugin_load(int argc, char *argv[])
   // elevate the access to read files as root if compiled with capabilities, 
if not
   // change the effective user to root
   {
+
+#if TS_USE_POSIX_CAP
     uint32_t elevate_access = 0;
     REC_ReadConfigInteger(elevate_access, "proxy.config.plugin.load_elevated");
     ElevateAccess access(elevate_access != 0);
+#endif /* TS_USE_POSIX_CAP */
 
     handle = dlopen(path, RTLD_NOW);
     if (!handle) {

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/33f651c9/proxy/http/remap/RemapConfig.cc
----------------------------------------------------------------------
diff --git a/proxy/http/remap/RemapConfig.cc b/proxy/http/remap/RemapConfig.cc
index 2bbc67c..398039f 100644
--- a/proxy/http/remap/RemapConfig.cc
+++ b/proxy/http/remap/RemapConfig.cc
@@ -631,12 +631,12 @@ remap_load_plugin(const char ** argv, int argc, 
url_mapping *mp, char *errbuf, i
     }
     Debug("remap_plugin", "New remap plugin info created for \"%s\"", c);
 
-    // elevate the access to read files as root if compiled with capabilities, 
if not
-    // change the effective user to root
     {
+#if TS_USE_POSIX_CAP
       uint32_t elevate_access = 0;
       REC_ReadConfigInteger(elevate_access, 
"proxy.config.plugin.load_elevated");
       ElevateAccess access(elevate_access != 0);
+#endif /* TS_USE_POSIX_CAP */
 
       if ((pi->dlh = dlopen(c, RTLD_NOW)) == NULL) {
 #if defined(freebsd) || defined(openbsd)

Reply via email to