diff options
Diffstat (limited to 'src/common')
-rw-r--r-- | src/common/address.c | 2 | ||||
-rw-r--r-- | src/common/compat.c | 126 | ||||
-rw-r--r-- | src/common/compat.h | 5 | ||||
-rwxr-xr-x | src/common/gen_linux_syscalls.pl | 37 | ||||
-rwxr-xr-x | src/common/gen_server_ciphers.py | 115 | ||||
-rw-r--r-- | src/common/get_mozilla_ciphers.py | 210 | ||||
-rw-r--r-- | src/common/include.am | 16 | ||||
-rw-r--r-- | src/common/linux_syscalls.inc | 2 | ||||
-rw-r--r-- | src/common/log.c | 21 | ||||
-rw-r--r-- | src/common/sandbox.c | 219 | ||||
-rw-r--r-- | src/common/sandbox.h | 38 | ||||
-rw-r--r-- | src/common/torlog.h | 5 | ||||
-rw-r--r-- | src/common/tortls.c | 5 | ||||
-rw-r--r-- | src/common/util.c | 79 |
14 files changed, 399 insertions, 481 deletions
diff --git a/src/common/address.c b/src/common/address.c index 2825b123d..29d4c0447 100644 --- a/src/common/address.c +++ b/src/common/address.c @@ -264,7 +264,7 @@ tor_addr_lookup(const char *name, uint16_t family, tor_addr_t *addr) &((struct sockaddr_in6*)best->ai_addr)->sin6_addr); result = 0; } - freeaddrinfo(res); + sandbox_freeaddrinfo(res); return result; } return (err == EAI_AGAIN) ? 1 : -1; diff --git a/src/common/compat.c b/src/common/compat.c index 1c460b6ae..e25ecc462 100644 --- a/src/common/compat.c +++ b/src/common/compat.c @@ -1708,6 +1708,106 @@ log_credential_status(void) } #endif +#ifndef _WIN32 +/** Cached struct from the last getpwname() call we did successfully. */ +static struct passwd *passwd_cached = NULL; + +/** Helper: copy a struct passwd object. + * + * We only copy the fields pw_uid, pw_gid, pw_name, pw_dir. Tor doesn't use + * any others, and I don't want to run into incompatibilities. + */ +static struct passwd * +tor_passwd_dup(const struct passwd *pw) +{ + struct passwd *new_pw = tor_malloc_zero(sizeof(struct passwd)); + if (pw->pw_name) + new_pw->pw_name = tor_strdup(pw->pw_name); + if (pw->pw_dir) + new_pw->pw_dir = tor_strdup(pw->pw_dir); + new_pw->pw_uid = pw->pw_uid; + new_pw->pw_gid = pw->pw_gid; + + return new_pw; +} + +/** Helper: free one of our cached 'struct passwd' values. */ +static void +tor_passwd_free(struct passwd *pw) +{ + if (!pw) + return; + + tor_free(pw->pw_name); + tor_free(pw->pw_dir); + tor_free(pw); +} + +/** Wrapper around getpwnam() that caches result. Used so that we don't need + * to give the sandbox access to /etc/passwd. + * + * The following fields alone will definitely be copied in the output: pw_uid, + * pw_gid, pw_name, pw_dir. Other fields are not present in cached values. + * + * When called with a NULL argument, this function clears storage associated + * with static variables it uses. + **/ +const struct passwd * +tor_getpwnam(const char *username) +{ + struct passwd *pw; + + if (username == NULL) { + tor_passwd_free(passwd_cached); + passwd_cached = NULL; + return NULL; + } + + if ((pw = getpwnam(username))) { + tor_passwd_free(passwd_cached); + passwd_cached = tor_passwd_dup(pw); + log_notice(LD_GENERAL, "Caching new entry %s for %s", + passwd_cached->pw_name, username); + return pw; + } + + /* Lookup failed */ + if (! passwd_cached || ! passwd_cached->pw_name) + return NULL; + + if (! strcmp(username, passwd_cached->pw_name)) + return passwd_cached; + + return NULL; +} + +/** Wrapper around getpwnam() that can use cached result from + * tor_getpwnam(). Used so that we don't need to give the sandbox access to + * /etc/passwd. + * + * The following fields alone will definitely be copied in the output: pw_uid, + * pw_gid, pw_name, pw_dir. Other fields are not present in cached values. + */ +const struct passwd * +tor_getpwuid(uid_t uid) +{ + struct passwd *pw; + + if ((pw = getpwuid(uid))) { + return pw; + } + + /* Lookup failed */ + if (! passwd_cached) + return NULL; + + if (uid == passwd_cached->pw_uid) + return passwd_cached; + + return NULL; +} +#endif + /** 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. */ @@ -1715,7 +1815,7 @@ int switch_id(const char *user) { #ifndef _WIN32 - struct passwd *pw = NULL; + const struct passwd *pw = NULL; uid_t old_uid; gid_t old_gid; static int have_already_switched_id = 0; @@ -1736,7 +1836,7 @@ switch_id(const char *user) old_gid = getgid(); /* Lookup the user and group information, if we have a problem, bail out. */ - pw = getpwnam(user); + pw = tor_getpwnam(user); if (pw == NULL) { log_warn(LD_CONFIG, "Error setting configured user: %s not found", user); return -1; @@ -1907,10 +2007,10 @@ tor_disable_debugger_attach(void) char * get_user_homedir(const char *username) { - struct passwd *pw; + const struct passwd *pw; tor_assert(username); - if (!(pw = getpwnam(username))) { + if (!(pw = tor_getpwnam(username))) { log_err(LD_CONFIG,"User \"%s\" not found.", username); return NULL; } @@ -2435,6 +2535,12 @@ tor_pthread_helper_fn(void *_data) func(arg); return NULL; } +/** + * A pthread attribute to make threads start detached. + */ +static pthread_attr_t attr_detached; +/** True iff we've called tor_threads_init() */ +static int threads_initialized = 0; #endif /** Minimalist interface to run a void function in the background. On @@ -2458,12 +2564,12 @@ spawn_func(void (*func)(void *), void *data) #elif defined(USE_PTHREADS) pthread_t thread; tor_pthread_data_t *d; + if (PREDICT_UNLIKELY(!threads_initialized)) + tor_threads_init(); d = tor_malloc(sizeof(tor_pthread_data_t)); d->data = data; d->func = func; - if (pthread_create(&thread,NULL,tor_pthread_helper_fn,d)) - return -1; - if (pthread_detach(thread)) + if (pthread_create(&thread,&attr_detached,tor_pthread_helper_fn,d)) return -1; return 0; #else @@ -2820,8 +2926,6 @@ tor_get_thread_id(void) * "reentrant" mutexes (i.e., once we can re-lock if we're already holding * them.) */ static pthread_mutexattr_t attr_reentrant; -/** True iff we've called tor_threads_init() */ -static int threads_initialized = 0; /** Initialize <b>mutex</b> so it can be locked. Every mutex must be set * up with tor_mutex_init() or tor_mutex_new(); not both. */ void @@ -2965,6 +3069,8 @@ tor_threads_init(void) if (!threads_initialized) { pthread_mutexattr_init(&attr_reentrant); pthread_mutexattr_settype(&attr_reentrant, PTHREAD_MUTEX_RECURSIVE); + tor_assert(0==pthread_attr_init(&attr_detached)); + tor_assert(0==pthread_attr_setdetachstate(&attr_detached, 1)); threads_initialized = 1; set_main_thread(); } @@ -3453,7 +3559,7 @@ get_total_system_memory(size_t *mem_out) *mem_out = mem_cached = (size_t) m; - return -1; + return 0; } #ifdef TOR_UNIT_TESTS diff --git a/src/common/compat.h b/src/common/compat.h index d723448fd..ec7d2415e 100644 --- a/src/common/compat.h +++ b/src/common/compat.h @@ -633,6 +633,11 @@ int switch_id(const char *user); char *get_user_homedir(const char *username); #endif +#ifndef _WIN32 +const struct passwd *tor_getpwnam(const char *username); +const struct passwd *tor_getpwuid(uid_t uid); +#endif + int get_parent_directory(char *fname); char *make_path_absolute(char *fname); diff --git a/src/common/gen_linux_syscalls.pl b/src/common/gen_linux_syscalls.pl deleted file mode 100755 index 3c64098a0..000000000 --- a/src/common/gen_linux_syscalls.pl +++ /dev/null @@ -1,37 +0,0 @@ -#!/usr/bin/perl -w - -use strict; -my %syscalls = (); - -while (<>) { - if (/^#define (__NR_\w+) /) { - $syscalls{$1} = 1; - } -} - -print <<EOL; -/* Automatically generated with - gen_sandbox_syscalls.pl /usr/include/asm/unistd*.h - Do not edit. - */ -static const struct { - int syscall_num; const char *syscall_name; -} SYSCALLS_BY_NUMBER[] = { -EOL - -for my $k (sort keys %syscalls) { - my $name = $k; - $name =~ s/^__NR_//; - print <<EOL; -#ifdef $k - { $k, "$name" }, -#endif -EOL - -} - -print <<EOL - {0, NULL} -}; - -EOL diff --git a/src/common/gen_server_ciphers.py b/src/common/gen_server_ciphers.py deleted file mode 100755 index 97ed9d046..000000000 --- a/src/common/gen_server_ciphers.py +++ /dev/null @@ -1,115 +0,0 @@ -#!/usr/bin/python -# Copyright 2014, The Tor Project, Inc -# See LICENSE for licensing information - -# This script parses openssl headers to find ciphersuite names, determines -# which ones we should be willing to use as a server, and sorts them according -# to preference rules. -# -# Run it on all the files in your openssl include directory. - -import re -import sys - -EPHEMERAL_INDICATORS = [ "_EDH_", "_DHE_", "_ECDHE_" ] -BAD_STUFF = [ "_DES_40_", "MD5", "_RC4_", "_DES_64_", - "_SEED_", "_CAMELLIA_", "_NULL" ] - -# these never get #ifdeffed. -MANDATORY = [ - "TLS1_TXT_DHE_RSA_WITH_AES_256_SHA", - "TLS1_TXT_DHE_RSA_WITH_AES_128_SHA", - "SSL3_TXT_EDH_RSA_DES_192_CBC3_SHA", -] - -def find_ciphers(filename): - with open(filename) as f: - for line in f: - m = re.search(r'(?:SSL3|TLS1)_TXT_\w+', line) - if m: - yield m.group(0) - -def usable_cipher(ciph): - ephemeral = False - for e in EPHEMERAL_INDICATORS: - if e in ciph: - ephemeral = True - if not ephemeral: - return False - - if "_RSA_" not in ciph: - return False - - for b in BAD_STUFF: - if b in ciph: - return False - return True - -# All fields we sort on, in order of priority. -FIELDS = [ 'cipher', 'fwsec', 'mode', 'digest', 'bitlength' ] -# Map from sorted fields to recognized value in descending order of goodness -FIELD_VALS = { 'cipher' : [ 'AES', 'DES'], - 'fwsec' : [ 'ECDHE', 'DHE' ], - 'mode' : [ 'GCM', 'CBC' ], - 'digest' : [ 'SHA384', 'SHA256', 'SHA' ], - 'bitlength' : [ '256', '128', '192' ], -} - -class Ciphersuite(object): - def __init__(self, name, fwsec, cipher, bitlength, mode, digest): - self.name = name - self.fwsec = fwsec - self.cipher = cipher - self.bitlength = bitlength - self.mode = mode - self.digest = digest - - for f in FIELDS: - assert(getattr(self, f) in FIELD_VALS[f]) - - def sort_key(self): - return tuple(FIELD_VALS[f].index(getattr(self,f)) for f in FIELDS) - - -def parse_cipher(ciph): - m = re.match('(?:TLS1|SSL3)_TXT_(EDH|DHE|ECDHE)_RSA(?:_WITH)?_(AES|DES)_(256|128|192)(|_CBC|_CBC3|_GCM)_(SHA|SHA256|SHA384)$', ciph) - - if not m: - print "/* Couldn't parse %s ! */"%ciph - return None - - fwsec, cipher, bits, mode, digest = m.groups() - if fwsec == 'EDH': - fwsec = 'DHE' - - if mode in [ '_CBC3', '_CBC', '' ]: - mode = 'CBC' - elif mode == '_GCM': - mode = 'GCM' - - return Ciphersuite(ciph, fwsec, cipher, bits, mode, digest) - -ALL_CIPHERS = [] - -for fname in sys.argv[1:]: - ALL_CIPHERS += (parse_cipher(c) - for c in find_ciphers(fname) - if usable_cipher(c) ) - -ALL_CIPHERS.sort(key=Ciphersuite.sort_key) - -for c in ALL_CIPHERS: - if c is ALL_CIPHERS[-1]: - colon = ';' - else: - colon = ' ":"' - - if c.name in MANDATORY: - print " /* Required */" - print ' %s%s'%(c.name,colon) - else: - print "#ifdef %s"%c.name - print ' %s%s'%(c.name,colon) - print "#endif" - - diff --git a/src/common/get_mozilla_ciphers.py b/src/common/get_mozilla_ciphers.py deleted file mode 100644 index 0636eb365..000000000 --- a/src/common/get_mozilla_ciphers.py +++ /dev/null @@ -1,210 +0,0 @@ -#!/usr/bin/python -# coding=utf-8 -# Copyright 2011, The Tor Project, Inc -# original version by Arturo Filastò -# See LICENSE for licensing information - -# This script parses Firefox and OpenSSL sources, and uses this information -# to generate a ciphers.inc file. -# -# It takes two arguments: the location of a firefox source directory, and the -# location of an openssl source directory. - -import os -import re -import sys - -if len(sys.argv) != 3: - print >>sys.stderr, "Syntax: get_mozilla_ciphers.py <firefox-source-dir> <openssl-source-dir>" - sys.exit(1) - -ff_root = sys.argv[1] -ossl_root = sys.argv[2] - -def ff(s): - return os.path.join(ff_root, s) -def ossl(s): - return os.path.join(ossl_root, s) - -##### -# Read the cpp file to understand what Ciphers map to what name : -# Make "ciphers" a map from name used in the javascript to a cipher macro name -fileA = open(ff('security/manager/ssl/src/nsNSSComponent.cpp'),'r') - -# The input format is a file containing exactly one section of the form: -# static CipherPref CipherPrefs[] = { -# {"name", MACRO_NAME}, // comment -# ... -# {NULL, 0} -# } - -inCipherSection = False -cipherLines = [] -for line in fileA: - if line.startswith('static const CipherPref sCipherPrefs[]'): - # Get the starting boundary of the Cipher Preferences - inCipherSection = True - elif inCipherSection: - line = line.strip() - if line.startswith('{ nullptr, 0}'): - # At the ending boundary of the Cipher Prefs - break - else: - cipherLines.append(line) -fileA.close() - -# Parse the lines and put them into a dict -ciphers = {} -cipher_pref = {} -key_pending = None -for line in cipherLines: - m = re.search(r'^{\s*\"([^\"]+)\",\s*(\S+)\s*(?:,\s*(true|false))?\s*}', line) - if m: - assert not key_pending - key,value,enabled = m.groups() - if enabled == 'true': - ciphers[key] = value - cipher_pref[value] = key - continue - m = re.search(r'^{\s*\"([^\"]+)\",', line) - if m: - assert not key_pending - key_pending = m.group(1) - continue - m = re.search(r'^\s*(\S+)(?:,\s*(true|false))?\s*}', line) - if m: - assert key_pending - key = key_pending - value,enabled = m.groups() - key_pending = None - if enabled == 'true': - ciphers[key] = value - cipher_pref[value] = key - -#### -# Now find the correct order for the ciphers -fileC = open(ff('security/nss/lib/ssl/ssl3con.c'), 'r') -firefox_ciphers = [] -inEnum=False -for line in fileC: - if not inEnum: - if "ssl3CipherSuiteCfg cipherSuites[" in line: - inEnum = True - continue - - if line.startswith("};"): - break - - m = re.match(r'^\s*\{\s*([A-Z_0-9]+),', line) - if m: - firefox_ciphers.append(m.group(1)) - -fileC.close() - -##### -# Read the JS file to understand what ciphers are enabled. The format is -# pref("name", true/false); -# Build a map enabled_ciphers from javascript name to "true" or "false", -# and an (unordered!) list of the macro names for those ciphers that are -# enabled. -fileB = open(ff('netwerk/base/public/security-prefs.js'), 'r') - -enabled_ciphers = {} -for line in fileB: - m = re.match(r'pref\(\"([^\"]+)\"\s*,\s*(\S*)\s*\)', line) - if not m: - continue - key, val = m.groups() - if key.startswith("security.ssl3"): - enabled_ciphers[key] = val -fileB.close() - -used_ciphers = [] -for k, v in enabled_ciphers.items(): - if v == "true": - used_ciphers.append(ciphers[k]) - -#oSSLinclude = ('/usr/include/openssl/ssl3.h', '/usr/include/openssl/ssl.h', -# '/usr/include/openssl/ssl2.h', '/usr/include/openssl/ssl23.h', -# '/usr/include/openssl/tls1.h') -oSSLinclude = ('ssl/ssl3.h', 'ssl/ssl.h', - 'ssl/ssl2.h', 'ssl/ssl23.h', - 'ssl/tls1.h') - -##### -# This reads the hex code for the ciphers that are used by firefox. -# sslProtoD is set to a map from macro name to macro value in sslproto.h; -# cipher_codes is set to an (unordered!) list of these hex values. -sslProto = open(ff('security/nss/lib/ssl/sslproto.h'), 'r') -sslProtoD = {} - -for line in sslProto: - m = re.match('#define\s+(\S+)\s+(\S+)', line) - if m: - key, value = m.groups() - sslProtoD[key] = value -sslProto.close() - -cipher_codes = [] -for x in used_ciphers: - cipher_codes.append(sslProtoD[x].lower()) - -#### -# Now read through all the openssl include files, and try to find the openssl -# macro names for those files. -openssl_macro_by_hex = {} -all_openssl_macros = {} -for fl in oSSLinclude: - fp = open(ossl(fl), 'r') - for line in fp.readlines(): - m = re.match('#define\s+(\S+)\s+(\S+)', line) - if m: - value,key = m.groups() - if key.startswith('0x') and "_CK_" in value: - key = key.replace('0x0300','0x').lower() - #print "%s %s" % (key, value) - openssl_macro_by_hex[key] = value - all_openssl_macros[value]=key - fp.close() - -# Now generate the output. -print """\ -/* This is an include file used to define the list of ciphers clients should - * advertise. Before including it, you should define the CIPHER and XCIPHER - * macros. - * - * This file was automatically generated by get_mozilla_ciphers.py. - */""" -# Go in order by the order in CipherPrefs -for firefox_macro in firefox_ciphers: - - try: - js_cipher_name = cipher_pref[firefox_macro] - except KeyError: - # This one has no javascript preference. - continue - - # The cipher needs to be enabled in security-prefs.js - if enabled_ciphers.get(js_cipher_name, 'false') != 'true': - continue - - hexval = sslProtoD[firefox_macro].lower() - - try: - openssl_macro = openssl_macro_by_hex[hexval.lower()] - openssl_macro = openssl_macro.replace("_CK_", "_TXT_") - if openssl_macro not in all_openssl_macros: - raise KeyError() - format = {'hex':hexval, 'macro':openssl_macro, 'note':""} - except KeyError: - # openssl doesn't have a macro for this. - format = {'hex':hexval, 'macro':firefox_macro, - 'note':"/* No openssl macro found for "+hexval+" */\n"} - - res = """\ -%(note)s#ifdef %(macro)s - CIPHER(%(hex)s, %(macro)s) -#else - XCIPHER(%(hex)s, %(macro)s) -#endif""" % format - print res diff --git a/src/common/include.am b/src/common/include.am index c9dcedd52..68e0110c2 100644 --- a/src/common/include.am +++ b/src/common/include.am @@ -24,6 +24,14 @@ else libor_extra_source= endif +if USE_MEMPOOLS +libor_mempool_source=src/common/mempool.c +libor_mempool_header=src/common/mempool.h +else +libor_mempool_source= +libor_mempool_header= +endif + src_common_libcurve25519_donna_a_CFLAGS= if BUILD_CURVE25519_DONNA @@ -56,13 +64,13 @@ LIBOR_A_SOURCES = \ src/common/di_ops.c \ src/common/log.c \ src/common/memarea.c \ - src/common/mempool.c \ src/common/util.c \ src/common/util_codedigest.c \ src/common/util_process.c \ src/common/sandbox.c \ src/ext/csiphash.c \ - $(libor_extra_source) + $(libor_extra_source) \ + $(libor_mempool_source) LIBOR_CRYPTO_A_SOURCES = \ src/common/aes.c \ @@ -104,7 +112,6 @@ COMMONHEADERS = \ src/common/crypto_curve25519.h \ src/common/di_ops.h \ src/common/memarea.h \ - src/common/mempool.h \ src/common/linux_syscalls.inc \ src/common/procmon.h \ src/common/sandbox.h \ @@ -114,7 +121,8 @@ COMMONHEADERS = \ src/common/torlog.h \ src/common/tortls.h \ src/common/util.h \ - src/common/util_process.h + src/common/util_process.h \ + $(libor_mempool_header) noinst_HEADERS+= $(COMMONHEADERS) diff --git a/src/common/linux_syscalls.inc b/src/common/linux_syscalls.inc index 912735660..cf47c7380 100644 --- a/src/common/linux_syscalls.inc +++ b/src/common/linux_syscalls.inc @@ -1,5 +1,5 @@ /* Automatically generated with - gen_sandbox_syscalls.pl /usr/include/asm/unistd*.h + gen_linux_syscalls.pl /usr/include/asm/unistd*.h Do not edit. */ static const struct { diff --git a/src/common/log.c b/src/common/log.c index 592dc2c5d..517fa4faa 100644 --- a/src/common/log.c +++ b/src/common/log.c @@ -562,6 +562,27 @@ tor_log_update_sigsafe_err_fds(void) UNLOCK_LOGS(); } +/** Add to <b>out</b> a copy of every currently configured log file name. Used + * to enable access to these filenames with the sandbox code. */ +void +tor_log_get_logfile_names(smartlist_t *out) +{ + logfile_t *lf; + tor_assert(out); + + LOCK_LOGS(); + + for (lf = logfiles; lf; lf = lf->next) { + if (lf->is_temporary || lf->is_syslog || lf->callback) + continue; + if (lf->filename == NULL) + continue; + smartlist_add(out, tor_strdup(lf->filename)); + } + + UNLOCK_LOGS(); +} + /** Output a message to the log, prefixed with a function name <b>fn</b>. */ #ifdef __GNUC__ /** GCC-based implementation of the log_fn backend, used when we have diff --git a/src/common/sandbox.c b/src/common/sandbox.c index 8516c754f..05b91be7b 100644 --- a/src/common/sandbox.c +++ b/src/common/sandbox.c @@ -33,6 +33,8 @@ #include "util.h" #include "tor_queue.h" +#include "ht.h" + #define DEBUGGING_CLOSE #if defined(USE_LIBSECCOMP) @@ -67,12 +69,32 @@ #include <execinfo.h> #endif +/** + * Linux 32 bit definitions + */ +#if defined(__i386__) + +#define REG_SYSCALL REG_EAX +#define M_SYSCALL gregs[REG_SYSCALL] + +/** + * Linux 64 bit definitions + */ +#elif defined(__x86_64__) + +#define REG_SYSCALL REG_RAX +#define M_SYSCALL gregs[REG_SYSCALL] + +#elif defined(__arm__) + +#define M_SYSCALL arm_r7 + +#endif + /**Determines if at least one sandbox is active.*/ static int sandbox_active = 0; /** Holds the parameter list configuration for the sandbox.*/ static sandbox_cfg_t *filter_dynamic = NULL; -/** Holds a list of pre-recorded results from getaddrinfo().*/ -static sb_addr_info_t *sb_addr_info = NULL; #undef SCMP_CMP #define SCMP_CMP(a,b,c) ((struct scmp_arg_cmp){(a),(b),(c),0}) @@ -113,8 +135,11 @@ static int filter_nopar_gen[] = { #ifdef __NR_getgid32 SCMP_SYS(getgid32), #endif +#ifdef __NR_getrlimit SCMP_SYS(getrlimit), +#endif SCMP_SYS(gettimeofday), + SCMP_SYS(gettid), SCMP_SYS(getuid), #ifdef __NR_getuid32 SCMP_SYS(getuid32), @@ -125,10 +150,14 @@ static int filter_nopar_gen[] = { #endif SCMP_SYS(mkdir), SCMP_SYS(mlockall), +#ifdef __NR_mmap + /* XXXX restrict this in the same ways as mmap2 */ SCMP_SYS(mmap), +#endif SCMP_SYS(munmap), SCMP_SYS(read), SCMP_SYS(rt_sigreturn), + SCMP_SYS(sched_getaffinity), SCMP_SYS(set_robust_list), #ifdef __NR_sigreturn SCMP_SYS(sigreturn), @@ -157,6 +186,7 @@ static int filter_nopar_gen[] = { // socket syscalls SCMP_SYS(bind), + SCMP_SYS(listen), SCMP_SYS(connect), SCMP_SYS(getsockname), SCMP_SYS(recvmsg), @@ -204,6 +234,7 @@ sb_rt_sigaction(scmp_filter_ctx ctx, sandbox_cfg_t *filter) return rc; } +#if 0 /** * Function responsible for setting up the execve syscall for * the seccomp filter sandbox. @@ -232,6 +263,7 @@ sb_execve(scmp_filter_ctx ctx, sandbox_cfg_t *filter) return 0; } +#endif /** * Function responsible for setting up the time syscall for @@ -241,8 +273,12 @@ static int sb_time(scmp_filter_ctx ctx, sandbox_cfg_t *filter) { (void) filter; +#ifdef __NR_time return seccomp_rule_add_1(ctx, SCMP_ACT_ALLOW, SCMP_SYS(time), SCMP_CMP(0, SCMP_CMP_EQ, 0)); +#else + return 0; +#endif } /** @@ -551,6 +587,18 @@ sb_setsockopt(scmp_filter_ctx ctx, sandbox_cfg_t *filter) if (rc) return rc; + rc = seccomp_rule_add_2(ctx, SCMP_ACT_ALLOW, SCMP_SYS(setsockopt), + SCMP_CMP(1, SCMP_CMP_EQ, SOL_SOCKET), + SCMP_CMP(2, SCMP_CMP_EQ, SO_SNDBUF)); + if (rc) + return rc; + + rc = seccomp_rule_add_2(ctx, SCMP_ACT_ALLOW, SCMP_SYS(setsockopt), + SCMP_CMP(1, SCMP_CMP_EQ, SOL_SOCKET), + SCMP_CMP(2, SCMP_CMP_EQ, SO_RCVBUF)); + if (rc) + return rc; + #ifdef IP_TRANSPARENT rc = seccomp_rule_add_2(ctx, SCMP_ACT_ALLOW, SCMP_SYS(setsockopt), SCMP_CMP(1, SCMP_CMP_EQ, SOL_IP), @@ -856,7 +904,9 @@ sb_stat64(scmp_filter_ctx ctx, sandbox_cfg_t *filter) static sandbox_filter_func_t filter_func[] = { sb_rt_sigaction, sb_rt_sigprocmask, +#if 0 sb_execve, +#endif sb_time, sb_accept4, #ifdef __NR_mmap2 @@ -1240,6 +1290,7 @@ sandbox_cfg_allow_openat_filename_array(sandbox_cfg_t **cfg, ...) return 0; } +#if 0 int sandbox_cfg_allow_execve(sandbox_cfg_t **cfg, const char *com) { @@ -1279,74 +1330,155 @@ sandbox_cfg_allow_execve_array(sandbox_cfg_t **cfg, ...) va_end(ap); return 0; } +#endif + +/** Cache entry for getaddrinfo results; used when sandboxing is implemented + * so that we can consult the cache when the sandbox prevents us from doing + * getaddrinfo. + * + * We support only a limited range of getaddrinfo calls, where servname is null + * and hints contains only socktype=SOCK_STREAM, family in INET,INET6,UNSPEC. + */ +typedef struct cached_getaddrinfo_item_t { + HT_ENTRY(cached_getaddrinfo_item_t) node; + char *name; + int family; + /** set if no error; otherwise NULL */ + struct addrinfo *res; + /** 0 for no error; otherwise an EAI_* value */ + int err; +} cached_getaddrinfo_item_t; + +static unsigned +cached_getaddrinfo_item_hash(const cached_getaddrinfo_item_t *item) +{ + return (unsigned)siphash24g(item->name, strlen(item->name)) + item->family; +} + +static unsigned +cached_getaddrinfo_items_eq(const cached_getaddrinfo_item_t *a, + const cached_getaddrinfo_item_t *b) +{ + return (a->family == b->family) && 0 == strcmp(a->name, b->name); +} + +static void +cached_getaddrinfo_item_free(cached_getaddrinfo_item_t *item) +{ + if (item == NULL) + return; + + tor_free(item->name); + if (item->res) + freeaddrinfo(item->res); + tor_free(item); +} + +static HT_HEAD(getaddrinfo_cache, cached_getaddrinfo_item_t) + getaddrinfo_cache = HT_INITIALIZER(); + +HT_PROTOTYPE(getaddrinfo_cache, cached_getaddrinfo_item_t, node, + cached_getaddrinfo_item_hash, + cached_getaddrinfo_items_eq); +HT_GENERATE(getaddrinfo_cache, cached_getaddrinfo_item_t, node, + cached_getaddrinfo_item_hash, + cached_getaddrinfo_items_eq, + 0.6, tor_malloc_, tor_realloc_, tor_free_); int sandbox_getaddrinfo(const char *name, const char *servname, const struct addrinfo *hints, struct addrinfo **res) { - sb_addr_info_t *el; + int err; + struct cached_getaddrinfo_item_t search, *item; - if (servname != NULL) - return -1; + if (servname != NULL) { + log_warn(LD_BUG, "called with non-NULL servname"); + return EAI_NONAME; + } + if (name == NULL) { + log_warn(LD_BUG, "called with NULL name"); + return EAI_NONAME; + } *res = NULL; - for (el = sb_addr_info; el; el = el->next) { - if (!strcmp(el->name, name)) { - *res = tor_malloc(sizeof(struct addrinfo)); + memset(&search, 0, sizeof(search)); + search.name = (char *) name; + search.family = hints ? hints->ai_family : AF_UNSPEC; + item = HT_FIND(getaddrinfo_cache, &getaddrinfo_cache, &search); - memcpy(*res, el->info, sizeof(struct addrinfo)); - /* XXXX What if there are multiple items in the list? */ - return 0; + if (! sandbox_is_active()) { + /* If the sandbox is not turned on yet, then getaddrinfo and store the + result. */ + + err = getaddrinfo(name, NULL, hints, res); + log_info(LD_NET,"(Sandbox) getaddrinfo %s.", err ? "failed" : "succeeded"); + + if (! item) { + item = tor_malloc_zero(sizeof(*item)); + item->name = tor_strdup(name); + item->family = hints ? hints->ai_family : AF_UNSPEC; + HT_INSERT(getaddrinfo_cache, &getaddrinfo_cache, item); } - } - if (!sandbox_active) { - if (getaddrinfo(name, NULL, hints, res)) { - log_err(LD_BUG,"(Sandbox) getaddrinfo failed!"); - return -1; + if (item->res) { + freeaddrinfo(item->res); + item->res = NULL; } + item->res = *res; + item->err = err; + return err; + } - return 0; + /* Otherwise, the sanbox is on. If we have an item, yield its cached + result. */ + if (item) { + *res = item->res; + return item->err; } - // getting here means something went wrong + /* getting here means something went wrong */ log_err(LD_BUG,"(Sandbox) failed to get address %s!", name); - if (*res) { - tor_free(*res); - res = NULL; - } - return -1; + return EAI_NONAME; } int -sandbox_add_addrinfo(const char* name) +sandbox_add_addrinfo(const char *name) { - int ret; + struct addrinfo *res; struct addrinfo hints; - sb_addr_info_t *el = NULL; - - el = tor_malloc(sizeof(sb_addr_info_t)); + int i; + static const int families[] = { AF_INET, AF_INET6, AF_UNSPEC }; memset(&hints, 0, sizeof(hints)); - hints.ai_family = AF_INET; hints.ai_socktype = SOCK_STREAM; + for (i = 0; i < 3; ++i) { + hints.ai_family = families[i]; - ret = getaddrinfo(name, NULL, &hints, &(el->info)); - if (ret) { - log_err(LD_BUG,"(Sandbox) failed to getaddrinfo"); - ret = -2; - tor_free(el); - goto out; + res = NULL; + (void) sandbox_getaddrinfo(name, NULL, &hints, &res); + if (res) + sandbox_freeaddrinfo(res); } - el->name = tor_strdup(name); - el->next = sb_addr_info; - sb_addr_info = el; + return 0; +} - out: - return ret; +void +sandbox_free_getaddrinfo_cache(void) +{ + cached_getaddrinfo_item_t **next, **item; + + for (item = HT_START(getaddrinfo_cache, &getaddrinfo_cache); + item; + item = next) { + next = HT_NEXT_RMV(getaddrinfo_cache, &getaddrinfo_cache, item); + cached_getaddrinfo_item_free(*item); + } + + HT_CLEAR(getaddrinfo_cache, &getaddrinfo_cache); } /** @@ -1431,7 +1563,8 @@ install_syscall_filter(sandbox_cfg_t* cfg) // loading the seccomp2 filter if ((rc = seccomp_load(ctx))) { - log_err(LD_BUG, "(Sandbox) failed to load!"); + log_err(LD_BUG, "(Sandbox) failed to load: %d (%s)!", rc, + strerror(-rc)); goto end; } @@ -1491,7 +1624,7 @@ sigsys_debugging(int nr, siginfo_t *info, void *void_context) if (!ctx) return; - syscall = (int) ctx->uc_mcontext.gregs[REG_SYSCALL]; + syscall = (int) ctx->uc_mcontext.M_SYSCALL; #ifdef USE_BACKTRACE depth = backtrace(syscall_cb_buf, MAX_DEPTH); @@ -1659,6 +1792,7 @@ sandbox_cfg_allow_openat_filename_array(sandbox_cfg_t **cfg, ...) return 0; } +#if 0 int sandbox_cfg_allow_execve(sandbox_cfg_t **cfg, const char *com) { @@ -1672,6 +1806,7 @@ sandbox_cfg_allow_execve_array(sandbox_cfg_t **cfg, ...) (void)cfg; return 0; } +#endif int sandbox_cfg_allow_stat_filename(sandbox_cfg_t **cfg, char *file) diff --git a/src/common/sandbox.h b/src/common/sandbox.h index c40f5e0d1..20d5d5080 100644 --- a/src/common/sandbox.h +++ b/src/common/sandbox.h @@ -91,21 +91,6 @@ struct sandbox_cfg_elem { struct sandbox_cfg_elem *next; }; -/** - * Structure used for keeping a linked list of getaddrinfo pre-recorded - * results. - */ -struct sb_addr_info_el { - /** Name of the address info result. */ - char *name; - /** Pre-recorded getaddrinfo result. */ - struct addrinfo *info; - /** Next element in the list. */ - struct sb_addr_info_el *next; -}; -/** Typedef to structure used to manage an addrinfo list. */ -typedef struct sb_addr_info_el sb_addr_info_t; - /** Function pointer defining the prototype of a filter function.*/ typedef int (*sandbox_filter_func_t)(scmp_filter_ctx ctx, sandbox_cfg_t *filter); @@ -119,22 +104,6 @@ typedef struct { sandbox_cfg_t *filter_dynamic; } sandbox_t; -/** - * Linux 32 bit definitions - */ -#if defined(__i386__) - -#define REG_SYSCALL REG_EAX - -/** - * Linux 64 bit definitions - */ -#elif defined(__x86_64__) - -#define REG_SYSCALL REG_RAX - -#endif - #endif // USE_LIBSECCOMP #ifdef USE_LIBSECCOMP @@ -146,11 +115,16 @@ struct addrinfo; int sandbox_getaddrinfo(const char *name, const char *servname, const struct addrinfo *hints, struct addrinfo **res); +#define sandbox_freeaddrinfo(addrinfo) ((void)0) +void sandbox_free_getaddrinfo_cache(void); #else #define sandbox_getaddrinfo(name, servname, hints, res) \ getaddrinfo((name),(servname), (hints),(res)) #define sandbox_add_addrinfo(name) \ ((void)(name)) +#define sandbox_freeaddrinfo(addrinfo) \ + freeaddrinfo((addrinfo)) +#define sandbox_free_getaddrinfo_cache() #endif #ifdef USE_LIBSECCOMP @@ -198,6 +172,7 @@ int sandbox_cfg_allow_openat_filename(sandbox_cfg_t **cfg, char *file); */ int sandbox_cfg_allow_openat_filename_array(sandbox_cfg_t **cfg, ...); +#if 0 /** * Function used to add a execve allowed filename to a supplied configuration. * The (char*) specifies the path to the allowed file; that pointer is stolen. @@ -211,6 +186,7 @@ int sandbox_cfg_allow_execve(sandbox_cfg_t **cfg, const char *com); * one must be NULL. */ int sandbox_cfg_allow_execve_array(sandbox_cfg_t **cfg, ...); +#endif /** * Function used to add a stat/stat64 allowed filename to a configuration. diff --git a/src/common/torlog.h b/src/common/torlog.h index 4493b251d..34f70f3c0 100644 --- a/src/common/torlog.h +++ b/src/common/torlog.h @@ -156,9 +156,12 @@ void tor_log_err_sigsafe(const char *m, ...); int tor_log_get_sigsafe_err_fds(const int **out); void tor_log_update_sigsafe_err_fds(void); -#if defined(__GNUC__) || defined(RUNNING_DOXYGEN) +struct smartlist_t; +void tor_log_get_logfile_names(struct smartlist_t *out); + extern int log_global_min_severity_; +#if defined(__GNUC__) || defined(RUNNING_DOXYGEN) void log_fn_(int severity, log_domain_mask_t domain, const char *funcname, const char *format, ...) CHECK_PRINTF(4,5); diff --git a/src/common/tortls.c b/src/common/tortls.c index a6444b818..ea0f21cb2 100644 --- a/src/common/tortls.c +++ b/src/common/tortls.c @@ -1477,10 +1477,13 @@ prune_v2_cipher_list(void) inp = outp = v2_cipher_list; while (*inp) { - unsigned char cipherid[2]; + unsigned char cipherid[3]; const SSL_CIPHER *cipher; /* Is there no better way to do this? */ set_uint16(cipherid, htons(*inp)); + cipherid[2] = 0; /* If ssl23_get_cipher_by_char finds no cipher starting + * with a two-byte 'cipherid', it may look for a v2 + * cipher with the appropriate 3 bytes. */ cipher = m->get_cipher_by_char(cipherid); if (cipher) { tor_assert((cipher->id & 0xffff) == *inp); diff --git a/src/common/util.c b/src/common/util.c index f79e72bea..2d7893b38 100644 --- a/src/common/util.c +++ b/src/common/util.c @@ -1517,7 +1517,7 @@ void format_iso_time_nospace_usec(char *buf, const struct timeval *tv) { tor_assert(tv); - format_iso_time_nospace(buf, tv->tv_sec); + format_iso_time_nospace(buf, (time_t)tv->tv_sec); tor_snprintf(buf+ISO_TIME_LEN, 8, ".%06d", (int)tv->tv_usec); } @@ -1872,7 +1872,7 @@ check_private_dir(const char *dirname, cpd_check_t check, char *f; #ifndef _WIN32 int mask; - struct passwd *pw = NULL; + const struct passwd *pw = NULL; uid_t running_uid; gid_t running_gid; #else @@ -1919,7 +1919,7 @@ check_private_dir(const char *dirname, cpd_check_t check, if (effective_user) { /* Look up the user and group information. * If we have a problem, bail out. */ - pw = getpwnam(effective_user); + pw = tor_getpwnam(effective_user); if (pw == NULL) { log_warn(LD_CONFIG, "Error setting configured user: %s not found", effective_user); @@ -1933,13 +1933,13 @@ check_private_dir(const char *dirname, cpd_check_t check, } if (st.st_uid != running_uid) { - struct passwd *pw = NULL; + const struct passwd *pw = NULL; char *process_ownername = NULL; - pw = getpwuid(running_uid); + pw = tor_getpwuid(running_uid); process_ownername = pw ? tor_strdup(pw->pw_name) : tor_strdup("<unknown>"); - pw = getpwuid(st.st_uid); + pw = tor_getpwuid(st.st_uid); log_warn(LD_FS, "%s is not owned by this user (%s, %d) but by " "%s (%d). Perhaps you are running Tor as the wrong user?", @@ -2005,7 +2005,8 @@ write_str_to_file(const char *fname, const char *str, int bin) #ifdef _WIN32 if (!bin && strchr(str, '\r')) { log_warn(LD_BUG, - "We're writing a text string that already contains a CR."); + "We're writing a text string that already contains a CR to %s", + escaped(fname)); } #endif return write_bytes_to_file(fname, str, strlen(str), bin); @@ -4573,6 +4574,30 @@ stream_status_to_string(enum stream_status stream_status) } } +/* DOCDOC */ +static void +log_portfw_spawn_error_message(const char *buf, + const char *executable, int *child_status) +{ + /* Parse error message */ + int retval, child_state, saved_errno; + retval = tor_sscanf(buf, SPAWN_ERROR_MESSAGE "%x/%x", + &child_state, &saved_errno); + if (retval == 2) { + log_warn(LD_GENERAL, + "Failed to start child process \"%s\" in state %d: %s", + executable, child_state, strerror(saved_errno)); + if (child_status) + *child_status = 1; + } else { + /* Failed to parse message from child process, log it as a + warning */ + log_warn(LD_GENERAL, + "Unexpected message from port forwarding helper \"%s\": %s", + executable, buf); + } +} + #ifdef _WIN32 /** Return a smartlist containing lines outputted from @@ -4720,23 +4745,7 @@ log_from_pipe(FILE *stream, int severity, const char *executable, /* Check if buf starts with SPAWN_ERROR_MESSAGE */ if (strcmpstart(buf, SPAWN_ERROR_MESSAGE) == 0) { - /* Parse error message */ - int retval, child_state, saved_errno; - retval = tor_sscanf(buf, SPAWN_ERROR_MESSAGE "%x/%x", - &child_state, &saved_errno); - if (retval == 2) { - log_warn(LD_GENERAL, - "Failed to start child process \"%s\" in state %d: %s", - executable, child_state, strerror(saved_errno)); - if (child_status) - *child_status = 1; - } else { - /* Failed to parse message from child process, log it as a - warning */ - log_warn(LD_GENERAL, - "Unexpected message from port forwarding helper \"%s\": %s", - executable, buf); - } + log_portfw_spawn_error_message(buf, executable, child_status); } else { log_fn(severity, LD_GENERAL, "Port forwarding helper says: %s", buf); } @@ -4814,7 +4823,7 @@ get_string_from_pipe(FILE *stream, char *buf_out, size_t count) /** Parse a <b>line</b> from tor-fw-helper and issue an appropriate * log message to our user. */ static void -handle_fw_helper_line(const char *line) +handle_fw_helper_line(const char *executable, const char *line) { smartlist_t *tokens = smartlist_new(); char *message = NULL; @@ -4825,6 +4834,19 @@ handle_fw_helper_line(const char *line) int port = 0; int success = 0; + if (strcmpstart(line, SPAWN_ERROR_MESSAGE) == 0) { + /* We need to check for SPAWN_ERROR_MESSAGE again here, since it's + * possible that it got sent after we tried to read it in log_from_pipe. + * + * XXX Ideally, we should be using one of stdout/stderr for the real + * output, and one for the output of the startup code. We used to do that + * before cd05f35d2c. + */ + int child_status; + log_portfw_spawn_error_message(line, executable, &child_status); + goto done; + } + smartlist_split_string(tokens, line, NULL, SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, -1); @@ -4904,7 +4926,8 @@ handle_fw_helper_line(const char *line) /** Read what tor-fw-helper has to say in its stdout and handle it * appropriately */ static int -handle_fw_helper_output(process_handle_t *process_handle) +handle_fw_helper_output(const char *executable, + process_handle_t *process_handle) { smartlist_t *fw_helper_output = NULL; enum stream_status stream_status = 0; @@ -4919,7 +4942,7 @@ handle_fw_helper_output(process_handle_t *process_handle) /* Handle the lines we got: */ SMARTLIST_FOREACH_BEGIN(fw_helper_output, char *, line) { - handle_fw_helper_line(line); + handle_fw_helper_line(executable, line); tor_free(line); } SMARTLIST_FOREACH_END(line); @@ -5034,7 +5057,7 @@ tor_check_port_forwarding(const char *filename, stderr_status = log_from_pipe(child_handle->stderr_handle, LOG_INFO, filename, &retval); #endif - if (handle_fw_helper_output(child_handle) < 0) { + if (handle_fw_helper_output(filename, child_handle) < 0) { log_warn(LD_GENERAL, "Failed to handle fw helper output."); stdout_status = -1; retval = -1; |