Loading .gitignore +2 −0 Original line number Diff line number Diff line Loading @@ -191,6 +191,8 @@ uptime-*.json /src/lib/libtor-memarea-testing.a /src/lib/libtor-net.a /src/lib/libtor-net-testing.a /src/lib/libtor-process.a /src/lib/libtor-process-testing.a /src/lib/libtor-sandbox.a /src/lib/libtor-sandbox-testing.a /src/lib/libtor-string.a Loading Makefile.am +2 −0 Original line number Diff line number Diff line Loading @@ -40,6 +40,7 @@ endif # "Common" libraries used to link tor's utility code. TOR_UTIL_LIBS = \ src/common/libor.a \ src/lib/libtor-process.a \ src/lib/libtor-fs.a \ src/lib/libtor-encoding.a \ src/lib/libtor-sandbox.a \ Loading @@ -62,6 +63,7 @@ TOR_UTIL_LIBS = \ # and tests) TOR_UTIL_TESTING_LIBS = \ src/common/libor-testing.a \ src/lib/libtor-process-testing.a \ src/lib/libtor-fs-testing.a \ src/lib/libtor-encoding-testing.a \ src/lib/libtor-sandbox-testing.a \ Loading src/common/compat.c +0 −502 Original line number Diff line number Diff line Loading @@ -367,421 +367,6 @@ set_max_file_descriptors(rlim_t limit, int *max_out) return 0; } #ifndef _WIN32 /** Log details of current user and group credentials. Return 0 on * success. Logs and return -1 on failure. */ static int log_credential_status(void) { /** Log level to use when describing non-error UID/GID status. */ #define CREDENTIAL_LOG_LEVEL LOG_INFO /* Real, effective and saved UIDs */ uid_t ruid, euid, suid; /* Read, effective and saved GIDs */ gid_t rgid, egid, sgid; /* Supplementary groups */ gid_t *sup_gids = NULL; int sup_gids_size; /* Number of supplementary groups */ int ngids; /* log UIDs */ #ifdef HAVE_GETRESUID if (getresuid(&ruid, &euid, &suid) != 0 ) { log_warn(LD_GENERAL, "Error getting changed UIDs: %s", strerror(errno)); return -1; } else { log_fn(CREDENTIAL_LOG_LEVEL, LD_GENERAL, "UID is %u (real), %u (effective), %u (saved)", (unsigned)ruid, (unsigned)euid, (unsigned)suid); } #else /* !(defined(HAVE_GETRESUID)) */ /* getresuid is not present on MacOS X, so we can't get the saved (E)UID */ ruid = getuid(); euid = geteuid(); (void)suid; log_fn(CREDENTIAL_LOG_LEVEL, LD_GENERAL, "UID is %u (real), %u (effective), unknown (saved)", (unsigned)ruid, (unsigned)euid); #endif /* defined(HAVE_GETRESUID) */ /* log GIDs */ #ifdef HAVE_GETRESGID if (getresgid(&rgid, &egid, &sgid) != 0 ) { log_warn(LD_GENERAL, "Error getting changed GIDs: %s", strerror(errno)); return -1; } else { log_fn(CREDENTIAL_LOG_LEVEL, LD_GENERAL, "GID is %u (real), %u (effective), %u (saved)", (unsigned)rgid, (unsigned)egid, (unsigned)sgid); } #else /* !(defined(HAVE_GETRESGID)) */ /* getresgid is not present on MacOS X, so we can't get the saved (E)GID */ rgid = getgid(); egid = getegid(); (void)sgid; log_fn(CREDENTIAL_LOG_LEVEL, LD_GENERAL, "GID is %u (real), %u (effective), unknown (saved)", (unsigned)rgid, (unsigned)egid); #endif /* defined(HAVE_GETRESGID) */ /* log supplementary groups */ sup_gids_size = 64; sup_gids = tor_calloc(64, sizeof(gid_t)); while ((ngids = getgroups(sup_gids_size, sup_gids)) < 0 && errno == EINVAL && sup_gids_size < NGROUPS_MAX) { sup_gids_size *= 2; sup_gids = tor_reallocarray(sup_gids, sizeof(gid_t), sup_gids_size); } if (ngids < 0) { log_warn(LD_GENERAL, "Error getting supplementary GIDs: %s", strerror(errno)); tor_free(sup_gids); return -1; } else { int i, retval = 0; char *s = NULL; smartlist_t *elts = smartlist_new(); for (i = 0; i<ngids; i++) { smartlist_add_asprintf(elts, "%u", (unsigned)sup_gids[i]); } s = smartlist_join_strings(elts, " ", 0, NULL); log_fn(CREDENTIAL_LOG_LEVEL, LD_GENERAL, "Supplementary groups are: %s",s); tor_free(s); SMARTLIST_FOREACH(elts, char *, cp, tor_free(cp)); smartlist_free(elts); tor_free(sup_gids); return retval; } return 0; } #endif /* !defined(_WIN32) */ /** Return true iff we were compiled with capability support, and capabilities * seem to work. **/ int have_capability_support(void) { #ifdef HAVE_LINUX_CAPABILITIES cap_t caps = cap_get_proc(); if (caps == NULL) return 0; cap_free(caps); return 1; #else /* !(defined(HAVE_LINUX_CAPABILITIES)) */ return 0; #endif /* defined(HAVE_LINUX_CAPABILITIES) */ } #ifdef HAVE_LINUX_CAPABILITIES /** Helper. Drop all capabilities but a small set, and set PR_KEEPCAPS as * appropriate. * * If pre_setuid, retain only CAP_NET_BIND_SERVICE, CAP_SETUID, and * CAP_SETGID, and use PR_KEEPCAPS to ensure that capabilities persist across * setuid(). * * If not pre_setuid, retain only CAP_NET_BIND_SERVICE, and disable * PR_KEEPCAPS. * * Return 0 on success, and -1 on failure. */ static int drop_capabilities(int pre_setuid) { /* We keep these three capabilities, and these only, as we setuid. * After we setuid, we drop all but the first. */ const cap_value_t caplist[] = { CAP_NET_BIND_SERVICE, CAP_SETUID, CAP_SETGID }; const char *where = pre_setuid ? "pre-setuid" : "post-setuid"; const int n_effective = pre_setuid ? 3 : 1; const int n_permitted = pre_setuid ? 3 : 1; const int n_inheritable = 1; const int keepcaps = pre_setuid ? 1 : 0; /* Sets whether we keep capabilities across a setuid. */ if (prctl(PR_SET_KEEPCAPS, keepcaps) < 0) { log_warn(LD_CONFIG, "Unable to call prctl() %s: %s", where, strerror(errno)); return -1; } cap_t caps = cap_get_proc(); if (!caps) { log_warn(LD_CONFIG, "Unable to call cap_get_proc() %s: %s", where, strerror(errno)); return -1; } cap_clear(caps); cap_set_flag(caps, CAP_EFFECTIVE, n_effective, caplist, CAP_SET); cap_set_flag(caps, CAP_PERMITTED, n_permitted, caplist, CAP_SET); cap_set_flag(caps, CAP_INHERITABLE, n_inheritable, caplist, CAP_SET); int r = cap_set_proc(caps); cap_free(caps); if (r < 0) { log_warn(LD_CONFIG, "No permission to set capabilities %s: %s", where, strerror(errno)); return -1; } return 0; } #endif /* defined(HAVE_LINUX_CAPABILITIES) */ /** 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. * * If SWITCH_ID_KEEP_BINDLOW is set in 'flags', try to use the capability * system to retain the abilitity to bind low ports. * * If SWITCH_ID_WARN_IF_NO_CAPS is set in flags, also warn if we have * don't have capability support. */ int switch_id(const char *user, const unsigned flags) { #ifndef _WIN32 const struct passwd *pw = NULL; uid_t old_uid; gid_t old_gid; static int have_already_switched_id = 0; const int keep_bindlow = !!(flags & SWITCH_ID_KEEP_BINDLOW); const int warn_if_no_caps = !!(flags & SWITCH_ID_WARN_IF_NO_CAPS); tor_assert(user); if (have_already_switched_id) return 0; /* Log the initial credential state */ if (log_credential_status()) return -1; log_fn(CREDENTIAL_LOG_LEVEL, LD_GENERAL, "Changing user and groups"); /* Get old UID/GID to check if we changed correctly */ old_uid = getuid(); old_gid = getgid(); /* Lookup the user and group information, if we have a problem, bail out. */ pw = tor_getpwnam(user); if (pw == NULL) { log_warn(LD_CONFIG, "Error setting configured user: %s not found", user); return -1; } #ifdef HAVE_LINUX_CAPABILITIES (void) warn_if_no_caps; if (keep_bindlow) { if (drop_capabilities(1)) return -1; } #else /* !(defined(HAVE_LINUX_CAPABILITIES)) */ (void) keep_bindlow; if (warn_if_no_caps) { log_warn(LD_CONFIG, "KeepBindCapabilities set, but no capability support " "on this system."); } #endif /* defined(HAVE_LINUX_CAPABILITIES) */ /* Properly switch egid,gid,euid,uid here or bail out */ if (setgroups(1, &pw->pw_gid)) { log_warn(LD_GENERAL, "Error setting groups to gid %d: \"%s\".", (int)pw->pw_gid, strerror(errno)); if (old_uid == pw->pw_uid) { log_warn(LD_GENERAL, "Tor is already running as %s. You do not need " "the \"User\" option if you are already running as the user " "you want to be. (If you did not set the User option in your " "torrc, check whether it was specified on the command line " "by a startup script.)", user); } else { log_warn(LD_GENERAL, "If you set the \"User\" option, you must start Tor" " as root."); } return -1; } if (setegid(pw->pw_gid)) { log_warn(LD_GENERAL, "Error setting egid to %d: %s", (int)pw->pw_gid, strerror(errno)); return -1; } if (setgid(pw->pw_gid)) { log_warn(LD_GENERAL, "Error setting gid to %d: %s", (int)pw->pw_gid, strerror(errno)); return -1; } if (setuid(pw->pw_uid)) { log_warn(LD_GENERAL, "Error setting configured uid to %s (%d): %s", user, (int)pw->pw_uid, strerror(errno)); return -1; } if (seteuid(pw->pw_uid)) { log_warn(LD_GENERAL, "Error setting configured euid to %s (%d): %s", user, (int)pw->pw_uid, strerror(errno)); return -1; } /* This is how OpenBSD rolls: if (setgroups(1, &pw->pw_gid) || setegid(pw->pw_gid) || setgid(pw->pw_gid) || setuid(pw->pw_uid) || seteuid(pw->pw_uid)) { setgid(pw->pw_gid) || seteuid(pw->pw_uid) || setuid(pw->pw_uid)) { log_warn(LD_GENERAL, "Error setting configured UID/GID: %s", strerror(errno)); return -1; } */ /* We've properly switched egid, gid, euid, uid, and supplementary groups if * we're here. */ #ifdef HAVE_LINUX_CAPABILITIES if (keep_bindlow) { if (drop_capabilities(0)) return -1; } #endif /* defined(HAVE_LINUX_CAPABILITIES) */ #if !defined(CYGWIN) && !defined(__CYGWIN__) /* If we tried to drop privilege to a group/user other than root, attempt to * restore root (E)(U|G)ID, and abort if the operation succeeds */ /* Only check for privilege dropping if we were asked to be non-root */ if (pw->pw_uid) { /* Try changing GID/EGID */ if (pw->pw_gid != old_gid && (setgid(old_gid) != -1 || setegid(old_gid) != -1)) { log_warn(LD_GENERAL, "Was able to restore group credentials even after " "switching GID: this means that the setgid code didn't work."); return -1; } /* Try changing UID/EUID */ if (pw->pw_uid != old_uid && (setuid(old_uid) != -1 || seteuid(old_uid) != -1)) { log_warn(LD_GENERAL, "Was able to restore user credentials even after " "switching UID: this means that the setuid code didn't work."); return -1; } } #endif /* !defined(CYGWIN) && !defined(__CYGWIN__) */ /* Check what really happened */ if (log_credential_status()) { return -1; } have_already_switched_id = 1; /* mark success so we never try again */ #if defined(__linux__) && defined(HAVE_SYS_PRCTL_H) && \ defined(HAVE_PRCTL) && defined(PR_SET_DUMPABLE) if (pw->pw_uid) { /* Re-enable core dumps if we're not running as root. */ log_info(LD_CONFIG, "Re-enabling coredumps"); if (prctl(PR_SET_DUMPABLE, 1)) { log_warn(LD_CONFIG, "Unable to re-enable coredumps: %s",strerror(errno)); } } #endif /* defined(__linux__) && defined(HAVE_SYS_PRCTL_H) && ... */ return 0; #else /* !(!defined(_WIN32)) */ (void)user; (void)flags; log_warn(LD_CONFIG, "Switching users is unsupported on your OS."); return -1; #endif /* !defined(_WIN32) */ } /* We only use the linux prctl for now. There is no Win32 support; this may * also work on various BSD systems and Mac OS X - send testing feedback! * * On recent Gnu/Linux kernels it is possible to create a system-wide policy * that will prevent non-root processes from attaching to other processes * unless they are the parent process; thus gdb can attach to programs that * they execute but they cannot attach to other processes running as the same * user. The system wide policy may be set with the sysctl * kernel.yama.ptrace_scope or by inspecting * /proc/sys/kernel/yama/ptrace_scope and it is 1 by default on Ubuntu 11.04. * * This ptrace scope will be ignored on Gnu/Linux for users with * CAP_SYS_PTRACE and so it is very likely that root will still be able to * attach to the Tor process. */ /** Attempt to disable debugger attachment: return 1 on success, -1 on * failure, and 0 if we don't know how to try on this platform. */ int tor_disable_debugger_attach(void) { int r = -1; log_debug(LD_CONFIG, "Attemping to disable debugger attachment to Tor for " "unprivileged users."); #if defined(__linux__) && defined(HAVE_SYS_PRCTL_H) \ && defined(HAVE_PRCTL) && defined(PR_SET_DUMPABLE) #define TRIED_TO_DISABLE r = prctl(PR_SET_DUMPABLE, 0); #elif defined(__APPLE__) && defined(PT_DENY_ATTACH) #define TRIED_TO_ATTACH r = ptrace(PT_DENY_ATTACH, 0, 0, 0); #endif /* defined(__linux__) && defined(HAVE_SYS_PRCTL_H) ... || ... */ // XXX: TODO - Mac OS X has dtrace and this may be disabled. // XXX: TODO - Windows probably has something similar #ifdef TRIED_TO_DISABLE if (r == 0) { log_debug(LD_CONFIG,"Debugger attachment disabled for " "unprivileged users."); return 1; } else { log_warn(LD_CONFIG, "Unable to disable debugger attaching: %s", strerror(errno)); } #endif /* defined(TRIED_TO_DISABLE) */ #undef TRIED_TO_DISABLE return r; } #ifndef HAVE__NSGETENVIRON #ifndef HAVE_EXTERN_ENVIRON_DECLARED /* Some platforms declare environ under some circumstances, others don't. */ #ifndef RUNNING_DOXYGEN extern char **environ; #endif #endif /* !defined(HAVE_EXTERN_ENVIRON_DECLARED) */ #endif /* !defined(HAVE__NSGETENVIRON) */ /** Return the current environment. This is a portable replacement for * 'environ'. */ char ** get_environment(void) { #ifdef HAVE__NSGETENVIRON /* This is for compatibility between OSX versions. Otherwise (for example) * when we do a mostly-static build on OSX 10.7, the resulting binary won't * work on OSX 10.6. */ return *_NSGetEnviron(); #else /* !(defined(HAVE__NSGETENVIRON)) */ return environ; #endif /* defined(HAVE__NSGETENVIRON) */ } /** Get name of current host and write it to <b>name</b> array, whose * length is specified by <b>namelen</b> argument. Return 0 upon * successful completion; otherwise return return -1. (Currently, Loading Loading @@ -965,93 +550,6 @@ compute_num_cpus(void) return num_cpus; } #if defined(HAVE_MLOCKALL) && HAVE_DECL_MLOCKALL && defined(RLIMIT_MEMLOCK) #define HAVE_UNIX_MLOCKALL #endif #ifdef HAVE_UNIX_MLOCKALL /** Attempt to raise the current and max rlimit to infinity for our process. * This only needs to be done once and can probably only be done when we have * not already dropped privileges. */ static int tor_set_max_memlock(void) { /* Future consideration for Windows is probably SetProcessWorkingSetSize * This is similar to setting the memory rlimit of RLIMIT_MEMLOCK * http://msdn.microsoft.com/en-us/library/ms686234(VS.85).aspx */ struct rlimit limit; /* RLIM_INFINITY is -1 on some platforms. */ limit.rlim_cur = RLIM_INFINITY; limit.rlim_max = RLIM_INFINITY; if (setrlimit(RLIMIT_MEMLOCK, &limit) == -1) { if (errno == EPERM) { log_warn(LD_GENERAL, "You appear to lack permissions to change memory " "limits. Are you root?"); } log_warn(LD_GENERAL, "Unable to raise RLIMIT_MEMLOCK: %s", strerror(errno)); return -1; } return 0; } #endif /* defined(HAVE_UNIX_MLOCKALL) */ /** Attempt to lock all current and all future memory pages. * This should only be called once and while we're privileged. * Like mlockall() we return 0 when we're successful and -1 when we're not. * Unlike mlockall() we return 1 if we've already attempted to lock memory. */ int tor_mlockall(void) { static int memory_lock_attempted = 0; if (memory_lock_attempted) { return 1; } memory_lock_attempted = 1; /* * Future consideration for Windows may be VirtualLock * VirtualLock appears to implement mlock() but not mlockall() * * http://msdn.microsoft.com/en-us/library/aa366895(VS.85).aspx */ #ifdef HAVE_UNIX_MLOCKALL if (tor_set_max_memlock() == 0) { log_debug(LD_GENERAL, "RLIMIT_MEMLOCK is now set to RLIM_INFINITY."); } if (mlockall(MCL_CURRENT|MCL_FUTURE) == 0) { log_info(LD_GENERAL, "Insecure OS paging is effectively disabled."); return 0; } else { if (errno == ENOSYS) { /* Apple - it's 2009! I'm looking at you. Grrr. */ log_notice(LD_GENERAL, "It appears that mlockall() is not available on " "your platform."); } else if (errno == EPERM) { log_notice(LD_GENERAL, "It appears that you lack the permissions to " "lock memory. Are you root?"); } log_notice(LD_GENERAL, "Unable to lock all current and future memory " "pages: %s", strerror(errno)); return -1; } #else /* !(defined(HAVE_UNIX_MLOCKALL)) */ log_warn(LD_GENERAL, "Unable to lock memory pages. mlockall() unsupported?"); return -1; #endif /* defined(HAVE_UNIX_MLOCKALL) */ } /** * On Windows, WSAEWOULDBLOCK is not always correct: when you see it, * you need to ask the socket for its actual errno. Also, you need to Loading src/common/compat.h +0 −18 Original line number Diff line number Diff line Loading @@ -137,28 +137,10 @@ MOCK_DECL(const char *, get_uname, (void)); typedef unsigned long rlim_t; #endif int set_max_file_descriptors(rlim_t limit, int *max); int tor_disable_debugger_attach(void); #if defined(HAVE_SYS_CAPABILITY_H) && defined(HAVE_CAP_SET_PROC) #define HAVE_LINUX_CAPABILITIES #endif int have_capability_support(void); /** Flag for switch_id; see switch_id() for documentation */ #define SWITCH_ID_KEEP_BINDLOW (1<<0) /** Flag for switch_id; see switch_id() for documentation */ #define SWITCH_ID_WARN_IF_NO_CAPS (1<<1) int switch_id(const char *user, unsigned flags); char **get_environment(void); MOCK_DECL(int, get_total_system_memory, (size_t *mem_out)); int compute_num_cpus(void); int tor_mlockall(void); /** Macros for MIN/MAX. Never use these when the arguments could have * side-effects. * {With GCC extensions we could probably define a safer MIN/MAX. But Loading src/common/include.am +0 −2 Original line number Diff line number Diff line Loading @@ -29,7 +29,6 @@ LIBOR_A_SRC = \ src/common/compat.c \ src/common/compat_time.c \ src/common/util.c \ src/common/util_process.c \ src/common/token_bucket.c \ src/common/workqueue.c \ $(libor_extra_source) \ Loading Loading @@ -71,7 +70,6 @@ COMMONHEADERS = \ src/common/timers.h \ src/common/token_bucket.h \ src/common/util.h \ src/common/util_process.h \ src/common/workqueue.h noinst_HEADERS+= $(COMMONHEADERS) Loading
.gitignore +2 −0 Original line number Diff line number Diff line Loading @@ -191,6 +191,8 @@ uptime-*.json /src/lib/libtor-memarea-testing.a /src/lib/libtor-net.a /src/lib/libtor-net-testing.a /src/lib/libtor-process.a /src/lib/libtor-process-testing.a /src/lib/libtor-sandbox.a /src/lib/libtor-sandbox-testing.a /src/lib/libtor-string.a Loading
Makefile.am +2 −0 Original line number Diff line number Diff line Loading @@ -40,6 +40,7 @@ endif # "Common" libraries used to link tor's utility code. TOR_UTIL_LIBS = \ src/common/libor.a \ src/lib/libtor-process.a \ src/lib/libtor-fs.a \ src/lib/libtor-encoding.a \ src/lib/libtor-sandbox.a \ Loading @@ -62,6 +63,7 @@ TOR_UTIL_LIBS = \ # and tests) TOR_UTIL_TESTING_LIBS = \ src/common/libor-testing.a \ src/lib/libtor-process-testing.a \ src/lib/libtor-fs-testing.a \ src/lib/libtor-encoding-testing.a \ src/lib/libtor-sandbox-testing.a \ Loading
src/common/compat.c +0 −502 Original line number Diff line number Diff line Loading @@ -367,421 +367,6 @@ set_max_file_descriptors(rlim_t limit, int *max_out) return 0; } #ifndef _WIN32 /** Log details of current user and group credentials. Return 0 on * success. Logs and return -1 on failure. */ static int log_credential_status(void) { /** Log level to use when describing non-error UID/GID status. */ #define CREDENTIAL_LOG_LEVEL LOG_INFO /* Real, effective and saved UIDs */ uid_t ruid, euid, suid; /* Read, effective and saved GIDs */ gid_t rgid, egid, sgid; /* Supplementary groups */ gid_t *sup_gids = NULL; int sup_gids_size; /* Number of supplementary groups */ int ngids; /* log UIDs */ #ifdef HAVE_GETRESUID if (getresuid(&ruid, &euid, &suid) != 0 ) { log_warn(LD_GENERAL, "Error getting changed UIDs: %s", strerror(errno)); return -1; } else { log_fn(CREDENTIAL_LOG_LEVEL, LD_GENERAL, "UID is %u (real), %u (effective), %u (saved)", (unsigned)ruid, (unsigned)euid, (unsigned)suid); } #else /* !(defined(HAVE_GETRESUID)) */ /* getresuid is not present on MacOS X, so we can't get the saved (E)UID */ ruid = getuid(); euid = geteuid(); (void)suid; log_fn(CREDENTIAL_LOG_LEVEL, LD_GENERAL, "UID is %u (real), %u (effective), unknown (saved)", (unsigned)ruid, (unsigned)euid); #endif /* defined(HAVE_GETRESUID) */ /* log GIDs */ #ifdef HAVE_GETRESGID if (getresgid(&rgid, &egid, &sgid) != 0 ) { log_warn(LD_GENERAL, "Error getting changed GIDs: %s", strerror(errno)); return -1; } else { log_fn(CREDENTIAL_LOG_LEVEL, LD_GENERAL, "GID is %u (real), %u (effective), %u (saved)", (unsigned)rgid, (unsigned)egid, (unsigned)sgid); } #else /* !(defined(HAVE_GETRESGID)) */ /* getresgid is not present on MacOS X, so we can't get the saved (E)GID */ rgid = getgid(); egid = getegid(); (void)sgid; log_fn(CREDENTIAL_LOG_LEVEL, LD_GENERAL, "GID is %u (real), %u (effective), unknown (saved)", (unsigned)rgid, (unsigned)egid); #endif /* defined(HAVE_GETRESGID) */ /* log supplementary groups */ sup_gids_size = 64; sup_gids = tor_calloc(64, sizeof(gid_t)); while ((ngids = getgroups(sup_gids_size, sup_gids)) < 0 && errno == EINVAL && sup_gids_size < NGROUPS_MAX) { sup_gids_size *= 2; sup_gids = tor_reallocarray(sup_gids, sizeof(gid_t), sup_gids_size); } if (ngids < 0) { log_warn(LD_GENERAL, "Error getting supplementary GIDs: %s", strerror(errno)); tor_free(sup_gids); return -1; } else { int i, retval = 0; char *s = NULL; smartlist_t *elts = smartlist_new(); for (i = 0; i<ngids; i++) { smartlist_add_asprintf(elts, "%u", (unsigned)sup_gids[i]); } s = smartlist_join_strings(elts, " ", 0, NULL); log_fn(CREDENTIAL_LOG_LEVEL, LD_GENERAL, "Supplementary groups are: %s",s); tor_free(s); SMARTLIST_FOREACH(elts, char *, cp, tor_free(cp)); smartlist_free(elts); tor_free(sup_gids); return retval; } return 0; } #endif /* !defined(_WIN32) */ /** Return true iff we were compiled with capability support, and capabilities * seem to work. **/ int have_capability_support(void) { #ifdef HAVE_LINUX_CAPABILITIES cap_t caps = cap_get_proc(); if (caps == NULL) return 0; cap_free(caps); return 1; #else /* !(defined(HAVE_LINUX_CAPABILITIES)) */ return 0; #endif /* defined(HAVE_LINUX_CAPABILITIES) */ } #ifdef HAVE_LINUX_CAPABILITIES /** Helper. Drop all capabilities but a small set, and set PR_KEEPCAPS as * appropriate. * * If pre_setuid, retain only CAP_NET_BIND_SERVICE, CAP_SETUID, and * CAP_SETGID, and use PR_KEEPCAPS to ensure that capabilities persist across * setuid(). * * If not pre_setuid, retain only CAP_NET_BIND_SERVICE, and disable * PR_KEEPCAPS. * * Return 0 on success, and -1 on failure. */ static int drop_capabilities(int pre_setuid) { /* We keep these three capabilities, and these only, as we setuid. * After we setuid, we drop all but the first. */ const cap_value_t caplist[] = { CAP_NET_BIND_SERVICE, CAP_SETUID, CAP_SETGID }; const char *where = pre_setuid ? "pre-setuid" : "post-setuid"; const int n_effective = pre_setuid ? 3 : 1; const int n_permitted = pre_setuid ? 3 : 1; const int n_inheritable = 1; const int keepcaps = pre_setuid ? 1 : 0; /* Sets whether we keep capabilities across a setuid. */ if (prctl(PR_SET_KEEPCAPS, keepcaps) < 0) { log_warn(LD_CONFIG, "Unable to call prctl() %s: %s", where, strerror(errno)); return -1; } cap_t caps = cap_get_proc(); if (!caps) { log_warn(LD_CONFIG, "Unable to call cap_get_proc() %s: %s", where, strerror(errno)); return -1; } cap_clear(caps); cap_set_flag(caps, CAP_EFFECTIVE, n_effective, caplist, CAP_SET); cap_set_flag(caps, CAP_PERMITTED, n_permitted, caplist, CAP_SET); cap_set_flag(caps, CAP_INHERITABLE, n_inheritable, caplist, CAP_SET); int r = cap_set_proc(caps); cap_free(caps); if (r < 0) { log_warn(LD_CONFIG, "No permission to set capabilities %s: %s", where, strerror(errno)); return -1; } return 0; } #endif /* defined(HAVE_LINUX_CAPABILITIES) */ /** 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. * * If SWITCH_ID_KEEP_BINDLOW is set in 'flags', try to use the capability * system to retain the abilitity to bind low ports. * * If SWITCH_ID_WARN_IF_NO_CAPS is set in flags, also warn if we have * don't have capability support. */ int switch_id(const char *user, const unsigned flags) { #ifndef _WIN32 const struct passwd *pw = NULL; uid_t old_uid; gid_t old_gid; static int have_already_switched_id = 0; const int keep_bindlow = !!(flags & SWITCH_ID_KEEP_BINDLOW); const int warn_if_no_caps = !!(flags & SWITCH_ID_WARN_IF_NO_CAPS); tor_assert(user); if (have_already_switched_id) return 0; /* Log the initial credential state */ if (log_credential_status()) return -1; log_fn(CREDENTIAL_LOG_LEVEL, LD_GENERAL, "Changing user and groups"); /* Get old UID/GID to check if we changed correctly */ old_uid = getuid(); old_gid = getgid(); /* Lookup the user and group information, if we have a problem, bail out. */ pw = tor_getpwnam(user); if (pw == NULL) { log_warn(LD_CONFIG, "Error setting configured user: %s not found", user); return -1; } #ifdef HAVE_LINUX_CAPABILITIES (void) warn_if_no_caps; if (keep_bindlow) { if (drop_capabilities(1)) return -1; } #else /* !(defined(HAVE_LINUX_CAPABILITIES)) */ (void) keep_bindlow; if (warn_if_no_caps) { log_warn(LD_CONFIG, "KeepBindCapabilities set, but no capability support " "on this system."); } #endif /* defined(HAVE_LINUX_CAPABILITIES) */ /* Properly switch egid,gid,euid,uid here or bail out */ if (setgroups(1, &pw->pw_gid)) { log_warn(LD_GENERAL, "Error setting groups to gid %d: \"%s\".", (int)pw->pw_gid, strerror(errno)); if (old_uid == pw->pw_uid) { log_warn(LD_GENERAL, "Tor is already running as %s. You do not need " "the \"User\" option if you are already running as the user " "you want to be. (If you did not set the User option in your " "torrc, check whether it was specified on the command line " "by a startup script.)", user); } else { log_warn(LD_GENERAL, "If you set the \"User\" option, you must start Tor" " as root."); } return -1; } if (setegid(pw->pw_gid)) { log_warn(LD_GENERAL, "Error setting egid to %d: %s", (int)pw->pw_gid, strerror(errno)); return -1; } if (setgid(pw->pw_gid)) { log_warn(LD_GENERAL, "Error setting gid to %d: %s", (int)pw->pw_gid, strerror(errno)); return -1; } if (setuid(pw->pw_uid)) { log_warn(LD_GENERAL, "Error setting configured uid to %s (%d): %s", user, (int)pw->pw_uid, strerror(errno)); return -1; } if (seteuid(pw->pw_uid)) { log_warn(LD_GENERAL, "Error setting configured euid to %s (%d): %s", user, (int)pw->pw_uid, strerror(errno)); return -1; } /* This is how OpenBSD rolls: if (setgroups(1, &pw->pw_gid) || setegid(pw->pw_gid) || setgid(pw->pw_gid) || setuid(pw->pw_uid) || seteuid(pw->pw_uid)) { setgid(pw->pw_gid) || seteuid(pw->pw_uid) || setuid(pw->pw_uid)) { log_warn(LD_GENERAL, "Error setting configured UID/GID: %s", strerror(errno)); return -1; } */ /* We've properly switched egid, gid, euid, uid, and supplementary groups if * we're here. */ #ifdef HAVE_LINUX_CAPABILITIES if (keep_bindlow) { if (drop_capabilities(0)) return -1; } #endif /* defined(HAVE_LINUX_CAPABILITIES) */ #if !defined(CYGWIN) && !defined(__CYGWIN__) /* If we tried to drop privilege to a group/user other than root, attempt to * restore root (E)(U|G)ID, and abort if the operation succeeds */ /* Only check for privilege dropping if we were asked to be non-root */ if (pw->pw_uid) { /* Try changing GID/EGID */ if (pw->pw_gid != old_gid && (setgid(old_gid) != -1 || setegid(old_gid) != -1)) { log_warn(LD_GENERAL, "Was able to restore group credentials even after " "switching GID: this means that the setgid code didn't work."); return -1; } /* Try changing UID/EUID */ if (pw->pw_uid != old_uid && (setuid(old_uid) != -1 || seteuid(old_uid) != -1)) { log_warn(LD_GENERAL, "Was able to restore user credentials even after " "switching UID: this means that the setuid code didn't work."); return -1; } } #endif /* !defined(CYGWIN) && !defined(__CYGWIN__) */ /* Check what really happened */ if (log_credential_status()) { return -1; } have_already_switched_id = 1; /* mark success so we never try again */ #if defined(__linux__) && defined(HAVE_SYS_PRCTL_H) && \ defined(HAVE_PRCTL) && defined(PR_SET_DUMPABLE) if (pw->pw_uid) { /* Re-enable core dumps if we're not running as root. */ log_info(LD_CONFIG, "Re-enabling coredumps"); if (prctl(PR_SET_DUMPABLE, 1)) { log_warn(LD_CONFIG, "Unable to re-enable coredumps: %s",strerror(errno)); } } #endif /* defined(__linux__) && defined(HAVE_SYS_PRCTL_H) && ... */ return 0; #else /* !(!defined(_WIN32)) */ (void)user; (void)flags; log_warn(LD_CONFIG, "Switching users is unsupported on your OS."); return -1; #endif /* !defined(_WIN32) */ } /* We only use the linux prctl for now. There is no Win32 support; this may * also work on various BSD systems and Mac OS X - send testing feedback! * * On recent Gnu/Linux kernels it is possible to create a system-wide policy * that will prevent non-root processes from attaching to other processes * unless they are the parent process; thus gdb can attach to programs that * they execute but they cannot attach to other processes running as the same * user. The system wide policy may be set with the sysctl * kernel.yama.ptrace_scope or by inspecting * /proc/sys/kernel/yama/ptrace_scope and it is 1 by default on Ubuntu 11.04. * * This ptrace scope will be ignored on Gnu/Linux for users with * CAP_SYS_PTRACE and so it is very likely that root will still be able to * attach to the Tor process. */ /** Attempt to disable debugger attachment: return 1 on success, -1 on * failure, and 0 if we don't know how to try on this platform. */ int tor_disable_debugger_attach(void) { int r = -1; log_debug(LD_CONFIG, "Attemping to disable debugger attachment to Tor for " "unprivileged users."); #if defined(__linux__) && defined(HAVE_SYS_PRCTL_H) \ && defined(HAVE_PRCTL) && defined(PR_SET_DUMPABLE) #define TRIED_TO_DISABLE r = prctl(PR_SET_DUMPABLE, 0); #elif defined(__APPLE__) && defined(PT_DENY_ATTACH) #define TRIED_TO_ATTACH r = ptrace(PT_DENY_ATTACH, 0, 0, 0); #endif /* defined(__linux__) && defined(HAVE_SYS_PRCTL_H) ... || ... */ // XXX: TODO - Mac OS X has dtrace and this may be disabled. // XXX: TODO - Windows probably has something similar #ifdef TRIED_TO_DISABLE if (r == 0) { log_debug(LD_CONFIG,"Debugger attachment disabled for " "unprivileged users."); return 1; } else { log_warn(LD_CONFIG, "Unable to disable debugger attaching: %s", strerror(errno)); } #endif /* defined(TRIED_TO_DISABLE) */ #undef TRIED_TO_DISABLE return r; } #ifndef HAVE__NSGETENVIRON #ifndef HAVE_EXTERN_ENVIRON_DECLARED /* Some platforms declare environ under some circumstances, others don't. */ #ifndef RUNNING_DOXYGEN extern char **environ; #endif #endif /* !defined(HAVE_EXTERN_ENVIRON_DECLARED) */ #endif /* !defined(HAVE__NSGETENVIRON) */ /** Return the current environment. This is a portable replacement for * 'environ'. */ char ** get_environment(void) { #ifdef HAVE__NSGETENVIRON /* This is for compatibility between OSX versions. Otherwise (for example) * when we do a mostly-static build on OSX 10.7, the resulting binary won't * work on OSX 10.6. */ return *_NSGetEnviron(); #else /* !(defined(HAVE__NSGETENVIRON)) */ return environ; #endif /* defined(HAVE__NSGETENVIRON) */ } /** Get name of current host and write it to <b>name</b> array, whose * length is specified by <b>namelen</b> argument. Return 0 upon * successful completion; otherwise return return -1. (Currently, Loading Loading @@ -965,93 +550,6 @@ compute_num_cpus(void) return num_cpus; } #if defined(HAVE_MLOCKALL) && HAVE_DECL_MLOCKALL && defined(RLIMIT_MEMLOCK) #define HAVE_UNIX_MLOCKALL #endif #ifdef HAVE_UNIX_MLOCKALL /** Attempt to raise the current and max rlimit to infinity for our process. * This only needs to be done once and can probably only be done when we have * not already dropped privileges. */ static int tor_set_max_memlock(void) { /* Future consideration for Windows is probably SetProcessWorkingSetSize * This is similar to setting the memory rlimit of RLIMIT_MEMLOCK * http://msdn.microsoft.com/en-us/library/ms686234(VS.85).aspx */ struct rlimit limit; /* RLIM_INFINITY is -1 on some platforms. */ limit.rlim_cur = RLIM_INFINITY; limit.rlim_max = RLIM_INFINITY; if (setrlimit(RLIMIT_MEMLOCK, &limit) == -1) { if (errno == EPERM) { log_warn(LD_GENERAL, "You appear to lack permissions to change memory " "limits. Are you root?"); } log_warn(LD_GENERAL, "Unable to raise RLIMIT_MEMLOCK: %s", strerror(errno)); return -1; } return 0; } #endif /* defined(HAVE_UNIX_MLOCKALL) */ /** Attempt to lock all current and all future memory pages. * This should only be called once and while we're privileged. * Like mlockall() we return 0 when we're successful and -1 when we're not. * Unlike mlockall() we return 1 if we've already attempted to lock memory. */ int tor_mlockall(void) { static int memory_lock_attempted = 0; if (memory_lock_attempted) { return 1; } memory_lock_attempted = 1; /* * Future consideration for Windows may be VirtualLock * VirtualLock appears to implement mlock() but not mlockall() * * http://msdn.microsoft.com/en-us/library/aa366895(VS.85).aspx */ #ifdef HAVE_UNIX_MLOCKALL if (tor_set_max_memlock() == 0) { log_debug(LD_GENERAL, "RLIMIT_MEMLOCK is now set to RLIM_INFINITY."); } if (mlockall(MCL_CURRENT|MCL_FUTURE) == 0) { log_info(LD_GENERAL, "Insecure OS paging is effectively disabled."); return 0; } else { if (errno == ENOSYS) { /* Apple - it's 2009! I'm looking at you. Grrr. */ log_notice(LD_GENERAL, "It appears that mlockall() is not available on " "your platform."); } else if (errno == EPERM) { log_notice(LD_GENERAL, "It appears that you lack the permissions to " "lock memory. Are you root?"); } log_notice(LD_GENERAL, "Unable to lock all current and future memory " "pages: %s", strerror(errno)); return -1; } #else /* !(defined(HAVE_UNIX_MLOCKALL)) */ log_warn(LD_GENERAL, "Unable to lock memory pages. mlockall() unsupported?"); return -1; #endif /* defined(HAVE_UNIX_MLOCKALL) */ } /** * On Windows, WSAEWOULDBLOCK is not always correct: when you see it, * you need to ask the socket for its actual errno. Also, you need to Loading
src/common/compat.h +0 −18 Original line number Diff line number Diff line Loading @@ -137,28 +137,10 @@ MOCK_DECL(const char *, get_uname, (void)); typedef unsigned long rlim_t; #endif int set_max_file_descriptors(rlim_t limit, int *max); int tor_disable_debugger_attach(void); #if defined(HAVE_SYS_CAPABILITY_H) && defined(HAVE_CAP_SET_PROC) #define HAVE_LINUX_CAPABILITIES #endif int have_capability_support(void); /** Flag for switch_id; see switch_id() for documentation */ #define SWITCH_ID_KEEP_BINDLOW (1<<0) /** Flag for switch_id; see switch_id() for documentation */ #define SWITCH_ID_WARN_IF_NO_CAPS (1<<1) int switch_id(const char *user, unsigned flags); char **get_environment(void); MOCK_DECL(int, get_total_system_memory, (size_t *mem_out)); int compute_num_cpus(void); int tor_mlockall(void); /** Macros for MIN/MAX. Never use these when the arguments could have * side-effects. * {With GCC extensions we could probably define a safer MIN/MAX. But Loading
src/common/include.am +0 −2 Original line number Diff line number Diff line Loading @@ -29,7 +29,6 @@ LIBOR_A_SRC = \ src/common/compat.c \ src/common/compat_time.c \ src/common/util.c \ src/common/util_process.c \ src/common/token_bucket.c \ src/common/workqueue.c \ $(libor_extra_source) \ Loading Loading @@ -71,7 +70,6 @@ COMMONHEADERS = \ src/common/timers.h \ src/common/token_bucket.h \ src/common/util.h \ src/common/util_process.h \ src/common/workqueue.h noinst_HEADERS+= $(COMMONHEADERS)