#include "orconfig.h"
#include "lib/process/restrict.h"
#include "lib/intmath/cmp.h"
#include "lib/log/log.h"
#include "lib/log/util_bug.h"
#include "lib/net/socket.h"
#ifdef HAVE_SYS_MMAN_H
#include <sys/mman.h>
#endif
#include <errno.h>
#include <stdlib.h>
#include <string.h>
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
#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
#undef TRIED_TO_DISABLE
return r;
}
#if defined(HAVE_MLOCKALL) && HAVE_DECL_MLOCKALL && defined(RLIMIT_MEMLOCK)
#define HAVE_UNIX_MLOCKALL
#endif
#ifdef HAVE_UNIX_MLOCKALL
static int
tor_set_max_memlock(void)
{
struct rlimit limit;
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
int
tor_mlockall(void)
{
static int memory_lock_attempted = 0;
if (memory_lock_attempted) {
return 1;
}
memory_lock_attempted = 1;
#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) {
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
log_warn(LD_GENERAL, "Unable to lock memory pages. mlockall() unsupported?");
return -1;
#endif
}
#define ULIMIT_BUFFER 32
int
set_max_file_descriptors(rlim_t limit, int *max_out)
{
if (limit < ULIMIT_BUFFER) {
log_warn(LD_CONFIG,
"ConnLimit must be at least %d. Failing.", ULIMIT_BUFFER);
return -1;
}
#ifndef HAVE_GETRLIMIT
#if defined(CYGWIN) || defined(__CYGWIN__)
const char *platform = "Cygwin";
const unsigned long MAX_CONNECTIONS = 3200;
#elif defined(_WIN32)
const char *platform = "Windows";
const unsigned long MAX_CONNECTIONS = 15000;
#else
const char *platform = "unknown platforms with no getrlimit()";
const unsigned long MAX_CONNECTIONS = 15000;
#endif
log_fn(LOG_INFO, LD_NET,
"This platform is missing getrlimit(). Proceeding.");
if (limit > MAX_CONNECTIONS) {
log_warn(LD_CONFIG,
"We do not support more than %lu file descriptors "
"on %s. Tried to raise to %lu.",
(unsigned long)MAX_CONNECTIONS, platform, (unsigned long)limit);
return -1;
}
limit = MAX_CONNECTIONS;
#else
struct rlimit rlim;
if (getrlimit(RLIMIT_NOFILE, &rlim) != 0) {
log_warn(LD_NET, "Could not get maximum number of file descriptors: %s",
strerror(errno));
return -1;
}
if (rlim.rlim_max < limit) {
log_warn(LD_CONFIG,"We need %lu file descriptors available, and we're "
"limited to %lu. Please change your ulimit -n.",
(unsigned long)limit, (unsigned long)rlim.rlim_max);
return -1;
}
if (rlim.rlim_max > rlim.rlim_cur) {
log_info(LD_NET,"Raising max file descriptors from %lu to %lu.",
(unsigned long)rlim.rlim_cur, (unsigned long)rlim.rlim_max);
}
*max_out = (int)rlim.rlim_cur - ULIMIT_BUFFER;
set_max_sockets(*max_out);
rlim.rlim_cur = rlim.rlim_max;
if (setrlimit(RLIMIT_NOFILE, &rlim) != 0) {
int couldnt_set = 1;
const int setrlimit_errno = errno;
#ifdef OPEN_MAX
uint64_t try_limit = OPEN_MAX - ULIMIT_BUFFER;
if (errno == EINVAL && try_limit < (uint64_t) rlim.rlim_cur) {
rlim.rlim_cur = MIN((rlim_t) try_limit, rlim.rlim_cur);
if (setrlimit(RLIMIT_NOFILE, &rlim) == 0) {
if (rlim.rlim_cur < (rlim_t)limit) {
log_warn(LD_CONFIG, "We are limited to %lu file descriptors by "
"OPEN_MAX (%lu), and ConnLimit is %lu. Changing "
"ConnLimit; sorry.",
(unsigned long)try_limit, (unsigned long)OPEN_MAX,
(unsigned long)limit);
} else {
log_info(LD_CONFIG, "Dropped connection limit to %lu based on "
"OPEN_MAX (%lu); Apparently, %lu was too high and rlimit "
"lied to us.",
(unsigned long)try_limit, (unsigned long)OPEN_MAX,
(unsigned long)rlim.rlim_max);
}
couldnt_set = 0;
}
}
#endif
if (couldnt_set) {
log_warn(LD_CONFIG,"Couldn't set maximum number of file descriptors: %s",
strerror(setrlimit_errno));
}
}
limit = rlim.rlim_cur;
#endif
if (limit > INT_MAX)
limit = INT_MAX;
tor_assert(max_out);
*max_out = (int)limit - ULIMIT_BUFFER;
set_max_sockets(*max_out);
return 0;
}