You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@trafficserver.apache.org by jp...@apache.org on 2014/10/03 01:52:14 UTC
[7/7] git commit: TS-3103: improve privilege elevation
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 <jp...@apache.org>
Authored: Tue Sep 30 13:19:47 2014 -0700
Committer: James Peach <jp...@apache.org>
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)