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)
