aboutsummaryrefslogtreecommitdiff
path: root/src/common/compat.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/common/compat.c')
-rw-r--r--src/common/compat.c502
1 files changed, 0 insertions, 502 deletions
diff --git a/src/common/compat.c b/src/common/compat.c
index 48e706456e..d03c4a501a 100644
--- a/src/common/compat.c
+++ b/src/common/compat.c
@@ -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,
@@ -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