diff options
Diffstat (limited to 'src/common/compat.c')
-rw-r--r-- | src/common/compat.c | 685 |
1 files changed, 517 insertions, 168 deletions
diff --git a/src/common/compat.c b/src/common/compat.c index a4e50747cd..59e3898deb 100644 --- a/src/common/compat.c +++ b/src/common/compat.c @@ -1,6 +1,6 @@ /* Copyright (c) 2003-2004, Roger Dingledine * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2011, The Tor Project, Inc. */ + * Copyright (c) 2007-2012, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** @@ -15,13 +15,17 @@ /* This is required on rh7 to make strptime not complain. * We also need it to make memmem get defined (where available) */ -/* XXXX023 We should just use AC_USE_SYSTEM_EXTENSIONS in our autoconf, - * and get this (and other important stuff!) automatically */ +/* XXXX024 We should just use AC_USE_SYSTEM_EXTENSIONS in our autoconf, + * and get this (and other important stuff!) automatically. Once we do that, + * make sure to also change the extern char **environ detection in + * configure.in, because whether that is declared or not depends on whether + * we have _GNU_SOURCE defined! Maybe that means that once we take this out, + * we can also take out the configure check. */ #define _GNU_SOURCE #include "compat.h" -#ifdef MS_WINDOWS +#ifdef _WIN32 #include <process.h> #include <windows.h> #include <sys/locking.h> @@ -51,6 +55,9 @@ #ifdef HAVE_ARPA_INET_H #include <arpa/inet.h> #endif +#ifdef HAVE_CRT_EXTERNS_H +#include <crt_externs.h> +#endif #ifndef HAVE_GETTIMEOFDAY #ifdef HAVE_FTIME @@ -58,6 +65,14 @@ #endif #endif +/* Includes for the process attaching prevention */ +#if defined(HAVE_SYS_PRCTL_H) && defined(__linux__) +#include <sys/prctl.h> +#elif defined(__APPLE__) +#include <sys/types.h> +#include <sys/ptrace.h> +#endif + #ifdef HAVE_NETDB_H #include <netdb.h> #endif @@ -103,7 +118,45 @@ #include "strlcat.c" #endif -#ifdef HAVE_SYS_MMAN_H +/** As open(path, flags, mode), but return an fd with the close-on-exec mode + * set. */ +int +tor_open_cloexec(const char *path, int flags, unsigned mode) +{ + int fd; +#ifdef O_CLOEXEC + fd = open(path, flags|O_CLOEXEC, mode); + if (fd >= 0) + return fd; + /* If we got an error, see if it is EINVAL. EINVAL might indicate that, + * even though we were built on a system with O_CLOEXEC support, we + * are running on one without. */ + if (errno != EINVAL) + return -1; +#endif + + fd = open(path, flags, mode); +#ifdef FD_CLOEXEC + if (fd >= 0) + fcntl(fd, F_SETFD, FD_CLOEXEC); +#endif + return fd; +} + +/** As fopen(path,mode), but ensures that the O_CLOEXEC bit is set on the + * underlying file handle. */ +FILE * +tor_fopen_cloexec(const char *path, const char *mode) +{ + FILE *result = fopen(path, mode); +#ifdef FD_CLOEXEC + if (result != NULL) + fcntl(fileno(result), F_SETFD, FD_CLOEXEC); +#endif + return result; +} + +#if defined(HAVE_SYS_MMAN_H) || defined(RUNNING_DOXYGEN) /** Try to create a memory mapping for <b>filename</b> and return it. On * failure, return NULL. Sets errno properly, using ERANGE to mean * "empty file". */ @@ -118,7 +171,7 @@ tor_mmap_file(const char *filename) tor_assert(filename); - fd = open(filename, O_RDONLY, 0); + fd = tor_open_cloexec(filename, O_RDONLY, 0); if (fd<0) { int save_errno = errno; int severity = (errno == ENOENT) ? LOG_INFO : LOG_WARN; @@ -168,47 +221,55 @@ tor_munmap_file(tor_mmap_t *handle) munmap((char*)handle->data, handle->mapping_size); tor_free(handle); } -#elif defined(MS_WINDOWS) +#elif defined(_WIN32) tor_mmap_t * tor_mmap_file(const char *filename) { TCHAR tfilename[MAX_PATH]= {0}; tor_mmap_t *res = tor_malloc_zero(sizeof(tor_mmap_t)); int empty = 0; - res->file_handle = INVALID_HANDLE_VALUE; + HANDLE file_handle = INVALID_HANDLE_VALUE; + DWORD size_low, size_high; + uint64_t real_size; res->mmap_handle = NULL; #ifdef UNICODE mbstowcs(tfilename,filename,MAX_PATH); #else strlcpy(tfilename,filename,MAX_PATH); #endif - res->file_handle = CreateFile(tfilename, - GENERIC_READ, FILE_SHARE_READ, - NULL, - OPEN_EXISTING, - FILE_ATTRIBUTE_NORMAL, - 0); + file_handle = CreateFile(tfilename, + GENERIC_READ, FILE_SHARE_READ, + NULL, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + 0); - if (res->file_handle == INVALID_HANDLE_VALUE) + if (file_handle == INVALID_HANDLE_VALUE) goto win_err; - res->size = GetFileSize(res->file_handle, NULL); + size_low = GetFileSize(file_handle, &size_high); - if (res->size == 0) { + if (size_low == INVALID_FILE_SIZE && GetLastError() != NO_ERROR) { + log_warn(LD_FS,"Error getting size of \"%s\".",filename); + goto win_err; + } + if (size_low == 0 && size_high == 0) { log_info(LD_FS,"File \"%s\" is empty. Ignoring.",filename); empty = 1; goto err; } + real_size = (((uint64_t)size_high)<<32) | size_low; + if (real_size > SIZE_MAX) { + log_warn(LD_FS,"File \"%s\" is too big to map; not trying.",filename); + goto err; + } + res->size = real_size; - res->mmap_handle = CreateFileMapping(res->file_handle, + res->mmap_handle = CreateFileMapping(file_handle, NULL, PAGE_READONLY, -#if SIZEOF_SIZE_T > 4 - (res->base.size >> 32), -#else - 0, -#endif - (res->size & 0xfffffffful), + size_high, + size_low, NULL); if (res->mmap_handle == NULL) goto win_err; @@ -218,6 +279,7 @@ tor_mmap_file(const char *filename) if (!res->data) goto win_err; + CloseHandle(file_handle); return res; win_err: { DWORD e = GetLastError(); @@ -234,6 +296,8 @@ tor_mmap_file(const char *filename) err: if (empty) errno = ERANGE; + if (file_handle != INVALID_HANDLE_VALUE) + CloseHandle(file_handle); tor_munmap_file(res); return NULL; } @@ -247,8 +311,6 @@ tor_munmap_file(tor_mmap_t *handle) if (handle->mmap_handle != NULL) CloseHandle(handle->mmap_handle); - if (handle->file_handle != INVALID_HANDLE_VALUE) - CloseHandle(handle->file_handle); tor_free(handle); } #else @@ -270,7 +332,7 @@ tor_munmap_file(tor_mmap_t *handle) { char *d = (char*)handle->data; tor_free(d); - memset(handle, 0, sizeof(tor_mmap_t)); + memwipe(handle, 0, sizeof(tor_mmap_t)); tor_free(handle); } #endif @@ -304,7 +366,7 @@ tor_vsnprintf(char *str, size_t size, const char *format, va_list args) return -1; /* no place for the NUL */ if (size > SIZE_T_CEILING) return -1; -#ifdef MS_WINDOWS +#ifdef _WIN32 r = _vsnprintf(str, size, format, args); #else r = vsnprintf(str, size, format, args); @@ -447,10 +509,13 @@ tor_memmem(const void *_haystack, size_t hlen, #endif } -/* Tables to implement ctypes-replacement TOR_IS*() functions. Each table +/** + * Tables to implement ctypes-replacement TOR_IS*() functions. Each table * has 256 bits to look up whether a character is in some set or not. This * fails on non-ASCII platforms, but it is hard to find a platform whose * character set is not a superset of ASCII nowadays. */ + +/**@{*/ const uint32_t TOR_ISALPHA_TABLE[8] = { 0, 0, 0x7fffffe, 0x7fffffe, 0, 0, 0, 0 }; const uint32_t TOR_ISALNUM_TABLE[8] = @@ -463,8 +528,10 @@ const uint32_t TOR_ISPRINT_TABLE[8] = { 0, 0xffffffff, 0xffffffff, 0x7fffffff, 0, 0, 0, 0x0 }; const uint32_t TOR_ISUPPER_TABLE[8] = { 0, 0, 0x7fffffe, 0, 0, 0, 0, 0 }; const uint32_t TOR_ISLOWER_TABLE[8] = { 0, 0, 0, 0x7fffffe, 0, 0, 0, 0 }; -/* Upper-casing and lowercasing tables to map characters to upper/lowercase - * equivalents. */ + +/** Upper-casing and lowercasing tables to map characters to upper/lowercase + * equivalents. Used by tor_toupper() and tor_tolower(). */ +/**@{*/ const char TOR_TOUPPER_TABLE[256] = { 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15, 16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31, @@ -501,6 +568,22 @@ const char TOR_TOLOWER_TABLE[256] = { 224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239, 240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255, }; +/**@}*/ + +/** Helper for tor_strtok_r_impl: Advances cp past all characters in + * <b>sep</b>, and returns its new value. */ +static char * +strtok_helper(char *cp, const char *sep) +{ + if (sep[1]) { + while (*cp && strchr(sep, *cp)) + ++cp; + } else { + while (*cp && *cp == *sep) + ++cp; + } + return cp; +} /** Implementation of strtok_r for platforms whose coders haven't figured out * how to write one. Hey guys! You can use this code here for free! */ @@ -508,19 +591,22 @@ char * tor_strtok_r_impl(char *str, const char *sep, char **lasts) { char *cp, *start; - if (str) + tor_assert(*sep); + if (str) { + str = strtok_helper(str, sep); + if (!*str) + return NULL; start = cp = *lasts = str; - else if (!*lasts) + } else if (!*lasts || !**lasts) { return NULL; - else + } else { start = cp = *lasts; + } - tor_assert(*sep); if (sep[1]) { while (*cp && !strchr(sep, *cp)) ++cp; } else { - tor_assert(strlen(sep) == 1); cp = strchr(cp, *sep); } @@ -528,12 +614,12 @@ tor_strtok_r_impl(char *str, const char *sep, char **lasts) *lasts = NULL; } else { *cp++ = '\0'; - *lasts = cp; + *lasts = strtok_helper(cp, sep); } return start; } -#ifdef MS_WINDOWS +#ifdef _WIN32 /** Take a filename and return a pointer to its final element. This * function is called on __FILE__ to fix a MSVC nit where __FILE__ * contains the full path to the file. This is bad, because it @@ -633,7 +719,7 @@ set_uint64(void *cp, uint64_t v) int replace_file(const char *from, const char *to) { -#ifndef MS_WINDOWS +#ifndef _WIN32 return rename(from,to); #else switch (file_status(to)) @@ -695,14 +781,14 @@ tor_lockfile_lock(const char *filename, int blocking, int *locked_out) *locked_out = 0; log_info(LD_FS, "Locking \"%s\"", filename); - fd = open(filename, O_RDWR|O_CREAT|O_TRUNC, 0600); + fd = tor_open_cloexec(filename, O_RDWR|O_CREAT|O_TRUNC, 0600); if (fd < 0) { log_warn(LD_FS,"Couldn't open \"%s\" for locking: %s", filename, strerror(errno)); return NULL; } -#ifdef WIN32 +#ifdef _WIN32 _lseek(fd, 0, SEEK_SET); if (_locking(fd, blocking ? _LK_LOCK : _LK_NBLCK, 1) < 0) { if (errno != EACCES && errno != EDEADLOCK) @@ -751,7 +837,7 @@ tor_lockfile_unlock(tor_lockfile_t *lockfile) tor_assert(lockfile); log_info(LD_FS, "Unlocking \"%s\"", lockfile->filename); -#ifdef WIN32 +#ifdef _WIN32 _lseek(lockfile->fd, 0, SEEK_SET); if (_locking(lockfile->fd, _LK_UNLCK, 1) < 0) { log_warn(LD_FS,"Error unlocking \"%s\": %s", lockfile->filename, @@ -787,7 +873,7 @@ tor_lockfile_unlock(tor_lockfile_t *lockfile) off_t tor_fd_getpos(int fd) { -#ifdef WIN32 +#ifdef _WIN32 return (off_t) _lseek(fd, 0, SEEK_CUR); #else return (off_t) lseek(fd, 0, SEEK_CUR); @@ -798,7 +884,7 @@ tor_fd_getpos(int fd) int tor_fd_seekend(int fd) { -#ifdef WIN32 +#ifdef _WIN32 return _lseek(fd, 0, SEEK_END) < 0 ? -1 : 0; #else return lseek(fd, 0, SEEK_END) < 0 ? -1 : 0; @@ -851,7 +937,7 @@ tor_close_socket(tor_socket_t s) * tor_close_socket to close sockets, and always using close() on * files. */ -#if defined(MS_WINDOWS) +#if defined(_WIN32) r = closesocket(s); #else r = close(s); @@ -872,7 +958,7 @@ tor_close_socket(tor_socket_t s) } else { int err = tor_socket_errno(-1); log_info(LD_NET, "Close returned an error: %s", tor_socket_strerror(err)); -#ifdef WIN32 +#ifdef _WIN32 if (err != WSAENOTSOCK) --n_sockets_open; #else @@ -922,13 +1008,33 @@ mark_socket_open(tor_socket_t s) tor_socket_t tor_open_socket(int domain, int type, int protocol) { - tor_socket_t s = socket(domain, type, protocol); - if (SOCKET_OK(s)) { - socket_accounting_lock(); - ++n_sockets_open; - mark_socket_open(s); - socket_accounting_unlock(); - } + tor_socket_t s; +#ifdef SOCK_CLOEXEC + s = socket(domain, type|SOCK_CLOEXEC, protocol); + if (SOCKET_OK(s)) + goto socket_ok; + /* If we got an error, see if it is EINVAL. EINVAL might indicate that, + * even though we were built on a system with SOCK_CLOEXEC support, we + * are running on one without. */ + if (errno != EINVAL) + return s; +#endif /* SOCK_CLOEXEC */ + + s = socket(domain, type, protocol); + if (! SOCKET_OK(s)) + return s; + +#if defined(FD_CLOEXEC) + fcntl(s, F_SETFD, FD_CLOEXEC); +#endif + + goto socket_ok; /* So that socket_ok will not be unused. */ + + socket_ok: + socket_accounting_lock(); + ++n_sockets_open; + mark_socket_open(s); + socket_accounting_unlock(); return s; } @@ -936,13 +1042,34 @@ tor_open_socket(int domain, int type, int protocol) tor_socket_t tor_accept_socket(tor_socket_t sockfd, struct sockaddr *addr, socklen_t *len) { - tor_socket_t s = accept(sockfd, addr, len); - if (SOCKET_OK(s)) { - socket_accounting_lock(); - ++n_sockets_open; - mark_socket_open(s); - socket_accounting_unlock(); - } + tor_socket_t s; +#if defined(HAVE_ACCEPT4) && defined(SOCK_CLOEXEC) + s = accept4(sockfd, addr, len, SOCK_CLOEXEC); + if (SOCKET_OK(s)) + goto socket_ok; + /* If we got an error, see if it is ENOSYS. ENOSYS indicates that, + * even though we were built on a system with accept4 support, we + * are running on one without. Also, check for EINVAL, which indicates that + * we are missing SOCK_CLOEXEC support. */ + if (errno != EINVAL && errno != ENOSYS) + return s; +#endif + + s = accept(sockfd, addr, len); + if (!SOCKET_OK(s)) + return s; + +#if defined(FD_CLOEXEC) + fcntl(s, F_SETFD, FD_CLOEXEC); +#endif + + goto socket_ok; /* So that socket_ok will not be unused. */ + + socket_ok: + socket_accounting_lock(); + ++n_sockets_open; + mark_socket_open(s); + socket_accounting_unlock(); return s; } @@ -962,7 +1089,7 @@ get_n_open_sockets(void) void set_socket_nonblocking(tor_socket_t socket) { -#if defined(MS_WINDOWS) +#if defined(_WIN32) unsigned long nonblocking = 1; ioctlsocket(socket, FIONBIO, (unsigned long*) &nonblocking); #else @@ -991,22 +1118,45 @@ int tor_socketpair(int family, int type, int protocol, tor_socket_t fd[2]) { //don't use win32 socketpairs (they are always bad) -#if defined(HAVE_SOCKETPAIR) && !defined(MS_WINDOWS) +#if defined(HAVE_SOCKETPAIR) && !defined(_WIN32) int r; + +#ifdef SOCK_CLOEXEC + r = socketpair(family, type|SOCK_CLOEXEC, protocol, fd); + if (r == 0) + goto sockets_ok; + /* If we got an error, see if it is EINVAL. EINVAL might indicate that, + * even though we were built on a system with SOCK_CLOEXEC support, we + * are running on one without. */ + if (errno != EINVAL) + return -errno; +#endif + r = socketpair(family, type, protocol, fd); - if (r == 0) { - socket_accounting_lock(); - if (fd[0] >= 0) { - ++n_sockets_open; - mark_socket_open(fd[0]); - } - if (fd[1] >= 0) { - ++n_sockets_open; - mark_socket_open(fd[1]); - } - socket_accounting_unlock(); + if (r < 0) + return -errno; + +#if defined(FD_CLOEXEC) + if (SOCKET_OK(fd[0])) + fcntl(fd[0], F_SETFD, FD_CLOEXEC); + if (SOCKET_OK(fd[1])) + fcntl(fd[1], F_SETFD, FD_CLOEXEC); +#endif + goto sockets_ok; /* So that sockets_ok will not be unused. */ + + sockets_ok: + socket_accounting_lock(); + if (SOCKET_OK(fd[0])) { + ++n_sockets_open; + mark_socket_open(fd[0]); } - return r < 0 ? -errno : r; + if (SOCKET_OK(fd[1])) { + ++n_sockets_open; + mark_socket_open(fd[1]); + } + socket_accounting_unlock(); + + return 0; #else /* This socketpair does not work when localhost is down. So * it's really not the same thing at all. But it's close enough @@ -1026,7 +1176,7 @@ tor_socketpair(int family, int type, int protocol, tor_socket_t fd[2]) || family != AF_UNIX #endif ) { -#ifdef MS_WINDOWS +#ifdef _WIN32 return -WSAEAFNOSUPPORT; #else return -EAFNOSUPPORT; @@ -1037,7 +1187,7 @@ tor_socketpair(int family, int type, int protocol, tor_socket_t fd[2]) } listener = tor_open_socket(AF_INET, type, 0); - if (listener < 0) + if (!SOCKET_OK(listener)) return -tor_socket_errno(-1); memset(&listen_addr, 0, sizeof(listen_addr)); listen_addr.sin_family = AF_INET; @@ -1050,7 +1200,7 @@ tor_socketpair(int family, int type, int protocol, tor_socket_t fd[2]) goto tidy_up_and_fail; connector = tor_open_socket(AF_INET, type, 0); - if (connector < 0) + if (!SOCKET_OK(connector)) goto tidy_up_and_fail; /* We want to find out the port number to connect to. */ size = sizeof(connect_addr); @@ -1065,7 +1215,7 @@ tor_socketpair(int family, int type, int protocol, tor_socket_t fd[2]) size = sizeof(listen_addr); acceptor = tor_accept_socket(listener, (struct sockaddr *) &listen_addr, &size); - if (acceptor < 0) + if (!SOCKET_OK(acceptor)) goto tidy_up_and_fail; if (size != sizeof(listen_addr)) goto abort_tidy_up_and_fail; @@ -1086,7 +1236,7 @@ tor_socketpair(int family, int type, int protocol, tor_socket_t fd[2]) return 0; abort_tidy_up_and_fail: -#ifdef MS_WINDOWS +#ifdef _WIN32 saved_errno = WSAECONNABORTED; #else saved_errno = ECONNABORTED; /* I hope this is portable and appropriate. */ @@ -1108,13 +1258,16 @@ tor_socketpair(int family, int type, int protocol, tor_socket_t fd[2]) * tell Tor it's allowed to use. */ #define ULIMIT_BUFFER 32 /* keep 32 extra fd's beyond _ConnLimit */ -/** Learn the maximum allowed number of file descriptors. (Some systems - * have a low soft limit. +/** Learn the maximum allowed number of file descriptors, and tell the system + * we want to use up to that number. (Some systems have a low soft limit, and + * let us set it higher.) * * We compute this by finding the largest number that we can use. * If we can't find a number greater than or equal to <b>limit</b>, * then we fail: return -1. * + * If <b>limit</b> is 0, then do not adjust the current maximum. + * * Otherwise, return 0 and store the maximum we found inside <b>max_out</b>.*/ int set_max_file_descriptors(rlim_t limit, int *max_out) @@ -1128,7 +1281,7 @@ set_max_file_descriptors(rlim_t limit, int *max_out) #if defined(CYGWIN) || defined(__CYGWIN__) const char *platform = "Cygwin"; const unsigned long MAX_CONNECTIONS = 3200; -#elif defined(MS_WINDOWS) +#elif defined(_WIN32) const char *platform = "Windows"; const unsigned long MAX_CONNECTIONS = 15000; #else @@ -1147,14 +1300,20 @@ set_max_file_descriptors(rlim_t limit, int *max_out) limit = MAX_CONNECTIONS; #else /* HAVE_GETRLIMIT */ struct rlimit rlim; - tor_assert(limit > 0); if (getrlimit(RLIMIT_NOFILE, &rlim) != 0) { log_warn(LD_NET, "Could not get maximum number of file descriptors: %s", strerror(errno)); return -1; } - + if (limit == 0) { + /* If limit == 0, return the maximum value without setting it. */ + limit = rlim.rlim_max; + if (limit > INT_MAX) + limit = INT_MAX; + *max_out = (int)limit - ULIMIT_BUFFER; + return 0; + } 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.", @@ -1211,7 +1370,7 @@ set_max_file_descriptors(rlim_t limit, int *max_out) return 0; } -#ifndef MS_WINDOWS +#ifndef _WIN32 /** Log details of current user and group credentials. Return 0 on * success. Logs and return -1 on failure. */ @@ -1288,31 +1447,19 @@ log_credential_status(void) return -1; } else { int i, retval = 0; - char *strgid; char *s = NULL; - smartlist_t *elts = smartlist_create(); + smartlist_t *elts = smartlist_new(); for (i = 0; i<ngids; i++) { - strgid = tor_malloc(11); - if (tor_snprintf(strgid, 11, "%u", (unsigned)sup_gids[i]) < 0) { - log_warn(LD_GENERAL, "Error printing supplementary GIDs"); - tor_free(strgid); - retval = -1; - goto error; - } - smartlist_add(elts, strgid); + 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); - error: tor_free(s); - SMARTLIST_FOREACH(elts, char *, cp, - { - tor_free(cp); - }); + SMARTLIST_FOREACH(elts, char *, cp, tor_free(cp)); smartlist_free(elts); tor_free(sup_gids); @@ -1329,7 +1476,7 @@ log_credential_status(void) int switch_id(const char *user) { -#ifndef MS_WINDOWS +#ifndef _WIN32 struct passwd *pw = NULL; uid_t old_uid; gid_t old_gid; @@ -1464,6 +1611,58 @@ switch_id(const char *user) #endif } +/* 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, attempted; + r = -1; + attempted = 0; + 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) +#ifdef PR_SET_DUMPABLE + attempted = 1; + r = prctl(PR_SET_DUMPABLE, 0); +#endif +#endif +#if defined(__APPLE__) && defined(PT_DENY_ATTACH) + if (r < 0) { + attempted = 1; + r = ptrace(PT_DENY_ATTACH, 0, 0, 0); + } +#endif + + // XXX: TODO - Mac OS X has dtrace and this may be disabled. + // XXX: TODO - Windows probably has something similar + if (r == 0 && attempted) { + log_debug(LD_CONFIG,"Debugger attachment disabled for " + "unprivileged users."); + return 1; + } else if (attempted) { + log_warn(LD_CONFIG, "Unable to disable debugger attaching: %s", + strerror(errno)); + } + return r; +} + #ifdef HAVE_PWD_H /** Allocate and return a string containing the home directory for the * user <b>username</b>. Only works on posix-like systems. */ @@ -1481,14 +1680,18 @@ get_user_homedir(const char *username) } #endif -/** Modify <b>fname</b> to contain the name of the directory */ +/** Modify <b>fname</b> to contain the name of its parent directory. Doesn't + * actually examine the filesystem; does a purely syntactic modification. + * + * The parent of the root director is considered to be iteself. + * */ int get_parent_directory(char *fname) { char *cp; int at_end = 1; tor_assert(fname); -#ifdef MS_WINDOWS +#ifdef _WIN32 /* If we start with, say, c:, then don't consider that the start of the path */ if (fname[0] && fname[1] == ':') { @@ -1503,13 +1706,18 @@ get_parent_directory(char *fname) */ cp = fname + strlen(fname); at_end = 1; - while (--cp > fname) { + while (--cp >= fname) { int is_sep = (*cp == '/' -#ifdef MS_WINDOWS +#ifdef _WIN32 || *cp == '\\' #endif ); if (is_sep) { + if (cp == fname) { + /* This is the first separator in the file name; don't remove it! */ + cp[1] = '\0'; + return 0; + } *cp = '\0'; if (! at_end) return 0; @@ -1520,6 +1728,101 @@ get_parent_directory(char *fname) return -1; } +#ifndef _WIN32 +/** Return a newly allocated string containing the output of getcwd(). Return + * NULL on failure. (We can't just use getcwd() into a PATH_MAX buffer, since + * Hurd hasn't got a PATH_MAX.) + */ +static char * +alloc_getcwd(void) +{ + int saved_errno = errno; +/* We use this as a starting path length. Not too large seems sane. */ +#define START_PATH_LENGTH 128 +/* Nobody has a maxpath longer than this, as far as I know. And if they + * do, they shouldn't. */ +#define MAX_SANE_PATH_LENGTH 4096 + size_t path_length = START_PATH_LENGTH; + char *path = tor_malloc(path_length); + + errno = 0; + while (getcwd(path, path_length) == NULL) { + if (errno == ERANGE && path_length < MAX_SANE_PATH_LENGTH) { + path_length*=2; + path = tor_realloc(path, path_length); + } else { + tor_free(path); + path = NULL; + break; + } + } + errno = saved_errno; + return path; +} +#endif + +/** Expand possibly relative path <b>fname</b> to an absolute path. + * Return a newly allocated string, possibly equal to <b>fname</b>. */ +char * +make_path_absolute(char *fname) +{ +#ifdef _WIN32 + char *absfname_malloced = _fullpath(NULL, fname, 1); + + /* We don't want to assume that tor_free can free a string allocated + * with malloc. On failure, return fname (it's better than nothing). */ + char *absfname = tor_strdup(absfname_malloced ? absfname_malloced : fname); + if (absfname_malloced) free(absfname_malloced); + + return absfname; +#else + char *absfname = NULL, *path = NULL; + + tor_assert(fname); + + if (fname[0] == '/') { + absfname = tor_strdup(fname); + } else { + path = alloc_getcwd(); + if (path) { + tor_asprintf(&absfname, "%s/%s", path, fname); + tor_free(path); + } else { + /* If getcwd failed, the best we can do here is keep using the + * relative path. (Perhaps / isn't readable by this UID/GID.) */ + log_warn(LD_GENERAL, "Unable to find current working directory: %s", + strerror(errno)); + absfname = tor_strdup(fname); + } + } + return absfname; +#endif +} + +#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 +#endif + +/** 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 + return environ; +#endif +} + /** Set *addr to the IP address (in dotted-quad notation) stored in c. * Return 1 on success, 0 if c is badly formatted. (Like inet_aton(c,addr), * but works on Windows and Solaris.) @@ -1577,7 +1880,7 @@ tor_inet_ntop(int af, const void *src, char *dst, size_t len) addr->s6_addr[12], addr->s6_addr[13], addr->s6_addr[14], addr->s6_addr[15]); } - if (strlen(buf) > len) + if ((strlen(buf) + 1) > len) /* +1 for \0 */ return NULL; strlcpy(dst, buf, len); return dst; @@ -1618,7 +1921,7 @@ tor_inet_ntop(int af, const void *src, char *dst, size_t len) } } *cp = '\0'; - if (strlen(buf) > len) + if ((strlen(buf) + 1) > len) /* +1 for \0 */ return NULL; strlcpy(dst, buf, len); return dst; @@ -1678,24 +1981,30 @@ tor_inet_pton(int af, const char *src, void *dst) return 0; if (TOR_ISXDIGIT(*src)) { char *next; + ssize_t len; long r = strtol(src, &next, 16); - if (next > 4+src) - return 0; - if (next == src) - return 0; - if (r<0 || r>65536) + tor_assert(next != NULL); + tor_assert(next != src); + + len = *next == '\0' ? eow - src : next - src; + if (len > 4) return 0; + if (len > 1 && !TOR_ISXDIGIT(src[1])) + return 0; /* 0x is not valid */ + tor_assert(r >= 0); + tor_assert(r < 65536); words[i++] = (uint16_t)r; setWords++; src = next; if (*src != ':' && src != eow) return 0; ++src; - } else if (*src == ':' && i > 0 && gapPos==-1) { + } else if (*src == ':' && i > 0 && gapPos == -1) { gapPos = i; ++src; - } else if (*src == ':' && i == 0 && src[1] == ':' && gapPos==-1) { + } else if (*src == ':' && i == 0 && src+1 < eow && src[1] == ':' && + gapPos == -1) { gapPos = i; src += 2; } else { @@ -1755,7 +2064,7 @@ tor_lookup_hostname(const char *name, uint32_t *addr) void tor_init_weak_random(unsigned seed) { -#ifdef MS_WINDOWS +#ifdef _WIN32 srand(seed); #else srandom(seed); @@ -1768,7 +2077,7 @@ tor_init_weak_random(unsigned seed) long tor_weak_random(void) { -#ifdef MS_WINDOWS +#ifdef _WIN32 return rand(); #else return random(); @@ -1792,17 +2101,14 @@ get_uname(void) #ifdef HAVE_UNAME if (uname(&u) != -1) { /* (Linux says 0 is success, Solaris says 1 is success) */ - tor_snprintf(uname_result, sizeof(uname_result), "%s %s", - u.sysname, u.machine); + strlcpy(uname_result, u.sysname, sizeof(uname_result)); } else #endif { -#ifdef MS_WINDOWS +#ifdef _WIN32 OSVERSIONINFOEX info; int i; const char *plat = NULL; - const char *extra = NULL; - char acsd[MAX_PATH] = {0}; static struct { unsigned major; unsigned minor; const char *version; } win_version_table[] = { @@ -1827,20 +2133,11 @@ get_uname(void) uname_result_is_set = 1; return uname_result; } -#ifdef UNICODE - wcstombs(acsd, info.szCSDVersion, MAX_PATH); -#else - strlcpy(acsd, info.szCSDVersion, sizeof(acsd)); -#endif if (info.dwMajorVersion == 4 && info.dwMinorVersion == 0) { if (info.dwPlatformId == VER_PLATFORM_WIN32_NT) plat = "Windows NT 4.0"; else plat = "Windows 95"; - if (acsd[1] == 'B') - extra = "OSR2 (B)"; - else if (acsd[1] == 'C') - extra = "OSR2 (C)"; } else { for (i=0; win_version_table[i].major>0; ++i) { if (win_version_table[i].major == info.dwMajorVersion && @@ -1850,39 +2147,25 @@ get_uname(void) } } } - if (plat && !strcmp(plat, "Windows 98")) { - if (acsd[1] == 'A') - extra = "SE (A)"; - else if (acsd[1] == 'B') - extra = "SE (B)"; - } if (plat) { - if (!extra) - extra = acsd; - tor_snprintf(uname_result, sizeof(uname_result), "%s %s", - plat, extra); + strlcpy(uname_result, plat, sizeof(uname_result)); } else { if (info.dwMajorVersion > 6 || (info.dwMajorVersion==6 && info.dwMinorVersion>2)) tor_snprintf(uname_result, sizeof(uname_result), - "Very recent version of Windows [major=%d,minor=%d] %s", - (int)info.dwMajorVersion,(int)info.dwMinorVersion, - acsd); + "Very recent version of Windows [major=%d,minor=%d]", + (int)info.dwMajorVersion,(int)info.dwMinorVersion); else tor_snprintf(uname_result, sizeof(uname_result), - "Unrecognized version of Windows [major=%d,minor=%d] %s", - (int)info.dwMajorVersion,(int)info.dwMinorVersion, - acsd); + "Unrecognized version of Windows [major=%d,minor=%d]", + (int)info.dwMajorVersion,(int)info.dwMinorVersion); } #if !defined (WINCE) -#ifdef VER_SUITE_BACKOFFICE - if (info.wProductType == VER_NT_DOMAIN_CONTROLLER) { - strlcat(uname_result, " [domain controller]", sizeof(uname_result)); - } else if (info.wProductType == VER_NT_SERVER) { - strlcat(uname_result, " [server]", sizeof(uname_result)); - } else if (info.wProductType == VER_NT_WORKSTATION) { - strlcat(uname_result, " [workstation]", sizeof(uname_result)); - } +#ifdef VER_NT_SERVER + if (info.wProductType == VER_NT_SERVER || + info.wProductType == VER_NT_DOMAIN_CONTROLLER) { + strlcat(uname_result, " [server]", sizeof(uname_result)); + } #endif #endif #else @@ -1995,13 +2278,59 @@ spawn_exit(void) #endif } +/** Implementation logic for compute_num_cpus(). */ +static int +compute_num_cpus_impl(void) +{ +#ifdef _WIN32 + SYSTEM_INFO info; + memset(&info, 0, sizeof(info)); + GetSystemInfo(&info); + if (info.dwNumberOfProcessors >= 1 && info.dwNumberOfProcessors < INT_MAX) + return (int)info.dwNumberOfProcessors; + else + return -1; +#elif defined(HAVE_SYSCONF) && defined(_SC_NPROCESSORS_CONF) + long cpus = sysconf(_SC_NPROCESSORS_CONF); + if (cpus >= 1 && cpus < INT_MAX) + return (int)cpus; + else + return -1; +#else + return -1; +#endif +} + +#define MAX_DETECTABLE_CPUS 16 + +/** Return how many CPUs we are running with. We assume that nobody is + * using hot-swappable CPUs, so we don't recompute this after the first + * time. Return -1 if we don't know how to tell the number of CPUs on this + * system. + */ +int +compute_num_cpus(void) +{ + static int num_cpus = -2; + if (num_cpus == -2) { + num_cpus = compute_num_cpus_impl(); + tor_assert(num_cpus != -2); + if (num_cpus > MAX_DETECTABLE_CPUS) + log_notice(LD_GENERAL, "Wow! I detected that you have %d CPUs. I " + "will not autodetect any more than %d, though. If you " + "want to configure more, set NumCPUs in your torrc", + num_cpus, MAX_DETECTABLE_CPUS); + } + return num_cpus; +} + /** Set *timeval to the current time of day. On error, log and terminate. * (Same as gettimeofday(timeval,NULL), but never returns -1.) */ void tor_gettimeofday(struct timeval *timeval) { -#ifdef MS_WINDOWS +#ifdef _WIN32 /* Epoch bias copied from perl: number of units between windows epoch and * Unix epoch. */ #define EPOCH_BIAS U64_LITERAL(116444736000000000) @@ -2046,12 +2375,18 @@ tor_gettimeofday(struct timeval *timeval) return; } -#if defined(TOR_IS_MULTITHREADED) && !defined(MS_WINDOWS) +#if defined(TOR_IS_MULTITHREADED) && !defined(_WIN32) /** Defined iff we need to add locks when defining fake versions of reentrant * versions of time-related functions. */ #define TIME_FNS_NEED_LOCKS #endif +/** Helper: Deal with confused or out-of-bounds values from localtime_r and + * friends. (On some platforms, they can give out-of-bounds values or can + * return NULL.) If <b>islocal</b>, this is a localtime result; otherwise + * it's from gmtime. The function returned <b>r</b>, when given <b>timep</b> + * as its input. If we need to store new results, store them in + * <b>resultbuf</b>. */ static struct tm * correct_tm(int islocal, const time_t *timep, struct tm *resultbuf, struct tm *r) @@ -2396,7 +2731,7 @@ tor_cond_new(void) { tor_cond_t *cond = tor_malloc_zero(sizeof(tor_cond_t)); InitializeCriticalSection(&cond->mutex); - cond->events = smartlist_create(); + cond->events = smartlist_new(); return cond; } void @@ -2590,7 +2925,7 @@ in_main_thread(void) * should call tor_socket_errno <em>at most once</em> on the failing * socket to get the error. */ -#if defined(MS_WINDOWS) +#if defined(_WIN32) int tor_socket_errno(tor_socket_t sock) { @@ -2606,7 +2941,7 @@ tor_socket_errno(tor_socket_t sock) } #endif -#if defined(MS_WINDOWS) +#if defined(_WIN32) #define E(code, s) { code, (s " [" #code " ]") } struct { int code; const char *msg; } windows_socket_errors[] = { E(WSAEINTR, "Interrupted function call"), @@ -2688,7 +3023,7 @@ tor_socket_strerror(int e) int network_init(void) { -#ifdef MS_WINDOWS +#ifdef _WIN32 /* This silly exercise is necessary before windows will allow * gethostbyname to work. */ WSADATA WSAData; @@ -2698,6 +3033,11 @@ network_init(void) log_warn(LD_NET,"Error initializing windows network layer: code was %d",r); return -1; } + if (sizeof(SOCKET) != sizeof(tor_socket_t)) { + log_warn(LD_BUG,"The tor_socket_t type does not match SOCKET in size; Tor " + "might not work. (Sizes are %d and %d respectively.)", + (int)sizeof(tor_socket_t), (int)sizeof(SOCKET)); + } /* WSAData.iMaxSockets might show the max sockets we're allowed to use. * We might use it to complain if we're trying to be a server but have * too few sockets available. */ @@ -2705,7 +3045,7 @@ network_init(void) return 0; } -#ifdef MS_WINDOWS +#ifdef _WIN32 /** Return a newly allocated string describing the windows system error code * <b>err</b>. Note that error codes are different from errno. Error codes * come from GetLastError() when a winapi call fails. errno is set only when @@ -2715,28 +3055,37 @@ format_win32_error(DWORD err) { TCHAR *str = NULL; char *result; + DWORD n; /* Somebody once decided that this interface was better than strerror(). */ - FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | + n = FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, err, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), - (LPVOID)&str, + (LPVOID)&str, 0, NULL); - if (str) { + if (str && n) { #ifdef UNICODE - char abuf[1024] = {0}; - wcstombs(abuf,str,1024); - result = tor_strdup(abuf); + size_t len; + if (n > 128*1024) + len = (128 * 1024) * 2 + 1; /* This shouldn't be possible, but let's + * make sure. */ + else + len = n * 2 + 1; + result = tor_malloc(len); + wcstombs(result,str,len); + result[len-1] = '\0'; #else result = tor_strdup(str); #endif - LocalFree(str); /* LocalFree != free() */ } else { result = tor_strdup("<unformattable error>"); } + if (str) { + LocalFree(str); /* LocalFree != free() */ + } return result; } #endif |