diff options
Diffstat (limited to 'src/common')
-rw-r--r-- | src/common/Makefile.am | 5 | ||||
-rw-r--r-- | src/common/address.c | 215 | ||||
-rw-r--r-- | src/common/address.h | 11 | ||||
-rw-r--r-- | src/common/aes.c | 347 | ||||
-rw-r--r-- | src/common/aes.h | 3 | ||||
-rw-r--r-- | src/common/compat.c | 98 | ||||
-rw-r--r-- | src/common/compat.h | 20 | ||||
-rw-r--r-- | src/common/container.h | 28 | ||||
-rw-r--r-- | src/common/crypto.c | 443 | ||||
-rw-r--r-- | src/common/crypto.h | 34 | ||||
-rw-r--r-- | src/common/tortls.c | 111 | ||||
-rw-r--r-- | src/common/tortls_states.h | 414 | ||||
-rw-r--r-- | src/common/util.c | 272 | ||||
-rw-r--r-- | src/common/util.h | 84 |
14 files changed, 1302 insertions, 783 deletions
diff --git a/src/common/Makefile.am b/src/common/Makefile.am index 2244fe58d3..04c411556a 100644 --- a/src/common/Makefile.am +++ b/src/common/Makefile.am @@ -51,14 +51,13 @@ noinst_HEADERS = \ torint.h \ torlog.h \ tortls.h \ - tortls_states.h \ util.h common_sha1.i: $(libor_SOURCES) $(libor_crypto_a_SOURCES) $(noinst_HEADERS) if test "@SHA1SUM@" != none; then \ - @SHA1SUM@ $(libor_SOURCES) $(libor_crypto_a_SOURCES) $(noinst_HEADERS) | @SED@ -n 's/^\(.*\)$$/"\1\\n"/p' > common_sha1.i; \ + (cd "$(srcdir)" && @SHA1SUM@ $(libor_SOURCES) $(libor_crypto_a_SOURCES) $(noinst_HEADERS)) | @SED@ -n 's/^\(.*\)$$/"\1\\n"/p' > common_sha1.i; \ elif test "@OPENSSL@" != none; then \ - @OPENSSL@ sha1 $(libor_SOURCES) $(libor_crypto_a_SOURCES) $(noinst_HEADERS) | @SED@ -n 's/SHA1(\(.*\))= \(.*\)/"\2 \1\\n"/p' > common_sha1.i; \ + (cd "$(srcdir)" && @OPENSSL@ sha1 $(libor_SOURCES) $(libor_crypto_a_SOURCES) $(noinst_HEADERS)) | @SED@ -n 's/SHA1(\(.*\))= \(.*\)/"\2 \1\\n"/p' > common_sha1.i; \ else \ rm common_sha1.i; \ touch common_sha1.i; \ diff --git a/src/common/address.c b/src/common/address.c index f40e428cce..2e9892c4dc 100644 --- a/src/common/address.c +++ b/src/common/address.c @@ -13,10 +13,16 @@ #include "util.h" #include "address.h" #include "torlog.h" +#include "container.h" #ifdef MS_WINDOWS #include <process.h> #include <windows.h> +#include <winsock2.h> +/* For access to structs needed by GetAdaptersAddresses */ +#undef _WIN32_WINNT +#define _WIN32_WINNT 0x0501 +#include <iphlpapi.h> #endif #ifdef HAVE_SYS_TIME_H @@ -46,6 +52,15 @@ #ifdef HAVE_SYS_UN_H #include <sys/un.h> #endif +#ifdef HAVE_IFADDRS_H +#include <ifaddrs.h> +#endif +#ifdef HAVE_SYS_IOCTL_H +#include <sys/ioctl.h> +#endif +#ifdef HAVE_NET_IF_H +#include <net/if.h> +#endif #include <stdarg.h> #include <stdio.h> #include <stdlib.h> @@ -962,8 +977,11 @@ char * tor_dup_addr(const tor_addr_t *addr) { char buf[TOR_ADDR_BUF_LEN]; - tor_addr_to_str(buf, addr, sizeof(buf), 0); - return tor_strdup(buf); + if (tor_addr_to_str(buf, addr, sizeof(buf), 0)) { + return tor_strdup(buf); + } else { + return tor_strdup("<unknown address type>"); + } } /** Return a string representing the address <b>addr</b>. This string is @@ -1061,7 +1079,7 @@ tor_addr_port_lookup(const char *s, tor_addr_t *addr_out, uint16_t *port_out) ++port; } - if (tor_addr_lookup(tmp, AF_UNSPEC, &addr) < 0) + if (tor_addr_lookup(tmp, AF_UNSPEC, &addr) != 0) goto err; tor_free(tmp); @@ -1083,6 +1101,169 @@ tor_addr_port_lookup(const char *s, tor_addr_t *addr_out, uint16_t *port_out) return -1; } +#ifdef MS_WINDOWS +typedef ULONG (WINAPI *GetAdaptersAddresses_fn_t)( + ULONG, ULONG, PVOID, PIP_ADAPTER_ADDRESSES, PULONG); +#endif + +/** Try to ask our network interfaces what addresses they are bound to. + * Return a new smartlist of tor_addr_t on success, and NULL on failure. + * (An empty smartlist indicates that we successfully learned that we have no + * addresses.) Log failure messages at <b>severity</b>. */ +static smartlist_t * +get_interface_addresses_raw(int severity) +{ +#if defined(HAVE_GETIFADDRS) + /* Most free Unixy systems provide getifaddrs, which gives us a linked list + * of struct ifaddrs. */ + struct ifaddrs *ifa = NULL; + const struct ifaddrs *i; + smartlist_t *result; + if (getifaddrs(&ifa) < 0) { + log_fn(severity, LD_NET, "Unable to call getifaddrs(): %s", + strerror(errno)); + return NULL; + } + + result = smartlist_create(); + for (i = ifa; i; i = i->ifa_next) { + tor_addr_t tmp; + if (!i->ifa_addr) + continue; + if (i->ifa_addr->sa_family != AF_INET && + i->ifa_addr->sa_family != AF_INET6) + continue; + if (tor_addr_from_sockaddr(&tmp, i->ifa_addr, NULL) < 0) + continue; + smartlist_add(result, tor_memdup(&tmp, sizeof(tmp))); + } + + freeifaddrs(ifa); + return result; +#elif defined(MS_WINDOWS) + /* Windows XP began to provide GetAdaptersAddresses. Windows 2000 had a + "GetAdaptersInfo", but that's deprecated; let's just try + GetAdaptersAddresses and fall back to connect+getsockname. + */ + HANDLE lib = load_windows_system_library(TEXT("iphlpapi.dll")); + smartlist_t *result = NULL; + GetAdaptersAddresses_fn_t fn; + ULONG size, res; + IP_ADAPTER_ADDRESSES *addresses = NULL, *address; + + (void) severity; + +#define FLAGS (GAA_FLAG_SKIP_ANYCAST | \ + GAA_FLAG_SKIP_MULTICAST | \ + GAA_FLAG_SKIP_DNS_SERVER) + + if (!lib) { + log_fn(severity, LD_NET, "Unable to load iphlpapi.dll"); + goto done; + } + + if (!(fn = (GetAdaptersAddresses_fn_t) + GetProcAddress(lib, "GetAdaptersAddresses"))) { + log_fn(severity, LD_NET, "Unable to obtain pointer to " + "GetAdaptersAddresses"); + goto done; + } + + /* Guess how much space we need. */ + size = 15*1024; + addresses = tor_malloc(size); + res = fn(AF_UNSPEC, FLAGS, NULL, addresses, &size); + if (res == ERROR_BUFFER_OVERFLOW) { + /* we didn't guess that we needed enough space; try again */ + tor_free(addresses); + addresses = tor_malloc(size); + res = fn(AF_UNSPEC, FLAGS, NULL, addresses, &size); + } + if (res != NO_ERROR) { + log_fn(severity, LD_NET, "GetAdaptersAddresses failed (result: %lu)", res); + goto done; + } + + result = smartlist_create(); + for (address = addresses; address; address = address->Next) { + IP_ADAPTER_UNICAST_ADDRESS *a; + for (a = address->FirstUnicastAddress; a; a = a->Next) { + /* Yes, it's a linked list inside a linked list */ + struct sockaddr *sa = a->Address.lpSockaddr; + tor_addr_t tmp; + if (sa->sa_family != AF_INET && sa->sa_family != AF_INET6) + continue; + if (tor_addr_from_sockaddr(&tmp, sa, NULL) < 0) + continue; + smartlist_add(result, tor_memdup(&tmp, sizeof(tmp))); + } + } + + done: + if (lib) + FreeLibrary(lib); + tor_free(addresses); + return result; +#elif defined(SIOCGIFCONF) && defined(HAVE_IOCTL) + /* Some older unixy systems make us use ioctl(SIOCGIFCONF) */ + struct ifconf ifc; + int fd, i, sz, n; + smartlist_t *result = NULL; + /* This interface, AFAICT, only supports AF_INET addresses */ + fd = socket(AF_INET, SOCK_DGRAM, 0); + if (fd < 0) { + log(severity, LD_NET, "socket failed: %s", strerror(errno)); + goto done; + } + /* Guess how much space we need. */ + ifc.ifc_len = sz = 15*1024; + ifc.ifc_ifcu.ifcu_req = tor_malloc(sz); + if (ioctl(fd, SIOCGIFCONF, &ifc) < 0) { + log(severity, LD_NET, "ioctl failed: %s", strerror(errno)); + close(fd); + goto done; + } + close(fd); + result = smartlist_create(); + if (ifc.ifc_len < sz) + sz = ifc.ifc_len; + n = sz / sizeof(struct ifreq); + for (i = 0; i < n ; ++i) { + struct ifreq *r = &ifc.ifc_ifcu.ifcu_req[i]; + struct sockaddr *sa = &r->ifr_addr; + tor_addr_t tmp; + if (sa->sa_family != AF_INET && sa->sa_family != AF_INET6) + continue; /* should be impossible */ + if (tor_addr_from_sockaddr(&tmp, sa, NULL) < 0) + continue; + smartlist_add(result, tor_memdup(&tmp, sizeof(tmp))); + } + done: + tor_free(ifc.ifc_ifcu.ifcu_req); + return result; +#else + (void) severity; + return NULL; +#endif +} + +/** Return true iff <b>a</b> is a multicast address. */ +static int +tor_addr_is_multicast(const tor_addr_t *a) +{ + sa_family_t family = tor_addr_family(a); + if (family == AF_INET) { + uint32_t ipv4h = tor_addr_to_ipv4h(a); + if ((ipv4h >> 24) == 0xe0) + return 1; /* Multicast */ + } else if (family == AF_INET6) { + const uint8_t *a32 = tor_addr_to_in6_addr8(a); + if (a32[0] == 0xff) + return 1; + } + return 0; +} + /** Set *<b>addr</b> to the IP address (if any) of whatever interface * connects to the Internet. This address should only be used in checking * whether our address has changed. Return 0 on success, -1 on failure. @@ -1090,12 +1271,38 @@ tor_addr_port_lookup(const char *s, tor_addr_t *addr_out, uint16_t *port_out) int get_interface_address6(int severity, sa_family_t family, tor_addr_t *addr) { + /* XXX really, this function should yield a smartlist of addresses. */ + smartlist_t *addrs; int sock=-1, r=-1; struct sockaddr_storage my_addr, target_addr; socklen_t addr_len; - tor_assert(addr); + /* Try to do this the smart way if possible. */ + if ((addrs = get_interface_addresses_raw(severity))) { + int rv = -1; + SMARTLIST_FOREACH_BEGIN(addrs, tor_addr_t *, a) { + if (family != AF_UNSPEC && family != tor_addr_family(a)) + continue; + if (tor_addr_is_loopback(a) || + tor_addr_is_multicast(a)) + continue; + + tor_addr_copy(addr, a); + rv = 0; + + /* If we found a non-internal address, declare success. Otherwise, + * keep looking. */ + if (!tor_addr_is_internal(a, 0)) + break; + } SMARTLIST_FOREACH_END(a); + + SMARTLIST_FOREACH(addrs, tor_addr_t *, a, tor_free(a)); + smartlist_free(addrs); + return rv; + } + + /* Okay, the smart way is out. */ memset(addr, 0, sizeof(tor_addr_t)); memset(&target_addr, 0, sizeof(target_addr)); /* Don't worry: no packets are sent. We just need to use a real address diff --git a/src/common/address.h b/src/common/address.h index 359b0264d2..4568c32bf9 100644 --- a/src/common/address.h +++ b/src/common/address.h @@ -31,6 +31,13 @@ typedef struct tor_addr_t } addr; } tor_addr_t; +/** Holds an IP address and a TCP/UDP port. */ +typedef struct tor_addr_port_t +{ + tor_addr_t addr; + uint16_t port; +} tor_addr_port_t; + static INLINE const struct in6_addr *tor_addr_to_in6(const tor_addr_t *a); static INLINE uint32_t tor_addr_to_ipv4n(const tor_addr_t *a); static INLINE uint32_t tor_addr_to_ipv4h(const tor_addr_t *a); @@ -149,7 +156,7 @@ int tor_addr_compare_masked(const tor_addr_t *addr1, const tor_addr_t *addr2, unsigned int tor_addr_hash(const tor_addr_t *addr); int tor_addr_is_v4(const tor_addr_t *addr); -int tor_addr_is_internal(const tor_addr_t *ip, int for_listening) ATTR_PURE; +int tor_addr_is_internal(const tor_addr_t *ip, int for_listening); /** Longest length that can be required for a reverse lookup name. */ /* 32 nybbles, 32 dots, 8 characters of "ip6.arpa", 1 NUL: 73 characters. */ @@ -185,7 +192,7 @@ int tor_addr_port_split(int severity, const char *addrport, char **address_out, uint16_t *port_out); /* IPv4 helpers */ -int is_internal_IP(uint32_t ip, int for_listening) ATTR_PURE; +int is_internal_IP(uint32_t ip, int for_listening); int addr_port_lookup(int severity, const char *addrport, char **address, uint32_t *addr, uint16_t *port_out); int parse_port_range(const char *port, uint16_t *port_min_out, diff --git a/src/common/aes.c b/src/common/aes.c index 9c03b8085c..da7220fe19 100644 --- a/src/common/aes.c +++ b/src/common/aes.c @@ -14,34 +14,42 @@ #include <assert.h> #include <stdlib.h> #include <string.h> +#include <openssl/aes.h> +#include <openssl/evp.h> +#include <openssl/engine.h> +#include "crypto.h" +#if OPENSSL_VERSION_NUMBER >= OPENSSL_V_SERIES(1,0,0) +/* See comments about which counter mode implementation to use below. */ +#include <openssl/modes.h> +#define CAN_USE_OPENSSL_CTR +#endif #include "compat.h" #include "aes.h" #include "util.h" #include "torlog.h" -/* We have 2 strategies for getting AES: Via OpenSSL's AES_encrypt function, - * via OpenSSL's EVP_EncryptUpdate function. */ - -/** Defined iff we're using OpenSSL's AES functions for AES. */ -#undef USE_OPENSSL_AES -/** Defined iff we're using OpenSSL's EVP code for AES. */ -#undef USE_OPENSSL_EVP - -/* Here we pick which to use, if none is force-defined above */ -#if (!defined(USE_OPENSSL_AES) && \ - !defined(USE_OPENSSL_EVP)) - -#define USE_OPENSSL_EVP - +#ifdef ANDROID +/* Android's OpenSSL seems to have removed all of its Engine support. */ +#define DISABLE_ENGINES #endif -/* Include OpenSSL headers as needed. */ -#ifdef USE_OPENSSL_AES -# include <openssl/aes.h> -#endif -#ifdef USE_OPENSSL_EVP -# include <openssl/evp.h> -#endif +/* We have 2 strategies for getting AES: Via OpenSSL's AES_encrypt function, + * via OpenSSL's EVP_EncryptUpdate function. + * + * If there's any hardware acceleration in play, we want to be using EVP_* so + * we can get it. Otherwise, we'll want AES_*, which seems to be about 5% + * faster than indirecting through the EVP layer. + */ + +/* We have 2 strategies for counter mode: use our own, or use OpenSSL's. + * + * Here we have a counter mode that's faster than the one shipping with + * OpenSSL pre-1.0 (by about 10%!). But OpenSSL 1.0.0 added a counter mode + * implementation faster than the one here (by about 7%). So we pick which + * one to used based on the Openssl version above. (OpenSSL 1.0.0a fixed a + * critical bug in that counter mode implementation, so we need to test to + * make sure that we have a fixed version.) + */ /*======================================================================*/ /* Interface to AES code, and counter implementation */ @@ -49,11 +57,10 @@ /** Implements an AES counter-mode cipher. */ struct aes_cnt_cipher { /** This next element (however it's defined) is the AES key. */ -#if defined(USE_OPENSSL_EVP) - EVP_CIPHER_CTX key; -#elif defined(USE_OPENSSL_AES) - AES_KEY key; -#endif + union { + EVP_CIPHER_CTX evp; + AES_KEY aes; + } key; #if !defined(WORDS_BIGENDIAN) #define USING_COUNTER_VARS @@ -77,9 +84,102 @@ struct aes_cnt_cipher { /** The encrypted value of ctr_buf. */ uint8_t buf[16]; /** Our current stream position within buf. */ - uint8_t pos; + unsigned int pos; + + /** True iff we're using the evp implementation of this cipher. */ + uint8_t using_evp; }; +/** True iff we should prefer the EVP implementation for AES, either because + * we're testing it or because we have hardware acceleration configured */ +static int should_use_EVP = 0; + +#ifdef CAN_USE_OPENSSL_CTR +/** True iff we have tested the counter-mode implementation and found that it + * doesn't have the counter-mode bug from OpenSSL 1.0.0. */ +static int should_use_openssl_CTR = 0; +#endif + +/** Check whether we should use the EVP interface for AES. If <b>force_val</b> + * is nonnegative, we use use EVP iff it is true. Otherwise, we use EVP + * if there is an engine enabled for aes-ecb. */ +int +evaluate_evp_for_aes(int force_val) +{ + ENGINE *e; + + if (force_val >= 0) { + should_use_EVP = force_val; + return 0; + } +#ifdef DISABLE_ENGINES + should_use_EVP = 0; +#else + e = ENGINE_get_cipher_engine(NID_aes_128_ecb); + + if (e) { + log_notice(LD_CRYPTO, "AES engine \"%s\" found; using EVP_* functions.", + ENGINE_get_name(e)); + should_use_EVP = 1; + } else { + log_notice(LD_CRYPTO, "No AES engine found; using AES_* functions."); + should_use_EVP = 0; + } +#endif + + return 0; +} + +/** Test the OpenSSL counter mode implementation to see whether it has the + * counter-mode bug from OpenSSL 1.0.0. If the implementation works, then + * we will use it for future encryption/decryption operations. + * + * We can't just look at the OpenSSL version, since some distributions update + * their OpenSSL packages without changing the version number. + **/ +int +evaluate_ctr_for_aes(void) +{ +#ifdef CAN_USE_OPENSSL_CTR + /* Result of encrypting an all-zero block with an all-zero 128-bit AES key. + * This should be the same as encrypting an all-zero block with an all-zero + * 128-bit AES key in counter mode, starting at position 0 of the stream. + */ + static const unsigned char encrypt_zero[] = + "\x66\xe9\x4b\xd4\xef\x8a\x2c\x3b\x88\x4c\xfa\x59\xca\x34\x2b\x2e"; + unsigned char zero[16]; + unsigned char output[16]; + unsigned char ivec[16]; + unsigned char ivec_tmp[16]; + unsigned int pos, i; + AES_KEY key; + memset(zero, 0, sizeof(zero)); + memset(ivec, 0, sizeof(ivec)); + AES_set_encrypt_key(zero, 128, &key); + + pos = 0; + /* Encrypting a block one byte at a time should make the error manifest + * itself for known bogus openssl versions. */ + for (i=0; i<16; ++i) + AES_ctr128_encrypt(&zero[i], &output[i], 1, &key, ivec, ivec_tmp, &pos); + + if (memcmp(output, encrypt_zero, 16)) { + /* Counter mode is buggy */ + log_notice(LD_CRYPTO, "This OpenSSL has a buggy version of counter mode; " + "not using it."); + } else { + /* Counter mode is okay */ + log_notice(LD_CRYPTO, "This OpenSSL has a good implementation of counter " + "mode; using it."); + should_use_openssl_CTR = 1; + } +#else + log_notice(LD_CRYPTO, "This version of OpenSSL has a slow implementation of " + "counter mode; not using it."); +#endif + return 0; +} + #if !defined(USING_COUNTER_VARS) #define COUNTER(c, n) ((c)->ctr_buf.buf32[3-(n)]) #else @@ -100,15 +200,13 @@ _aes_fill_buf(aes_cnt_cipher_t *cipher) * None of these issues are insurmountable in principle. */ -#if defined(USE_OPENSSL_EVP) - { + if (cipher->using_evp) { int outl=16, inl=16; - EVP_EncryptUpdate(&cipher->key, cipher->buf, &outl, + EVP_EncryptUpdate(&cipher->key.evp, cipher->buf, &outl, cipher->ctr_buf.buf, inl); + } else { + AES_encrypt(cipher->ctr_buf.buf, cipher->buf, &cipher->key.aes); } -#elif defined(USE_OPENSSL_AES) - AES_encrypt(cipher->ctr_buf.buf, cipher->buf, &cipher->key); -#endif } /** @@ -129,18 +227,21 @@ aes_new_cipher(void) void aes_set_key(aes_cnt_cipher_t *cipher, const char *key, int key_bits) { -#if defined(USE_OPENSSL_EVP) - const EVP_CIPHER *c; - switch (key_bits) { - case 128: c = EVP_aes_128_ecb(); break; - case 192: c = EVP_aes_192_ecb(); break; - case 256: c = EVP_aes_256_ecb(); break; - default: tor_assert(0); + if (should_use_EVP) { + const EVP_CIPHER *c; + switch (key_bits) { + case 128: c = EVP_aes_128_ecb(); break; + case 192: c = EVP_aes_192_ecb(); break; + case 256: c = EVP_aes_256_ecb(); break; + default: tor_assert(0); + } + EVP_EncryptInit(&cipher->key.evp, c, (const unsigned char*)key, NULL); + cipher->using_evp = 1; + } else { + AES_set_encrypt_key((const unsigned char *)key, key_bits, &cipher->key.aes); + cipher->using_evp = 0; } - EVP_EncryptInit(&cipher->key, c, (const unsigned char*)key, NULL); -#elif defined(USE_OPENSSL_AES) - AES_set_encrypt_key((const unsigned char *)key, key_bits, &(cipher->key)); -#endif + #ifdef USING_COUNTER_VARS cipher->counter0 = 0; cipher->counter1 = 0; @@ -151,7 +252,13 @@ aes_set_key(aes_cnt_cipher_t *cipher, const char *key, int key_bits) memset(cipher->ctr_buf.buf, 0, sizeof(cipher->ctr_buf.buf)); cipher->pos = 0; - _aes_fill_buf(cipher); + +#ifdef CAN_USE_OPENSSL_CTR + if (should_use_openssl_CTR) + memset(cipher->buf, 0, sizeof(cipher->buf)); + else +#endif + _aes_fill_buf(cipher); } /** Release storage held by <b>cipher</b> @@ -161,9 +268,9 @@ aes_free_cipher(aes_cnt_cipher_t *cipher) { if (!cipher) return; -#ifdef USE_OPENSSL_EVP - EVP_CIPHER_CTX_cleanup(&cipher->key); -#endif + if (cipher->using_evp) { + EVP_CIPHER_CTX_cleanup(&cipher->key.evp); + } memset(cipher, 0, sizeof(aes_cnt_cipher_t)); tor_free(cipher); } @@ -176,6 +283,18 @@ aes_free_cipher(aes_cnt_cipher_t *cipher) #define UPDATE_CTR_BUF(c, n) #endif +#ifdef CAN_USE_OPENSSL_CTR +/* Helper function to use EVP with openssl's counter-mode wrapper. */ +static void evp_block128_fn(const uint8_t in[16], + uint8_t out[16], + const void *key) +{ + EVP_CIPHER_CTX *ctx = (void*)key; + int inl=16, outl=16; + EVP_EncryptUpdate(ctx, out, &outl, in, inl); +} +#endif + /** Encrypt <b>len</b> bytes from <b>input</b>, storing the result in * <b>output</b>. Uses the key in <b>cipher</b>, and advances the counter * by <b>len</b> bytes as it encrypts. @@ -184,41 +303,56 @@ void aes_crypt(aes_cnt_cipher_t *cipher, const char *input, size_t len, char *output) { - /* This function alone is up to 5% of our runtime in some profiles; anything - * we could do to make it faster would be great. - * - * Experimenting suggests that unrolling the inner loop into a switch - * statement doesn't help. What does seem to help is making the input and - * output buffers word aligned, and never crypting anything besides an - * integer number of words at a time -- it shaves maybe 4-5% of the per-byte - * encryption time measured by bench_aes. We can't do that with the current - * Tor protocol, though: Tor really likes to crypt things in 509-byte - * chunks. - * - * If we were really ambitous, we'd force len to be a multiple of the block - * size, and shave maybe another 4-5% off. - */ - int c = cipher->pos; - if (PREDICT_UNLIKELY(!len)) return; - - while (1) { - do { - if (len-- == 0) { cipher->pos = c; return; } - *(output++) = *(input++) ^ cipher->buf[c]; - } while (++c != 16); - cipher->pos = c = 0; - if (PREDICT_UNLIKELY(! ++COUNTER(cipher, 0))) { - if (PREDICT_UNLIKELY(! ++COUNTER(cipher, 1))) { - if (PREDICT_UNLIKELY(! ++COUNTER(cipher, 2))) { - ++COUNTER(cipher, 3); - UPDATE_CTR_BUF(cipher, 3); +#ifdef CAN_USE_OPENSSL_CTR + if (should_use_openssl_CTR) { + if (cipher->using_evp) { + /* In openssl 1.0.0, there's an if'd out EVP_aes_128_ctr in evp.h. If + * it weren't disabled, it might be better just to use that. + */ + CRYPTO_ctr128_encrypt((const unsigned char *)input, + (unsigned char *)output, + len, + &cipher->key.evp, + cipher->ctr_buf.buf, + cipher->buf, + &cipher->pos, + evp_block128_fn); + } else { + AES_ctr128_encrypt((const unsigned char *)input, + (unsigned char *)output, + len, + &cipher->key.aes, + cipher->ctr_buf.buf, + cipher->buf, + &cipher->pos); + } + return; + } + else +#endif + { + int c = cipher->pos; + if (PREDICT_UNLIKELY(!len)) return; + + while (1) { + do { + if (len-- == 0) { cipher->pos = c; return; } + *(output++) = *(input++) ^ cipher->buf[c]; + } while (++c != 16); + cipher->pos = c = 0; + if (PREDICT_UNLIKELY(! ++COUNTER(cipher, 0))) { + if (PREDICT_UNLIKELY(! ++COUNTER(cipher, 1))) { + if (PREDICT_UNLIKELY(! ++COUNTER(cipher, 2))) { + ++COUNTER(cipher, 3); + UPDATE_CTR_BUF(cipher, 3); + } + UPDATE_CTR_BUF(cipher, 2); } - UPDATE_CTR_BUF(cipher, 2); + UPDATE_CTR_BUF(cipher, 1); } - UPDATE_CTR_BUF(cipher, 1); + UPDATE_CTR_BUF(cipher, 0); + _aes_fill_buf(cipher); } - UPDATE_CTR_BUF(cipher, 0); - _aes_fill_buf(cipher); } } @@ -229,32 +363,36 @@ aes_crypt(aes_cnt_cipher_t *cipher, const char *input, size_t len, void aes_crypt_inplace(aes_cnt_cipher_t *cipher, char *data, size_t len) { +#ifdef CAN_USE_OPENSSL_CTR + if (should_use_openssl_CTR) { + aes_crypt(cipher, data, len, data); + return; + } + else +#endif + { + int c = cipher->pos; + if (PREDICT_UNLIKELY(!len)) return; - /* XXXX This function is up to 5% of our runtime in some profiles; - * we should look into unrolling some of the loops; taking advantage - * of alignment, using a bigger buffer, and so on. Not till after 0.1.2.x, - * though. */ - int c = cipher->pos; - if (PREDICT_UNLIKELY(!len)) return; - - while (1) { - do { - if (len-- == 0) { cipher->pos = c; return; } - *(data++) ^= cipher->buf[c]; - } while (++c != 16); - cipher->pos = c = 0; - if (PREDICT_UNLIKELY(! ++COUNTER(cipher, 0))) { - if (PREDICT_UNLIKELY(! ++COUNTER(cipher, 1))) { - if (PREDICT_UNLIKELY(! ++COUNTER(cipher, 2))) { - ++COUNTER(cipher, 3); - UPDATE_CTR_BUF(cipher, 3); + while (1) { + do { + if (len-- == 0) { cipher->pos = c; return; } + *(data++) ^= cipher->buf[c]; + } while (++c != 16); + cipher->pos = c = 0; + if (PREDICT_UNLIKELY(! ++COUNTER(cipher, 0))) { + if (PREDICT_UNLIKELY(! ++COUNTER(cipher, 1))) { + if (PREDICT_UNLIKELY(! ++COUNTER(cipher, 2))) { + ++COUNTER(cipher, 3); + UPDATE_CTR_BUF(cipher, 3); + } + UPDATE_CTR_BUF(cipher, 2); } - UPDATE_CTR_BUF(cipher, 2); + UPDATE_CTR_BUF(cipher, 1); } - UPDATE_CTR_BUF(cipher, 1); + UPDATE_CTR_BUF(cipher, 0); + _aes_fill_buf(cipher); } - UPDATE_CTR_BUF(cipher, 0); - _aes_fill_buf(cipher); } } @@ -272,6 +410,9 @@ aes_set_iv(aes_cnt_cipher_t *cipher, const char *iv) cipher->pos = 0; memcpy(cipher->ctr_buf.buf, iv, 16); - _aes_fill_buf(cipher); +#ifdef CAN_USE_OPENSSL_CTR + if (!should_use_openssl_CTR) +#endif + _aes_fill_buf(cipher); } diff --git a/src/common/aes.h b/src/common/aes.h index b2591942bc..f7f0319183 100644 --- a/src/common/aes.h +++ b/src/common/aes.h @@ -24,5 +24,8 @@ void aes_crypt(aes_cnt_cipher_t *cipher, const char *input, size_t len, void aes_crypt_inplace(aes_cnt_cipher_t *cipher, char *data, size_t len); void aes_set_iv(aes_cnt_cipher_t *cipher, const char *iv); +int evaluate_evp_for_aes(int force_value); +int evaluate_ctr_for_aes(void); + #endif diff --git a/src/common/compat.c b/src/common/compat.c index 9a2c9d764b..ff9d877cd6 100644 --- a/src/common/compat.c +++ b/src/common/compat.c @@ -58,6 +58,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 @@ -971,7 +979,7 @@ tor_open_socket(int domain, int type, int protocol) /** As socket(), but counts the number of open sockets. */ tor_socket_t -tor_accept_socket(int sockfd, struct sockaddr *addr, socklen_t *len) +tor_accept_socket(tor_socket_t sockfd, struct sockaddr *addr, socklen_t *len) { tor_socket_t s; #if defined(HAVE_ACCEPT4) && defined(SOCK_CLOEXEC) @@ -1519,6 +1527,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. */ @@ -1575,6 +1635,42 @@ get_parent_directory(char *fname) return -1; } +/** 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 WINDOWS + 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 path[PATH_MAX+1]; + char *absfname = NULL; + + tor_assert(fname); + + if (fname[0] == '/') { + absfname = tor_strdup(fname); + } else { + if (getcwd(path, PATH_MAX) != NULL) { + tor_asprintf(&absfname, "%s/%s", path, fname); + } else { + /* If getcwd failed, the best we can do here is keep using the + * relative path. (Perhaps / isn't readable by this UID/GID.) */ + absfname = tor_strdup(fname); + } + } + + return absfname; +#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.) diff --git a/src/common/compat.h b/src/common/compat.h index b005dd2974..2db8107a8e 100644 --- a/src/common/compat.h +++ b/src/common/compat.h @@ -135,7 +135,6 @@ extern INLINE double U64_TO_DBL(uint64_t x) { /* GCC has several useful attributes. */ #if defined(__GNUC__) && __GNUC__ >= 3 #define ATTR_NORETURN __attribute__((noreturn)) -#define ATTR_PURE __attribute__((pure)) #define ATTR_CONST __attribute__((const)) #define ATTR_MALLOC __attribute__((malloc)) #define ATTR_NORETURN __attribute__((noreturn)) @@ -168,7 +167,6 @@ extern INLINE double U64_TO_DBL(uint64_t x) { #define PREDICT_UNLIKELY(exp) __builtin_expect(!!(exp), 0) #else #define ATTR_NORETURN -#define ATTR_PURE #define ATTR_CONST #define ATTR_MALLOC #define ATTR_NORETURN @@ -271,9 +269,9 @@ int tor_asprintf(char **strp, const char *fmt, ...) int tor_vasprintf(char **strp, const char *fmt, va_list args); const void *tor_memmem(const void *haystack, size_t hlen, const void *needle, - size_t nlen) ATTR_PURE ATTR_NONNULL((1,3)); + size_t nlen) ATTR_NONNULL((1,3)); static const void *tor_memstr(const void *haystack, size_t hlen, - const char *needle) ATTR_PURE ATTR_NONNULL((1,3)); + const char *needle) ATTR_NONNULL((1,3)); static INLINE const void * tor_memstr(const void *haystack, size_t hlen, const char *needle) { @@ -337,7 +335,7 @@ struct tm *tor_gmtime_r(const time_t *timep, struct tm *result); #define timeradd(tv1,tv2,tvout) \ do { \ (tvout)->tv_sec = (tv1)->tv_sec + (tv2)->tv_sec; \ - (tvout)->tv_usec = (tv2)->tv_usec + (tv2)->tv_usec; \ + (tvout)->tv_usec = (tv1)->tv_usec + (tv2)->tv_usec; \ if ((tvout)->tv_usec >= 1000000) { \ (tvout)->tv_usec -= 1000000; \ (tvout)->tv_sec++; \ @@ -351,7 +349,7 @@ struct tm *tor_gmtime_r(const time_t *timep, struct tm *result); #define timersub(tv1,tv2,tvout) \ do { \ (tvout)->tv_sec = (tv1)->tv_sec - (tv2)->tv_sec; \ - (tvout)->tv_usec = (tv2)->tv_usec - (tv2)->tv_usec; \ + (tvout)->tv_usec = (tv1)->tv_usec - (tv2)->tv_usec; \ if ((tvout)->tv_usec < 0) { \ (tvout)->tv_usec += 1000000; \ (tvout)->tv_sec--; \ @@ -410,7 +408,7 @@ typedef int socklen_t; int tor_close_socket(tor_socket_t s); tor_socket_t tor_open_socket(int domain, int type, int protocol); -tor_socket_t tor_accept_socket(int sockfd, struct sockaddr *addr, +tor_socket_t tor_accept_socket(tor_socket_t sockfd, struct sockaddr *addr, socklen_t *len); int get_n_open_sockets(void); @@ -546,9 +544,9 @@ long tor_weak_random(void); /* ===== OS compatibility */ const char *get_uname(void); -uint16_t get_uint16(const void *cp) ATTR_PURE ATTR_NONNULL((1)); -uint32_t get_uint32(const void *cp) ATTR_PURE ATTR_NONNULL((1)); -uint64_t get_uint64(const void *cp) ATTR_PURE ATTR_NONNULL((1)); +uint16_t get_uint16(const void *cp) ATTR_NONNULL((1)); +uint32_t get_uint32(const void *cp) ATTR_NONNULL((1)); +uint64_t get_uint64(const void *cp) ATTR_NONNULL((1)); void set_uint16(void *cp, uint16_t v) ATTR_NONNULL((1)); void set_uint32(void *cp, uint32_t v) ATTR_NONNULL((1)); void set_uint64(void *cp, uint64_t v) ATTR_NONNULL((1)); @@ -566,12 +564,14 @@ set_uint8(void *cp, uint8_t v) typedef unsigned long rlim_t; #endif int set_max_file_descriptors(rlim_t limit, int *max); +int tor_disable_debugger_attach(void); int switch_id(const char *user); #ifdef HAVE_PWD_H char *get_user_homedir(const char *username); #endif int get_parent_directory(char *fname); +char *make_path_absolute(char *fname); int spawn_func(void (*func)(void *), void *data); void spawn_exit(void) ATTR_NORETURN; diff --git a/src/common/container.h b/src/common/container.h index 4a6eba789d..fe071cc1b3 100644 --- a/src/common/container.h +++ b/src/common/container.h @@ -35,19 +35,14 @@ void smartlist_remove(smartlist_t *sl, const void *element); void *smartlist_pop_last(smartlist_t *sl); void smartlist_reverse(smartlist_t *sl); void smartlist_string_remove(smartlist_t *sl, const char *element); -int smartlist_isin(const smartlist_t *sl, const void *element) ATTR_PURE; -int smartlist_string_isin(const smartlist_t *sl, const char *element) - ATTR_PURE; -int smartlist_string_pos(const smartlist_t *, const char *elt) ATTR_PURE; -int smartlist_string_isin_case(const smartlist_t *sl, const char *element) - ATTR_PURE; -int smartlist_string_num_isin(const smartlist_t *sl, int num) ATTR_PURE; -int smartlist_strings_eq(const smartlist_t *sl1, const smartlist_t *sl2) - ATTR_PURE; -int smartlist_digest_isin(const smartlist_t *sl, const char *element) - ATTR_PURE; -int smartlist_overlap(const smartlist_t *sl1, const smartlist_t *sl2) - ATTR_PURE; +int smartlist_isin(const smartlist_t *sl, const void *element); +int smartlist_string_isin(const smartlist_t *sl, const char *element); +int smartlist_string_pos(const smartlist_t *, const char *elt); +int smartlist_string_isin_case(const smartlist_t *sl, const char *element); +int smartlist_string_num_isin(const smartlist_t *sl, int num); +int smartlist_strings_eq(const smartlist_t *sl1, const smartlist_t *sl2); +int smartlist_digest_isin(const smartlist_t *sl, const char *element); +int smartlist_overlap(const smartlist_t *sl1, const smartlist_t *sl2); void smartlist_intersect(smartlist_t *sl1, const smartlist_t *sl2); void smartlist_subtract(smartlist_t *sl1, const smartlist_t *sl2); @@ -55,14 +50,14 @@ void smartlist_subtract(smartlist_t *sl1, const smartlist_t *sl2); #ifdef DEBUG_SMARTLIST /** Return the number of items in sl. */ -static INLINE int smartlist_len(const smartlist_t *sl) ATTR_PURE; +static INLINE int smartlist_len(const smartlist_t *sl); static INLINE int smartlist_len(const smartlist_t *sl) { tor_assert(sl); return (sl)->num_used; } /** Return the <b>idx</b>th element of sl. */ -static INLINE void *smartlist_get(const smartlist_t *sl, int idx) ATTR_PURE; +static INLINE void *smartlist_get(const smartlist_t *sl, int idx); static INLINE void *smartlist_get(const smartlist_t *sl, int idx) { tor_assert(sl); tor_assert(idx>=0); @@ -114,8 +109,7 @@ void smartlist_uniq_strings(smartlist_t *sl); void smartlist_uniq_digests(smartlist_t *sl); void smartlist_uniq_digests256(smartlist_t *sl); void *smartlist_bsearch(smartlist_t *sl, const void *key, - int (*compare)(const void *key, const void **member)) - ATTR_PURE; + int (*compare)(const void *key, const void **member)); int smartlist_bsearch_idx(const smartlist_t *sl, const void *key, int (*compare)(const void *key, const void **member), int *found_out); diff --git a/src/common/crypto.c b/src/common/crypto.c index f2ef833522..e377b01d41 100644 --- a/src/common/crypto.c +++ b/src/common/crypto.c @@ -60,7 +60,7 @@ #include "container.h" #include "compat.h" -#if OPENSSL_VERSION_NUMBER < 0x00907000l +#if OPENSSL_VERSION_NUMBER < OPENSSL_V_SERIES(0,9,7) #error "We require OpenSSL >= 0.9.7" #endif @@ -69,7 +69,10 @@ #define DISABLE_ENGINES #endif -#if OPENSSL_VERSION_NUMBER < 0x00908000l +/** Longest recognized */ +#define MAX_DNS_LABEL_SIZE 63 + +#if OPENSSL_VERSION_NUMBER < OPENSSL_V_SERIES(0,9,8) /** @{ */ /** On OpenSSL versions before 0.9.8, there is no working SHA256 * implementation, so we use Tom St Denis's nice speedy one, slightly adapted @@ -276,6 +279,10 @@ crypto_global_init(int useAccel, const char *accelName, const char *accelDir) } else { log_info(LD_CRYPTO, "NOT using OpenSSL engine support."); } + + evaluate_evp_for_aes(-1); + evaluate_ctr_for_aes(); + return crypto_seed_rng(1); } return 0; @@ -288,37 +295,6 @@ crypto_thread_cleanup(void) ERR_remove_state(0); } -/** Uninitialize the crypto library. Return 0 on success, -1 on failure. - */ -int -crypto_global_cleanup(void) -{ - EVP_cleanup(); - ERR_remove_state(0); - ERR_free_strings(); - -#ifndef DISABLE_ENGINES - ENGINE_cleanup(); -#endif - - CONF_modules_unload(1); - CRYPTO_cleanup_all_ex_data(); -#ifdef TOR_IS_MULTITHREADED - if (_n_openssl_mutexes) { - int n = _n_openssl_mutexes; - tor_mutex_t **ms = _openssl_mutexes; - int i; - _openssl_mutexes = NULL; - _n_openssl_mutexes = 0; - for (i=0;i<n;++i) { - tor_mutex_free(ms[i]); - } - tor_free(ms); - } -#endif - return 0; -} - /** used by tortls.c: wrap an RSA* in a crypto_pk_env_t. */ crypto_pk_env_t * _crypto_new_pk_env_rsa(RSA *rsa) @@ -477,7 +453,7 @@ crypto_pk_generate_key_with_bits(crypto_pk_env_t *env, int bits) if (env->key) RSA_free(env->key); -#if OPENSSL_VERSION_NUMBER < 0x00908000l +#if OPENSSL_VERSION_NUMBER < OPENSSL_V_SERIES(0,9,8) /* In OpenSSL 0.9.7, RSA_generate_key is all we have. */ env->key = RSA_generate_key(bits, 65537, NULL, NULL); #else @@ -1748,7 +1724,7 @@ crypto_hmac_sha256(char *hmac_out, const char *key, size_t key_len, const char *msg, size_t msg_len) { -#if (OPENSSL_VERSION_NUMBER >= 0x00908000l) +#if OPENSSL_VERSION_NUMBER >= OPENSSL_V_SERIES(0,9,8) /* If we've got OpenSSL >=0.9.8 we can use its hmac implementation. */ tor_assert(key_len < INT_MAX); tor_assert(msg_len < INT_MAX); @@ -1809,6 +1785,9 @@ crypto_hmac_sha256(char *hmac_out, /* DH */ +/** Our DH 'g' parameter */ +#define DH_GENERATOR 2 + /** Shared P parameter for our circuit-crypto DH key exchanges. */ static BIGNUM *dh_param_p = NULL; /** Shared P parameter for our TLS DH key exchanges. */ @@ -1816,49 +1795,327 @@ static BIGNUM *dh_param_p_tls = NULL; /** Shared G parameter for our DH key exchanges. */ static BIGNUM *dh_param_g = NULL; +/** Generate and return a reasonable and safe DH parameter p. */ +static BIGNUM * +crypto_generate_dynamic_dh_modulus(void) +{ + BIGNUM *dynamic_dh_modulus; + DH *dh_parameters; + int r, dh_codes; + char *s; + + dynamic_dh_modulus = BN_new(); + tor_assert(dynamic_dh_modulus); + + dh_parameters = DH_generate_parameters(DH_BYTES*8, DH_GENERATOR, NULL, NULL); + tor_assert(dh_parameters); + + r = DH_check(dh_parameters, &dh_codes); + tor_assert(r && !dh_codes); + + BN_copy(dynamic_dh_modulus, dh_parameters->p); + tor_assert(dynamic_dh_modulus); + + DH_free(dh_parameters); + + { /* log the dynamic DH modulus: */ + s = BN_bn2hex(dynamic_dh_modulus); + tor_assert(s); + log_info(LD_OR, "Dynamic DH modulus generated: [%s]", s); + OPENSSL_free(s); + } + + return dynamic_dh_modulus; +} + +/** Store our dynamic DH modulus (and its group parameters) to + <b>fname</b> for future use. */ +static int +crypto_store_dynamic_dh_modulus(const char *fname) +{ + int len, new_len; + DH *dh = NULL; + unsigned char *dh_string_repr = NULL, *cp = NULL; + char *base64_encoded_dh = NULL; + char *file_string = NULL; + int retval = -1; + static const char file_header[] = "# This file contains stored Diffie-" + "Hellman parameters for future use.\n# You *do not* need to edit this " + "file.\n\n"; + + tor_assert(fname); + + if (!dh_param_p_tls) { + log_info(LD_CRYPTO, "Tried to store a DH modulus that does not exist."); + goto done; + } + + if (!(dh = DH_new())) + goto done; + if (!(dh->p = BN_dup(dh_param_p_tls))) + goto done; + if (!(dh->g = BN_new())) + goto done; + if (!BN_set_word(dh->g, DH_GENERATOR)) + goto done; + + len = i2d_DHparams(dh, NULL); + if (len < 0) { + log_warn(LD_CRYPTO, "Error occured while DER encoding DH modulus (1)."); + goto done; + } + + cp = dh_string_repr = tor_malloc_zero(len+1); + len = i2d_DHparams(dh, &cp); + if ((len < 0) || ((cp - dh_string_repr) != len)) { + log_warn(LD_CRYPTO, "Error occured while DER encoding DH modulus (2)."); + goto done; + } + + base64_encoded_dh = tor_malloc_zero(len * 2); /* should be enough */ + new_len = base64_encode(base64_encoded_dh, len * 2, + (char *)dh_string_repr, len); + if (new_len < 0) { + log_warn(LD_CRYPTO, "Error occured while base64-encoding DH modulus."); + goto done; + } + + /* concatenate file header and the dh parameters blob */ + new_len = tor_asprintf(&file_string, "%s%s", file_header, base64_encoded_dh); + + /* write to file */ + if (write_bytes_to_new_file(fname, file_string, new_len, 0) < 0) { + log_info(LD_CRYPTO, "'%s' was already occupied.", fname); + goto done; + } + + retval = 0; + + done: + if (dh) + DH_free(dh); + tor_free(dh_string_repr); + tor_free(base64_encoded_dh); + tor_free(file_string); + + return retval; +} + +/** Return the dynamic DH modulus stored in <b>fname</b>. If there is no + dynamic DH modulus stored in <b>fname</b>, return NULL. */ +static BIGNUM * +crypto_get_stored_dynamic_dh_modulus(const char *fname) +{ + int retval; + char *contents = NULL; + const char *contents_tmp = NULL; + int dh_codes; + char *fname_new = NULL; + DH *stored_dh = NULL; + BIGNUM *dynamic_dh_modulus = NULL; + int length = 0; + unsigned char *base64_decoded_dh = NULL; + const unsigned char *cp = NULL; + + tor_assert(fname); + + contents = read_file_to_str(fname, RFTS_IGNORE_MISSING, NULL); + if (!contents) { + log_info(LD_CRYPTO, "Could not open file '%s'", fname); + goto done; /*usually means that ENOENT. don't try to move file to broken.*/ + } + + /* skip the file header */ + contents_tmp = eat_whitespace(contents); + if (!*contents_tmp) { + log_warn(LD_CRYPTO, "Stored dynamic DH modulus file " + "seems corrupted (eat_whitespace)."); + goto err; + } + + /* 'fname' contains the DH parameters stored in base64-ed DER + * format. We are only interested in the DH modulus. + * NOTE: We allocate more storage here than we need. Since we're already + * doing that, we can also add 1 byte extra to appease Coverity's + * scanner. */ + + cp = base64_decoded_dh = tor_malloc_zero(strlen(contents_tmp) + 1); + length = base64_decode((char *)base64_decoded_dh, strlen(contents_tmp), + contents_tmp, strlen(contents_tmp)); + if (length < 0) { + log_warn(LD_CRYPTO, "Stored dynamic DH modulus seems corrupted (base64)."); + goto err; + } + + stored_dh = d2i_DHparams(NULL, &cp, length); + if ((!stored_dh) || (cp - base64_decoded_dh != length)) { + log_warn(LD_CRYPTO, "Stored dynamic DH modulus seems corrupted (d2i)."); + goto err; + } + + { /* check the cryptographic qualities of the stored dynamic DH modulus: */ + retval = DH_check(stored_dh, &dh_codes); + if (!retval || dh_codes) { + log_warn(LD_CRYPTO, "Stored dynamic DH modulus is not a safe prime."); + goto err; + } + + retval = DH_size(stored_dh); + if (retval < DH_BYTES) { + log_warn(LD_CRYPTO, "Stored dynamic DH modulus is smaller " + "than '%d' bits.", DH_BYTES*8); + goto err; + } + + if (!BN_is_word(stored_dh->g, 2)) { + log_warn(LD_CRYPTO, "Stored dynamic DH parameters do not use '2' " + "as the group generator."); + goto err; + } + } + + { /* log the dynamic DH modulus: */ + char *s = BN_bn2hex(stored_dh->p); + tor_assert(s); + log_info(LD_OR, "Found stored dynamic DH modulus: [%s]", s); + OPENSSL_free(s); + } + + goto done; + + err: + + { /* move broken prime to $filename.broken */ + fname_new = tor_malloc(strlen(fname) + 8); + + /* no can do if these functions return error */ + strlcpy(fname_new, fname, strlen(fname) + 8); + strlcat(fname_new, ".broken", strlen(fname) + 8); + + log_warn(LD_CRYPTO, "Moving broken dynamic DH prime to '%s'.", fname_new); + + if (replace_file(fname, fname_new)) + log_notice(LD_CRYPTO, "Error while moving '%s' to '%s'.", + fname, fname_new); + + tor_free(fname_new); + } + + if (stored_dh) { + DH_free(stored_dh); + stored_dh = NULL; + } + + done: + tor_free(contents); + tor_free(base64_decoded_dh); + + if (stored_dh) { + dynamic_dh_modulus = BN_dup(stored_dh->p); + DH_free(stored_dh); + } + + return dynamic_dh_modulus; +} + +/** Set the global TLS Diffie-Hellman modulus. + * If <b>dynamic_dh_modulus_fname</b> is set, try to read a dynamic DH modulus + * off it and use it as the DH modulus. If that's not possible, + * generate a new dynamic DH modulus. + * If <b>dynamic_dh_modulus_fname</b> is NULL, use the Apache mod_ssl DH + * modulus. */ +void +crypto_set_tls_dh_prime(const char *dynamic_dh_modulus_fname) +{ + BIGNUM *tls_prime = NULL; + int store_dh_prime_afterwards = 0; + int r; + + /* If the space is occupied, free the previous TLS DH prime */ + if (dh_param_p_tls) { + BN_free(dh_param_p_tls); + dh_param_p_tls = NULL; + } + + if (dynamic_dh_modulus_fname) { /* use dynamic DH modulus: */ + log_info(LD_OR, "Using stored dynamic DH modulus."); + tls_prime = crypto_get_stored_dynamic_dh_modulus(dynamic_dh_modulus_fname); + + if (!tls_prime) { + log_notice(LD_OR, "Generating fresh dynamic DH modulus. " + "This might take a while..."); + tls_prime = crypto_generate_dynamic_dh_modulus(); + + store_dh_prime_afterwards++; + } + } else { /* use the static DH prime modulus used by Apache in mod_ssl: */ + tls_prime = BN_new(); + tor_assert(tls_prime); + + /* This is the 1024-bit safe prime that Apache uses for its DH stuff; see + * modules/ssl/ssl_engine_dh.c; Apache also uses a generator of 2 with this + * prime. + */ + r =BN_hex2bn(&tls_prime, + "D67DE440CBBBDC1936D693D34AFD0AD50C84D239A45F520BB88174CB98" + "BCE951849F912E639C72FB13B4B4D7177E16D55AC179BA420B2A29FE324A" + "467A635E81FF5901377BEDDCFD33168A461AAD3B72DAE8860078045B07A7" + "DBCA7874087D1510EA9FCC9DDD330507DD62DB88AEAA747DE0F4D6E2BD68" + "B0E7393E0F24218EB3"); + tor_assert(r); + } + + tor_assert(tls_prime); + + dh_param_p_tls = tls_prime; + + if (store_dh_prime_afterwards) + /* save the new dynamic DH modulus to disk. */ + if (crypto_store_dynamic_dh_modulus(dynamic_dh_modulus_fname)) { + log_notice(LD_CRYPTO, "Failed while storing dynamic DH modulus. " + "Make sure your data directory is sane."); + } +} + /** Initialize dh_param_p and dh_param_g if they are not already * set. */ static void init_dh_param(void) { - BIGNUM *p, *p2, *g; + BIGNUM *circuit_dh_prime, *generator; int r; - if (dh_param_p && dh_param_g && dh_param_p_tls) + if (dh_param_p && dh_param_g) return; - p = BN_new(); - p2 = BN_new(); - g = BN_new(); - tor_assert(p); - tor_assert(p2); - tor_assert(g); + circuit_dh_prime = BN_new(); + generator = BN_new(); + tor_assert(circuit_dh_prime && generator); + + /* Set our generator for all DH parameters */ + r = BN_set_word(generator, DH_GENERATOR); + tor_assert(r); /* This is from rfc2409, section 6.2. It's a safe prime, and supposedly it equals: 2^1024 - 2^960 - 1 + 2^64 * { [2^894 pi] + 129093 }. */ - r = BN_hex2bn(&p, + r = BN_hex2bn(&circuit_dh_prime, "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E08" "8A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B" "302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9" "A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE6" "49286651ECE65381FFFFFFFFFFFFFFFF"); tor_assert(r); - /* This is the 1024-bit safe prime that Apache uses for its DH stuff; see - * modules/ssl/ssl_engine_dh.c */ - r = BN_hex2bn(&p2, - "D67DE440CBBBDC1936D693D34AFD0AD50C84D239A45F520BB88174CB98" - "BCE951849F912E639C72FB13B4B4D7177E16D55AC179BA420B2A29FE324A" - "467A635E81FF5901377BEDDCFD33168A461AAD3B72DAE8860078045B07A7" - "DBCA7874087D1510EA9FCC9DDD330507DD62DB88AEAA747DE0F4D6E2BD68" - "B0E7393E0F24218EB3"); - tor_assert(r); - r = BN_set_word(g, 2); - tor_assert(r); - dh_param_p = p; - dh_param_p_tls = p2; - dh_param_g = g; + /* Set the new values as the global DH parameters. */ + dh_param_p = circuit_dh_prime; + dh_param_g = generator; + + /* Ensure that we have TLS DH parameters set up, too, even if we're + going to change them soon. */ + if (!dh_param_p_tls) { + crypto_set_tls_dh_prime(NULL); + } } /** Number of bits to use when choosing the x or y value in a Diffie-Hellman @@ -2122,13 +2379,6 @@ crypto_dh_free(crypto_dh_env_t *dh) * work for us too. */ #define ADD_ENTROPY 32 -/** True iff we should use OpenSSL's RAND_poll function to add entropy to its - * pool. - * - * Use RAND_poll if OpenSSL is 0.9.6 release or later. (The "f" means - *"release".) */ -#define HAVE_RAND_POLL (OPENSSL_VERSION_NUMBER >= 0x0090600fl) - /** True iff it's safe to use RAND_poll after setup. * * Versions of OpenSSL prior to 0.9.7k and 0.9.8c had a bug where RAND_poll @@ -2136,9 +2386,9 @@ crypto_dh_free(crypto_dh_env_t *dh) * that fd without checking whether it fit in the fd_set. Thus, if the * system has not just been started up, it is unsafe to call */ #define RAND_POLL_IS_SAFE \ - ((OPENSSL_VERSION_NUMBER >= 0x009070afl && \ - OPENSSL_VERSION_NUMBER <= 0x00907fffl) || \ - (OPENSSL_VERSION_NUMBER >= 0x0090803fl)) + ((OPENSSL_VERSION_NUMBER >= OPENSSL_V(0,9,7,'j') && \ + OPENSSL_VERSION_NUMBER < OPENSSL_V_SERIES(0,9,8)) || \ + OPENSSL_VERSION_NUMBER >= OPENSSL_V(0,9,8,'c')) /** Set the seed of the weak RNG to a random value. */ static void @@ -2172,8 +2422,7 @@ crypto_seed_rng(int startup) size_t n; #endif -#if HAVE_RAND_POLL - /* OpenSSL 0.9.6 adds a RAND_poll function that knows about more kinds of + /* OpenSSL has a RAND_poll function that knows about more kinds of * entropy than we do. We'll try calling that, *and* calling our own entropy * functions. If one succeeds, we'll accept the RNG as seeded. */ if (startup || RAND_POLL_IS_SAFE) { @@ -2181,7 +2430,6 @@ crypto_seed_rng(int startup) if (rand_poll_status == 0) log_warn(LD_CRYPTO, "RAND_poll() failed."); } -#endif #ifdef MS_WINDOWS if (!provider_set) { @@ -2307,9 +2555,12 @@ crypto_rand_double(void) } /** Generate and return a new random hostname starting with <b>prefix</b>, - * ending with <b>suffix</b>, and containing no less than + * ending with <b>suffix</b>, and containing no fewer than * <b>min_rand_len</b> and no more than <b>max_rand_len</b> random base32 - * characters between. */ + * characters between. + * + * Clip <b>max_rand_len</b> to MAX_DNS_LABEL_SIZE. + **/ char * crypto_random_hostname(int min_rand_len, int max_rand_len, const char *prefix, const char *suffix) @@ -2318,8 +2569,13 @@ crypto_random_hostname(int min_rand_len, int max_rand_len, const char *prefix, int randlen, rand_bytes_len; size_t resultlen, prefixlen; - tor_assert(max_rand_len >= min_rand_len); + if (max_rand_len > MAX_DNS_LABEL_SIZE) + max_rand_len = MAX_DNS_LABEL_SIZE; + if (min_rand_len > max_rand_len) + min_rand_len = max_rand_len; + randlen = min_rand_len + crypto_rand_int(max_rand_len - min_rand_len + 1); + prefixlen = strlen(prefix); resultlen = prefixlen + strlen(suffix) + randlen + 16; @@ -2830,5 +3086,44 @@ setup_openssl_threading(void) return 0; } #endif + +/** Uninitialize the crypto library. Return 0 on success, -1 on failure. + */ +int +crypto_global_cleanup(void) +{ + EVP_cleanup(); + ERR_remove_state(0); + ERR_free_strings(); + + if (dh_param_p) + BN_free(dh_param_p); + if (dh_param_p_tls) + BN_free(dh_param_p_tls); + if (dh_param_g) + BN_free(dh_param_g); + +#ifndef DISABLE_ENGINES + ENGINE_cleanup(); +#endif + + CONF_modules_unload(1); + CRYPTO_cleanup_all_ex_data(); +#ifdef TOR_IS_MULTITHREADED + if (_n_openssl_mutexes) { + int n = _n_openssl_mutexes; + tor_mutex_t **ms = _openssl_mutexes; + int i; + _openssl_mutexes = NULL; + _n_openssl_mutexes = 0; + for (i=0;i<n;++i) { + tor_mutex_free(ms[i]); + } + tor_free(ms); + } +#endif + return 0; +} + /** @} */ diff --git a/src/common/crypto.h b/src/common/crypto.h index 80c10296a3..4783654445 100644 --- a/src/common/crypto.h +++ b/src/common/crypto.h @@ -16,6 +16,38 @@ #include <stdio.h> #include "torint.h" +/* + Macro to create an arbitrary OpenSSL version number as used by + OPENSSL_VERSION_NUMBER or SSLeay(), since the actual numbers are a bit hard + to read. + + Don't use this directly, instead use one of the other OPENSSL_V macros + below. + + The format is: 4 bits major, 8 bits minor, 8 bits fix, 8 bits patch, 4 bit + status. + */ +#define OPENSSL_VER(a,b,c,d,e) \ + (((a)<<28) | \ + ((b)<<20) | \ + ((c)<<12) | \ + ((d)<< 4) | \ + (e)) +/** An openssl release number. For example, OPENSSL_V(0,9,8,'j') is the + * version for the released version of 0.9.8j */ +#define OPENSSL_V(a,b,c,d) \ + OPENSSL_VER((a),(b),(c),(d)-'a'+1,0xf) +/** An openssl release number for the first release in the series. For + * example, OPENSSL_V_NOPATCH(1,0,0) is the first released version of OpenSSL + * 1.0.0. */ +#define OPENSSL_V_NOPATCH(a,b,c) \ + OPENSSL_VER((a),(b),(c),0,0xf) +/** The first version that would occur for any alpha or beta in an openssl + * series. For example, OPENSSL_V_SERIES(0,9,8) is greater than any released + * 0.9.7, and less than any released 0.9.8. */ +#define OPENSSL_V_SERIES(a,b,c) \ + OPENSSL_VER((a),(b),(c),0,0) + /** Length of the output of our message digest. */ #define DIGEST_LEN 20 /** Length of the output of our second (improved) message digests. (For now @@ -91,6 +123,8 @@ int crypto_global_cleanup(void); crypto_pk_env_t *crypto_new_pk_env(void); void crypto_free_pk_env(crypto_pk_env_t *env); +void crypto_set_tls_dh_prime(const char *dynamic_dh_modulus_fname); + /* convenience function: wraps crypto_create_crypto_env, set_key, and init. */ crypto_cipher_env_t *crypto_create_init_cipher(const char *key, int encrypt_mode); diff --git a/src/common/tortls.c b/src/common/tortls.c index 554deb7bda..28bb3f9b41 100644 --- a/src/common/tortls.c +++ b/src/common/tortls.c @@ -44,10 +44,6 @@ #include <openssl/bio.h> #include <openssl/opensslv.h> -#if OPENSSL_VERSION_NUMBER < 0x00907000l -#error "We require OpenSSL >= 0.9.7" -#endif - #ifdef USE_BUFFEREVENTS #include <event2/bufferevent_ssl.h> #include <event2/buffer.h> @@ -65,6 +61,10 @@ #include "container.h" #include <string.h> +#if OPENSSL_VERSION_NUMBER < OPENSSL_V_SERIES(0,9,7) +#error "We require OpenSSL >= 0.9.7" +#endif + /* Enable the "v2" TLS handshake. */ #define V2_HANDSHAKE_SERVER @@ -79,6 +79,16 @@ #define ADDR(tls) (((tls) && (tls)->address) ? tls->address : "peer") +#if (OPENSSL_VERSION_NUMBER < OPENSSL_V(0,9,8,'s') || \ + (OPENSSL_VERSION_NUMBER >= OPENSSL_V_SERIES(0,9,9) && \ + OPENSSL_VERSION_NUMBER < OPENSSL_V(1,0,0,'f'))) +/* This is a version of OpenSSL before 0.9.8s/1.0.0f. It does not have + * the CVE-2011-4657 fix, and as such it can't use RELEASE_BUFFERS and + * SSL3 safely at the same time. + */ +#define DISABLE_SSL3_HANDSHAKE +#endif + /* We redefine these so that we can run correctly even if the vendor gives us * a version of OpenSSL that does not match its header files. (Apple: I am * looking at you.) @@ -227,22 +237,6 @@ static int tls_library_is_initialized = 0; #define _TOR_TLS_SYSCALL (_MIN_TOR_TLS_ERROR_VAL - 2) #define _TOR_TLS_ZERORETURN (_MIN_TOR_TLS_ERROR_VAL - 1) -#include "tortls_states.h" - -/** Return the symbolic name of an OpenSSL state. */ -static const char * -ssl_state_to_string(int ssl_state) -{ - static char buf[40]; - int i; - for (i = 0; state_map[i].name; ++i) { - if (state_map[i].state == ssl_state) - return state_map[i].name; - } - tor_snprintf(buf, sizeof(buf), "Unknown state %d", ssl_state); - return buf; -} - /** Write a description of the current state of <b>tls</b> into the * <b>sz</b>-byte buffer at <b>buf</b>. */ void @@ -256,7 +250,7 @@ tor_tls_get_state_description(tor_tls_t *tls, char *buf, size_t sz) return; } - ssl_state = ssl_state_to_string(tls->ssl->state); + ssl_state = SSL_state_string_long(tls->ssl); switch (tls->state) { #define CASE(st) case TOR_TLS_ST_##st: tortls_state = " in "#st ; break CASE(HANDSHAKE); @@ -283,10 +277,8 @@ tor_tls_log_one_error(tor_tls_t *tls, unsigned long err, { const char *state = NULL, *addr; const char *msg, *lib, *func; - int st; - st = (tls && tls->ssl) ? tls->ssl->state : -1; - state = (st>=0)?ssl_state_to_string(st):"---"; + state = (tls && tls->ssl)?SSL_state_string_long(tls->ssl):"---"; addr = tls ? tls->address : NULL; @@ -423,14 +415,14 @@ tor_tls_get_error(tor_tls_t *tls, int r, int extra, return _TOR_TLS_SYSCALL; if (r == 0) { log(severity, LD_NET, "TLS error: unexpected close while %s (%s)", - doing, ssl_state_to_string(tls->ssl->state)); + doing, SSL_state_string_long(tls->ssl)); tor_error = TOR_TLS_ERROR_IO; } else { int e = tor_socket_errno(tls->socket); log(severity, LD_NET, "TLS error: <syscall error while %s> (errno=%d: %s; state=%s)", doing, e, tor_socket_strerror(e), - ssl_state_to_string(tls->ssl->state)); + SSL_state_string_long(tls->ssl)); tor_error = tor_errno_to_tls_error(e); } tls_log_errors(tls, severity, domain, doing); @@ -439,7 +431,7 @@ tor_tls_get_error(tor_tls_t *tls, int r, int extra, if (extra&CATCH_ZERO) return _TOR_TLS_ZERORETURN; log(severity, LD_NET, "TLS connection closed while %s in state %s", - doing, ssl_state_to_string(tls->ssl->state)); + doing, SSL_state_string_long(tls->ssl)); tls_log_errors(tls, severity, domain, doing); return TOR_TLS_CLOSE; default: @@ -482,18 +474,18 @@ tor_tls_init(void) * program should be allowed to use renegotiation unless it first passed * a test of intelligence and determination. */ - if (version >= 0x009080c0L && version < 0x009080d0L) { + if (version > OPENSSL_V(0,9,8,'k') && version <= OPENSSL_V(0,9,8,'l')) { log_notice(LD_GENERAL, "OpenSSL %s looks like version 0.9.8l; " "I will try SSL3_FLAGS to enable renegotation.", SSLeay_version(SSLEAY_VERSION)); use_unsafe_renegotiation_flag = 1; use_unsafe_renegotiation_op = 1; - } else if (version >= 0x009080d0L) { + } else if (version > OPENSSL_V(0,9,8,'l')) { log_notice(LD_GENERAL, "OpenSSL %s looks like version 0.9.8m or later; " "I will try SSL_OP to enable renegotiation", SSLeay_version(SSLEAY_VERSION)); use_unsafe_renegotiation_op = 1; - } else if (version < 0x009080c0L) { + } else if (version <= OPENSSL_V(0,9,8,'k')) { log_notice(LD_GENERAL, "OpenSSL %s [%lx] looks like it's older than " "0.9.8l, but some vendors have backported 0.9.8l's " "renegotiation code to earlier versions, and some have " @@ -503,6 +495,7 @@ tor_tls_init(void) use_unsafe_renegotiation_flag = 1; use_unsafe_renegotiation_op = 1; } else { + /* this is dead code, yes? */ log_info(LD_GENERAL, "OpenSSL %s has version %lx", SSLeay_version(SSLEAY_VERSION), version); } @@ -580,7 +573,13 @@ tor_tls_create_certificate(crypto_pk_env_t *rsa, const char *cname_sign, unsigned int cert_lifetime) { + /* OpenSSL generates self-signed certificates with random 64-bit serial + * numbers, so let's do that too. */ +#define SERIAL_NUMBER_SIZE 8 + time_t start_time, end_time; + BIGNUM *serial_number = NULL; + unsigned char serial_tmp[SERIAL_NUMBER_SIZE]; EVP_PKEY *sign_pkey = NULL, *pkey=NULL; X509 *x509 = NULL; X509_NAME *name = NULL, *name_issuer=NULL; @@ -601,8 +600,15 @@ tor_tls_create_certificate(crypto_pk_env_t *rsa, goto error; if (!(X509_set_version(x509, 2))) goto error; - if (!(ASN1_INTEGER_set(X509_get_serialNumber(x509), (long)start_time))) - goto error; + + { /* our serial number is 8 random bytes. */ + if (crypto_rand((char *)serial_tmp, sizeof(serial_tmp)) < 0) + goto error; + if (!(serial_number = BN_bin2bn(serial_tmp, sizeof(serial_tmp), NULL))) + goto error; + if (!(BN_to_ASN1_INTEGER(serial_number, X509_get_serialNumber(x509)))) + goto error; + } if (!(name = tor_x509_name_new(cname))) goto error; @@ -635,11 +641,15 @@ tor_tls_create_certificate(crypto_pk_env_t *rsa, EVP_PKEY_free(sign_pkey); if (pkey) EVP_PKEY_free(pkey); + if (serial_number) + BN_free(serial_number); if (name) X509_NAME_free(name); if (name_issuer) X509_NAME_free(name_issuer); return x509; + +#undef SERIAL_NUMBER_SIZE } /** List of ciphers that servers should select from.*/ @@ -760,7 +770,7 @@ tor_cert_decode(const uint8_t *certificate, size_t certificate_len) if (certificate_len > INT_MAX) return NULL; -#if OPENSSL_VERSION_NUMBER < 0x00908000l +#if OPENSSL_VERSION_NUMBER < OPENSSL_V_SERIES(0,9,8) /* This ifdef suppresses a type warning. Take out this case once everybody * is using OpenSSL 0.9.8 or later. */ x509 = d2i_X509(NULL, (unsigned char**)&cp, (int)certificate_len); @@ -1149,16 +1159,37 @@ tor_tls_context_new(crypto_pk_env_t *identity, unsigned int key_lifetime, result->auth_key = crypto_pk_dup_key(rsa_auth); } -#ifdef EVERYONE_HAS_AES - /* Tell OpenSSL to only use TLS1 */ +#if 0 + /* Tell OpenSSL to only use TLS1. This may have subtly different results + * from SSLv23_method() with SSLv2 and SSLv3 disabled, so we need to do some + * investigation before we consider adjusting it. It should be compatible + * with existing Tors. */ if (!(result->ctx = SSL_CTX_new(TLSv1_method()))) goto error; -#else +#endif + /* Tell OpenSSL to use SSL3 or TLS1 but not SSL2. */ if (!(result->ctx = SSL_CTX_new(SSLv23_method()))) goto error; SSL_CTX_set_options(result->ctx, SSL_OP_NO_SSLv2); + + if ( +#ifdef DISABLE_SSL3_HANDSHAKE + 1 || #endif + SSLeay() < OPENSSL_V(0,9,8,'s') || + (SSLeay() >= OPENSSL_V_SERIES(0,9,9) && + SSLeay() < OPENSSL_V(1,0,0,'f'))) { + /* And not SSL3 if it's subject to CVE-2011-4657. */ + log_info(LD_NET, "Disabling SSLv3 because this OpenSSL version " + "might otherwise be vulnerable to CVE-2011-4657 " + "(compile-time version %08lx (%s); " + "runtime version %08lx (%s))", + (unsigned long)OPENSSL_VERSION_NUMBER, OPENSSL_VERSION_TEXT, + (unsigned long)SSLeay(), SSLeay_version(SSLEAY_VERSION)); + SSL_CTX_set_options(result->ctx, SSL_OP_NO_SSLv3); + } + SSL_CTX_set_options(result->ctx, SSL_OP_SINGLE_DH_USE); #ifdef SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION @@ -1301,7 +1332,7 @@ static void tor_tls_debug_state_callback(const SSL *ssl, int type, int val) { log_debug(LD_HANDSHAKE, "SSL %p is now in state %s [type=%d,val=%d].", - ssl, ssl_state_to_string(ssl->state), type, val); + ssl, SSL_state_string_long(ssl), type, val); } /** Invoked when we're accepting a connection on <b>ssl</b>, and the connection @@ -1706,16 +1737,16 @@ tor_tls_handshake(tor_tls_t *tls) oldstate = tls->ssl->state; if (tls->isServer) { log_debug(LD_HANDSHAKE, "About to call SSL_accept on %p (%s)", tls, - ssl_state_to_string(tls->ssl->state)); + SSL_state_string_long(tls->ssl)); r = SSL_accept(tls->ssl); } else { log_debug(LD_HANDSHAKE, "About to call SSL_connect on %p (%s)", tls, - ssl_state_to_string(tls->ssl->state)); + SSL_state_string_long(tls->ssl)); r = SSL_connect(tls->ssl); } if (oldstate != tls->ssl->state) log_debug(LD_HANDSHAKE, "After call, %p was in state %s", - tls, ssl_state_to_string(tls->ssl->state)); + tls, SSL_state_string_long(tls->ssl)); /* We need to call this here and not earlier, since OpenSSL has a penchant * for clearing its flags when you say accept or connect. */ tor_tls_unblock_renegotiation(tls); diff --git a/src/common/tortls_states.h b/src/common/tortls_states.h deleted file mode 100644 index dcff2479f6..0000000000 --- a/src/common/tortls_states.h +++ /dev/null @@ -1,414 +0,0 @@ -/* Copyright (c) 2003, Roger Dingledine - * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2011, The Tor Project, Inc. */ -/* See LICENSE for licensing information */ - -/* Helper file: included only in tortls.c */ - -#ifndef _TORTLS_STATES_H -#define _TORTLS_STATES_H - -/* The main body of this file was mechanically generated with this - perl script: - - my %keys = (); - for $fn (@ARGV) { - open(F, $fn); - while (<F>) { - next unless /^#define ((?:SSL|DTLS)\w*_ST_\w*)/; - $keys{$1} = 1; - } - close(F); - } - for $k (sort keys %keys) { - print "#ifdef $k\n S($k),\n#endif\n" - } -*/ - -/** Mapping from allowed value of SSL.state to the name of C macro for that - * state. Used for debugging an openssl connection. */ -static const struct { int state; const char *name; } state_map[] = { -#define S(state) { state, #state } -#ifdef DTLS1_ST_CR_HELLO_VERIFY_REQUEST_A - S(DTLS1_ST_CR_HELLO_VERIFY_REQUEST_A), -#endif -#ifdef DTLS1_ST_CR_HELLO_VERIFY_REQUEST_B - S(DTLS1_ST_CR_HELLO_VERIFY_REQUEST_B), -#endif -#ifdef DTLS1_ST_SW_HELLO_VERIFY_REQUEST_A - S(DTLS1_ST_SW_HELLO_VERIFY_REQUEST_A), -#endif -#ifdef DTLS1_ST_SW_HELLO_VERIFY_REQUEST_B - S(DTLS1_ST_SW_HELLO_VERIFY_REQUEST_B), -#endif -#ifdef SSL23_ST_CR_SRVR_HELLO_A - S(SSL23_ST_CR_SRVR_HELLO_A), -#endif -#ifdef SSL23_ST_CR_SRVR_HELLO_B - S(SSL23_ST_CR_SRVR_HELLO_B), -#endif -#ifdef SSL23_ST_CW_CLNT_HELLO_A - S(SSL23_ST_CW_CLNT_HELLO_A), -#endif -#ifdef SSL23_ST_CW_CLNT_HELLO_B - S(SSL23_ST_CW_CLNT_HELLO_B), -#endif -#ifdef SSL23_ST_SR_CLNT_HELLO_A - S(SSL23_ST_SR_CLNT_HELLO_A), -#endif -#ifdef SSL23_ST_SR_CLNT_HELLO_B - S(SSL23_ST_SR_CLNT_HELLO_B), -#endif -#ifdef SSL2_ST_CLIENT_START_ENCRYPTION - S(SSL2_ST_CLIENT_START_ENCRYPTION), -#endif -#ifdef SSL2_ST_GET_CLIENT_FINISHED_A - S(SSL2_ST_GET_CLIENT_FINISHED_A), -#endif -#ifdef SSL2_ST_GET_CLIENT_FINISHED_B - S(SSL2_ST_GET_CLIENT_FINISHED_B), -#endif -#ifdef SSL2_ST_GET_CLIENT_HELLO_A - S(SSL2_ST_GET_CLIENT_HELLO_A), -#endif -#ifdef SSL2_ST_GET_CLIENT_HELLO_B - S(SSL2_ST_GET_CLIENT_HELLO_B), -#endif -#ifdef SSL2_ST_GET_CLIENT_HELLO_C - S(SSL2_ST_GET_CLIENT_HELLO_C), -#endif -#ifdef SSL2_ST_GET_CLIENT_MASTER_KEY_A - S(SSL2_ST_GET_CLIENT_MASTER_KEY_A), -#endif -#ifdef SSL2_ST_GET_CLIENT_MASTER_KEY_B - S(SSL2_ST_GET_CLIENT_MASTER_KEY_B), -#endif -#ifdef SSL2_ST_GET_SERVER_FINISHED_A - S(SSL2_ST_GET_SERVER_FINISHED_A), -#endif -#ifdef SSL2_ST_GET_SERVER_FINISHED_B - S(SSL2_ST_GET_SERVER_FINISHED_B), -#endif -#ifdef SSL2_ST_GET_SERVER_HELLO_A - S(SSL2_ST_GET_SERVER_HELLO_A), -#endif -#ifdef SSL2_ST_GET_SERVER_HELLO_B - S(SSL2_ST_GET_SERVER_HELLO_B), -#endif -#ifdef SSL2_ST_GET_SERVER_VERIFY_A - S(SSL2_ST_GET_SERVER_VERIFY_A), -#endif -#ifdef SSL2_ST_GET_SERVER_VERIFY_B - S(SSL2_ST_GET_SERVER_VERIFY_B), -#endif -#ifdef SSL2_ST_SEND_CLIENT_CERTIFICATE_A - S(SSL2_ST_SEND_CLIENT_CERTIFICATE_A), -#endif -#ifdef SSL2_ST_SEND_CLIENT_CERTIFICATE_B - S(SSL2_ST_SEND_CLIENT_CERTIFICATE_B), -#endif -#ifdef SSL2_ST_SEND_CLIENT_CERTIFICATE_C - S(SSL2_ST_SEND_CLIENT_CERTIFICATE_C), -#endif -#ifdef SSL2_ST_SEND_CLIENT_CERTIFICATE_D - S(SSL2_ST_SEND_CLIENT_CERTIFICATE_D), -#endif -#ifdef SSL2_ST_SEND_CLIENT_FINISHED_A - S(SSL2_ST_SEND_CLIENT_FINISHED_A), -#endif -#ifdef SSL2_ST_SEND_CLIENT_FINISHED_B - S(SSL2_ST_SEND_CLIENT_FINISHED_B), -#endif -#ifdef SSL2_ST_SEND_CLIENT_HELLO_A - S(SSL2_ST_SEND_CLIENT_HELLO_A), -#endif -#ifdef SSL2_ST_SEND_CLIENT_HELLO_B - S(SSL2_ST_SEND_CLIENT_HELLO_B), -#endif -#ifdef SSL2_ST_SEND_CLIENT_MASTER_KEY_A - S(SSL2_ST_SEND_CLIENT_MASTER_KEY_A), -#endif -#ifdef SSL2_ST_SEND_CLIENT_MASTER_KEY_B - S(SSL2_ST_SEND_CLIENT_MASTER_KEY_B), -#endif -#ifdef SSL2_ST_SEND_REQUEST_CERTIFICATE_A - S(SSL2_ST_SEND_REQUEST_CERTIFICATE_A), -#endif -#ifdef SSL2_ST_SEND_REQUEST_CERTIFICATE_B - S(SSL2_ST_SEND_REQUEST_CERTIFICATE_B), -#endif -#ifdef SSL2_ST_SEND_REQUEST_CERTIFICATE_C - S(SSL2_ST_SEND_REQUEST_CERTIFICATE_C), -#endif -#ifdef SSL2_ST_SEND_REQUEST_CERTIFICATE_D - S(SSL2_ST_SEND_REQUEST_CERTIFICATE_D), -#endif -#ifdef SSL2_ST_SEND_SERVER_FINISHED_A - S(SSL2_ST_SEND_SERVER_FINISHED_A), -#endif -#ifdef SSL2_ST_SEND_SERVER_FINISHED_B - S(SSL2_ST_SEND_SERVER_FINISHED_B), -#endif -#ifdef SSL2_ST_SEND_SERVER_HELLO_A - S(SSL2_ST_SEND_SERVER_HELLO_A), -#endif -#ifdef SSL2_ST_SEND_SERVER_HELLO_B - S(SSL2_ST_SEND_SERVER_HELLO_B), -#endif -#ifdef SSL2_ST_SEND_SERVER_VERIFY_A - S(SSL2_ST_SEND_SERVER_VERIFY_A), -#endif -#ifdef SSL2_ST_SEND_SERVER_VERIFY_B - S(SSL2_ST_SEND_SERVER_VERIFY_B), -#endif -#ifdef SSL2_ST_SEND_SERVER_VERIFY_C - S(SSL2_ST_SEND_SERVER_VERIFY_C), -#endif -#ifdef SSL2_ST_SERVER_START_ENCRYPTION - S(SSL2_ST_SERVER_START_ENCRYPTION), -#endif -#ifdef SSL2_ST_X509_GET_CLIENT_CERTIFICATE - S(SSL2_ST_X509_GET_CLIENT_CERTIFICATE), -#endif -#ifdef SSL2_ST_X509_GET_SERVER_CERTIFICATE - S(SSL2_ST_X509_GET_SERVER_CERTIFICATE), -#endif -#ifdef SSL3_ST_CR_CERT_A - S(SSL3_ST_CR_CERT_A), -#endif -#ifdef SSL3_ST_CR_CERT_B - S(SSL3_ST_CR_CERT_B), -#endif -#ifdef SSL3_ST_CR_CERT_REQ_A - S(SSL3_ST_CR_CERT_REQ_A), -#endif -#ifdef SSL3_ST_CR_CERT_REQ_B - S(SSL3_ST_CR_CERT_REQ_B), -#endif -#ifdef SSL3_ST_CR_CERT_STATUS_A - S(SSL3_ST_CR_CERT_STATUS_A), -#endif -#ifdef SSL3_ST_CR_CERT_STATUS_B - S(SSL3_ST_CR_CERT_STATUS_B), -#endif -#ifdef SSL3_ST_CR_CHANGE_A - S(SSL3_ST_CR_CHANGE_A), -#endif -#ifdef SSL3_ST_CR_CHANGE_B - S(SSL3_ST_CR_CHANGE_B), -#endif -#ifdef SSL3_ST_CR_FINISHED_A - S(SSL3_ST_CR_FINISHED_A), -#endif -#ifdef SSL3_ST_CR_FINISHED_B - S(SSL3_ST_CR_FINISHED_B), -#endif -#ifdef SSL3_ST_CR_KEY_EXCH_A - S(SSL3_ST_CR_KEY_EXCH_A), -#endif -#ifdef SSL3_ST_CR_KEY_EXCH_B - S(SSL3_ST_CR_KEY_EXCH_B), -#endif -#ifdef SSL3_ST_CR_SESSION_TICKET_A - S(SSL3_ST_CR_SESSION_TICKET_A), -#endif -#ifdef SSL3_ST_CR_SESSION_TICKET_B - S(SSL3_ST_CR_SESSION_TICKET_B), -#endif -#ifdef SSL3_ST_CR_SRVR_DONE_A - S(SSL3_ST_CR_SRVR_DONE_A), -#endif -#ifdef SSL3_ST_CR_SRVR_DONE_B - S(SSL3_ST_CR_SRVR_DONE_B), -#endif -#ifdef SSL3_ST_CR_SRVR_HELLO_A - S(SSL3_ST_CR_SRVR_HELLO_A), -#endif -#ifdef SSL3_ST_CR_SRVR_HELLO_B - S(SSL3_ST_CR_SRVR_HELLO_B), -#endif -#ifdef SSL3_ST_CW_CERT_A - S(SSL3_ST_CW_CERT_A), -#endif -#ifdef SSL3_ST_CW_CERT_B - S(SSL3_ST_CW_CERT_B), -#endif -#ifdef SSL3_ST_CW_CERT_C - S(SSL3_ST_CW_CERT_C), -#endif -#ifdef SSL3_ST_CW_CERT_D - S(SSL3_ST_CW_CERT_D), -#endif -#ifdef SSL3_ST_CW_CERT_VRFY_A - S(SSL3_ST_CW_CERT_VRFY_A), -#endif -#ifdef SSL3_ST_CW_CERT_VRFY_B - S(SSL3_ST_CW_CERT_VRFY_B), -#endif -#ifdef SSL3_ST_CW_CHANGE_A - S(SSL3_ST_CW_CHANGE_A), -#endif -#ifdef SSL3_ST_CW_CHANGE_B - S(SSL3_ST_CW_CHANGE_B), -#endif -#ifdef SSL3_ST_CW_CLNT_HELLO_A - S(SSL3_ST_CW_CLNT_HELLO_A), -#endif -#ifdef SSL3_ST_CW_CLNT_HELLO_B - S(SSL3_ST_CW_CLNT_HELLO_B), -#endif -#ifdef SSL3_ST_CW_FINISHED_A - S(SSL3_ST_CW_FINISHED_A), -#endif -#ifdef SSL3_ST_CW_FINISHED_B - S(SSL3_ST_CW_FINISHED_B), -#endif -#ifdef SSL3_ST_CW_FLUSH - S(SSL3_ST_CW_FLUSH), -#endif -#ifdef SSL3_ST_CW_KEY_EXCH_A - S(SSL3_ST_CW_KEY_EXCH_A), -#endif -#ifdef SSL3_ST_CW_KEY_EXCH_B - S(SSL3_ST_CW_KEY_EXCH_B), -#endif -#ifdef SSL3_ST_SR_CERT_A - S(SSL3_ST_SR_CERT_A), -#endif -#ifdef SSL3_ST_SR_CERT_B - S(SSL3_ST_SR_CERT_B), -#endif -#ifdef SSL3_ST_SR_CERT_VRFY_A - S(SSL3_ST_SR_CERT_VRFY_A), -#endif -#ifdef SSL3_ST_SR_CERT_VRFY_B - S(SSL3_ST_SR_CERT_VRFY_B), -#endif -#ifdef SSL3_ST_SR_CHANGE_A - S(SSL3_ST_SR_CHANGE_A), -#endif -#ifdef SSL3_ST_SR_CHANGE_B - S(SSL3_ST_SR_CHANGE_B), -#endif -#ifdef SSL3_ST_SR_CLNT_HELLO_A - S(SSL3_ST_SR_CLNT_HELLO_A), -#endif -#ifdef SSL3_ST_SR_CLNT_HELLO_B - S(SSL3_ST_SR_CLNT_HELLO_B), -#endif -#ifdef SSL3_ST_SR_CLNT_HELLO_C - S(SSL3_ST_SR_CLNT_HELLO_C), -#endif -#ifdef SSL3_ST_SR_FINISHED_A - S(SSL3_ST_SR_FINISHED_A), -#endif -#ifdef SSL3_ST_SR_FINISHED_B - S(SSL3_ST_SR_FINISHED_B), -#endif -#ifdef SSL3_ST_SR_KEY_EXCH_A - S(SSL3_ST_SR_KEY_EXCH_A), -#endif -#ifdef SSL3_ST_SR_KEY_EXCH_B - S(SSL3_ST_SR_KEY_EXCH_B), -#endif -#ifdef SSL3_ST_SW_CERT_A - S(SSL3_ST_SW_CERT_A), -#endif -#ifdef SSL3_ST_SW_CERT_B - S(SSL3_ST_SW_CERT_B), -#endif -#ifdef SSL3_ST_SW_CERT_REQ_A - S(SSL3_ST_SW_CERT_REQ_A), -#endif -#ifdef SSL3_ST_SW_CERT_REQ_B - S(SSL3_ST_SW_CERT_REQ_B), -#endif -#ifdef SSL3_ST_SW_CERT_STATUS_A - S(SSL3_ST_SW_CERT_STATUS_A), -#endif -#ifdef SSL3_ST_SW_CERT_STATUS_B - S(SSL3_ST_SW_CERT_STATUS_B), -#endif -#ifdef SSL3_ST_SW_CHANGE_A - S(SSL3_ST_SW_CHANGE_A), -#endif -#ifdef SSL3_ST_SW_CHANGE_B - S(SSL3_ST_SW_CHANGE_B), -#endif -#ifdef SSL3_ST_SW_FINISHED_A - S(SSL3_ST_SW_FINISHED_A), -#endif -#ifdef SSL3_ST_SW_FINISHED_B - S(SSL3_ST_SW_FINISHED_B), -#endif -#ifdef SSL3_ST_SW_FLUSH - S(SSL3_ST_SW_FLUSH), -#endif -#ifdef SSL3_ST_SW_HELLO_REQ_A - S(SSL3_ST_SW_HELLO_REQ_A), -#endif -#ifdef SSL3_ST_SW_HELLO_REQ_B - S(SSL3_ST_SW_HELLO_REQ_B), -#endif -#ifdef SSL3_ST_SW_HELLO_REQ_C - S(SSL3_ST_SW_HELLO_REQ_C), -#endif -#ifdef SSL3_ST_SW_KEY_EXCH_A - S(SSL3_ST_SW_KEY_EXCH_A), -#endif -#ifdef SSL3_ST_SW_KEY_EXCH_B - S(SSL3_ST_SW_KEY_EXCH_B), -#endif -#ifdef SSL3_ST_SW_SESSION_TICKET_A - S(SSL3_ST_SW_SESSION_TICKET_A), -#endif -#ifdef SSL3_ST_SW_SESSION_TICKET_B - S(SSL3_ST_SW_SESSION_TICKET_B), -#endif -#ifdef SSL3_ST_SW_SRVR_DONE_A - S(SSL3_ST_SW_SRVR_DONE_A), -#endif -#ifdef SSL3_ST_SW_SRVR_DONE_B - S(SSL3_ST_SW_SRVR_DONE_B), -#endif -#ifdef SSL3_ST_SW_SRVR_HELLO_A - S(SSL3_ST_SW_SRVR_HELLO_A), -#endif -#ifdef SSL3_ST_SW_SRVR_HELLO_B - S(SSL3_ST_SW_SRVR_HELLO_B), -#endif -#ifdef SSL_ST_ACCEPT - S(SSL_ST_ACCEPT), -#endif -#ifdef SSL_ST_BEFORE - S(SSL_ST_BEFORE), -#endif -#ifdef SSL_ST_CONNECT - S(SSL_ST_CONNECT), -#endif -#ifdef SSL_ST_INIT - S(SSL_ST_INIT), -#endif -#ifdef SSL_ST_MASK - S(SSL_ST_MASK), -#endif -#ifdef SSL_ST_OK - S(SSL_ST_OK), -#endif -#ifdef SSL_ST_READ_BODY - S(SSL_ST_READ_BODY), -#endif -#ifdef SSL_ST_READ_DONE - S(SSL_ST_READ_DONE), -#endif -#ifdef SSL_ST_READ_HEADER - S(SSL_ST_READ_HEADER), -#endif -#ifdef SSL_ST_RENEGOTIATE - S(SSL_ST_RENEGOTIATE), -#endif - { 0, NULL } -}; - -#endif - diff --git a/src/common/util.c b/src/common/util.c index e7059a5bd9..22c816abcc 100644 --- a/src/common/util.c +++ b/src/common/util.c @@ -2157,13 +2157,12 @@ write_chunks_to_file(const char *fname, const smartlist_t *chunks, int bin) return write_chunks_to_file_impl(fname, chunks, flags); } -/** As write_str_to_file, but does not assume a NUL-terminated - * string. Instead, we write <b>len</b> bytes, starting at <b>str</b>. */ -int -write_bytes_to_file(const char *fname, const char *str, size_t len, - int bin) +/** Write <b>len</b> bytes, starting at <b>str</b>, to <b>fname</b> + using the open() flags passed in <b>flags</b>. */ +static int +write_bytes_to_file_impl(const char *fname, const char *str, size_t len, + int flags) { - int flags = OPEN_FLAGS_REPLACE|(bin?O_BINARY:O_TEXT); int r; sized_chunk_t c = { str, len }; smartlist_t *chunks = smartlist_create(); @@ -2173,20 +2172,35 @@ write_bytes_to_file(const char *fname, const char *str, size_t len, return r; } +/** As write_str_to_file, but does not assume a NUL-terminated + * string. Instead, we write <b>len</b> bytes, starting at <b>str</b>. */ +int +write_bytes_to_file(const char *fname, const char *str, size_t len, + int bin) +{ + return write_bytes_to_file_impl(fname, str, len, + OPEN_FLAGS_REPLACE|(bin?O_BINARY:O_TEXT)); +} + /** As write_bytes_to_file, but if the file already exists, append the bytes * to the end of the file instead of overwriting it. */ int append_bytes_to_file(const char *fname, const char *str, size_t len, int bin) { - int flags = OPEN_FLAGS_APPEND|(bin?O_BINARY:O_TEXT); - int r; - sized_chunk_t c = { str, len }; - smartlist_t *chunks = smartlist_create(); - smartlist_add(chunks, &c); - r = write_chunks_to_file_impl(fname, chunks, flags); - smartlist_free(chunks); - return r; + return write_bytes_to_file_impl(fname, str, len, + OPEN_FLAGS_APPEND|(bin?O_BINARY:O_TEXT)); +} + +/** Like write_str_to_file(), but also return -1 if there was a file + already residing in <b>fname</b>. */ +int +write_bytes_to_new_file(const char *fname, const char *str, size_t len, + int bin) +{ + return write_bytes_to_file_impl(fname, str, len, + OPEN_FLAGS_DONT_REPLACE| + (bin?O_BINARY:O_TEXT)); } /** Read the contents of <b>filename</b> into a newly allocated @@ -3179,28 +3193,72 @@ format_helper_exit_status(unsigned char child_state, int saved_errno, /* Maximum number of file descriptors, if we cannot get it via sysconf() */ #define DEFAULT_MAX_FD 256 -/** Terminate process running at PID <b>pid</b>. +/** Terminate the process of <b>process_handle</b>. * Code borrowed from Python's os.kill. */ int -tor_terminate_process(pid_t pid) +tor_terminate_process(process_handle_t *process_handle) { #ifdef MS_WINDOWS - HANDLE handle; - /* If the signal is outside of what GenerateConsoleCtrlEvent can use, - attempt to open and terminate the process. */ - handle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid); - if (!handle) - return -1; + if (tor_get_exit_code(process_handle, 0, NULL) == PROCESS_EXIT_RUNNING) { + HANDLE handle; + /* If the signal is outside of what GenerateConsoleCtrlEvent can use, + attempt to open and terminate the process. */ + handle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, + process_handle->pid.dwProcessId); + if (!handle) + return -1; - if (!TerminateProcess(handle, 0)) - return -1; - else - return 0; + if (!TerminateProcess(handle, 0)) + return -1; + else + return 0; + } #else /* Unix */ - return kill(pid, SIGTERM); + return kill(process_handle->pid, SIGTERM); +#endif + + return -1; +} + +/** Return the Process ID of <b>process_handle</b>. */ +int +tor_process_get_pid(process_handle_t *process_handle) +{ +#ifdef MS_WINDOWS + return (int) process_handle->pid.dwProcessId; +#else + return (int) process_handle->pid; +#endif +} + +#ifdef MS_WINDOWS +HANDLE +tor_process_get_stdout_pipe(process_handle_t *process_handle) +{ + return process_handle->stdout_pipe; +} +#else +FILE * +tor_process_get_stdout_pipe(process_handle_t *process_handle) +{ + return process_handle->stdout_handle; +} +#endif + +static process_handle_t * +process_handle_new(void) +{ + process_handle_t *out = tor_malloc_zero(sizeof(process_handle_t)); + +#ifndef MS_WINDOWS + out->stdout_pipe = -1; + out->stderr_pipe = -1; #endif + + return out; } +/*DOCDOC*/ #define CHILD_STATE_INIT 0 #define CHILD_STATE_PIPE 1 #define CHILD_STATE_MAXFD 2 @@ -3233,14 +3291,20 @@ tor_terminate_process(pid_t pid) */ int tor_spawn_background(const char *const filename, const char **argv, +#ifdef MS_WINDOWS + LPVOID envp, +#else const char **envp, - process_handle_t *process_handle) +#endif + process_handle_t **process_handle_out) { #ifdef MS_WINDOWS HANDLE stdout_pipe_read = NULL; HANDLE stdout_pipe_write = NULL; HANDLE stderr_pipe_read = NULL; HANDLE stderr_pipe_write = NULL; + process_handle_t *process_handle; + int status; STARTUPINFO siStartInfo; BOOL retval = FALSE; @@ -3250,30 +3314,26 @@ tor_spawn_background(const char *const filename, const char **argv, (void)envp; // Unused on Windows - /* process_handle must not be NULL */ - tor_assert(process_handle != NULL); - saAttr.nLength = sizeof(SECURITY_ATTRIBUTES); saAttr.bInheritHandle = TRUE; /* TODO: should we set explicit security attributes? (#2046, comment 5) */ saAttr.lpSecurityDescriptor = NULL; /* Assume failure to start process */ - memset(process_handle, 0, sizeof(process_handle_t)); - process_handle->status = PROCESS_STATUS_ERROR; + status = PROCESS_STATUS_ERROR; /* Set up pipe for stdout */ if (!CreatePipe(&stdout_pipe_read, &stdout_pipe_write, &saAttr, 0)) { log_warn(LD_GENERAL, "Failed to create pipe for stdout communication with child process: %s", format_win32_error(GetLastError())); - return process_handle->status; + return status; } if (!SetHandleInformation(stdout_pipe_read, HANDLE_FLAG_INHERIT, 0)) { log_warn(LD_GENERAL, "Failed to configure pipe for stdout communication with child " "process: %s", format_win32_error(GetLastError())); - return process_handle->status; + return status; } /* Set up pipe for stderr */ @@ -3281,13 +3341,13 @@ tor_spawn_background(const char *const filename, const char **argv, log_warn(LD_GENERAL, "Failed to create pipe for stderr communication with child process: %s", format_win32_error(GetLastError())); - return process_handle->status; + return status; } if (!SetHandleInformation(stderr_pipe_read, HANDLE_FLAG_INHERIT, 0)) { log_warn(LD_GENERAL, "Failed to configure pipe for stderr communication with child " "process: %s", format_win32_error(GetLastError())); - return process_handle->status; + return status; } /* Create the child process */ @@ -3296,6 +3356,9 @@ tor_spawn_background(const char *const filename, const char **argv, */ joined_argv = tor_join_win_cmdline(argv); + process_handle = process_handle_new(); + process_handle->status = status; + ZeroMemory(&(process_handle->pid), sizeof(PROCESS_INFORMATION)); ZeroMemory(&siStartInfo, sizeof(STARTUPINFO)); siStartInfo.cb = sizeof(STARTUPINFO); @@ -3315,7 +3378,7 @@ tor_spawn_background(const char *const filename, const char **argv, /*(TODO: set CREATE_NEW CONSOLE/PROCESS_GROUP to make GetExitCodeProcess() * work?) */ 0, // creation flags - NULL, // use parent's environment + envp, // use parent's environment NULL, // use parent's current directory &siStartInfo, // STARTUPINFO pointer &(process_handle->pid)); // receives PROCESS_INFORMATION @@ -3326,22 +3389,25 @@ tor_spawn_background(const char *const filename, const char **argv, log_warn(LD_GENERAL, "Failed to create child process %s: %s", filename?filename:argv[0], format_win32_error(GetLastError())); + tor_free(process_handle); } else { /* TODO: Close hProcess and hThread in process_handle->pid? */ process_handle->stdout_pipe = stdout_pipe_read; process_handle->stderr_pipe = stderr_pipe_read; - process_handle->status = PROCESS_STATUS_RUNNING; + status = process_handle->status = PROCESS_STATUS_RUNNING; } /* TODO: Close pipes on exit */ - - return process_handle->status; + *process_handle_out = process_handle; + return status; #else // MS_WINDOWS pid_t pid; int stdout_pipe[2]; int stderr_pipe[2]; int fd, retval; ssize_t nbytes; + process_handle_t *process_handle; + int status; const char *error_message = SPAWN_ERROR_MESSAGE; size_t error_message_length; @@ -3354,9 +3420,7 @@ tor_spawn_background(const char *const filename, const char **argv, static int max_fd = -1; - /* Assume failure to start */ - memset(process_handle, 0, sizeof(process_handle_t)); - process_handle->status = PROCESS_STATUS_ERROR; + status = PROCESS_STATUS_ERROR; /* We do the strlen here because strlen() is not signal handler safe, and we are not allowed to use unsafe functions between fork and exec */ @@ -3370,7 +3434,7 @@ tor_spawn_background(const char *const filename, const char **argv, log_warn(LD_GENERAL, "Failed to set up pipe for stdout communication with child process: %s", strerror(errno)); - return process_handle->status; + return status; } retval = pipe(stderr_pipe); @@ -3378,7 +3442,7 @@ tor_spawn_background(const char *const filename, const char **argv, log_warn(LD_GENERAL, "Failed to set up pipe for stderr communication with child process: %s", strerror(errno)); - return process_handle->status; + return status; } child_state = CHILD_STATE_MAXFD; @@ -3468,7 +3532,7 @@ tor_spawn_background(const char *const filename, const char **argv, _exit(255); /* Never reached, but avoids compiler warning */ - return process_handle->status; + return status; } /* In parent */ @@ -3479,9 +3543,11 @@ tor_spawn_background(const char *const filename, const char **argv, close(stdout_pipe[1]); close(stderr_pipe[0]); close(stderr_pipe[1]); - return process_handle->status; + return status; } + process_handle = process_handle_new(); + process_handle->status = status; process_handle->pid = pid; /* TODO: If the child process forked but failed to exec, waitpid it */ @@ -3505,7 +3571,7 @@ tor_spawn_background(const char *const filename, const char **argv, strerror(errno)); } - process_handle->status = PROCESS_STATUS_RUNNING; + status = process_handle->status = PROCESS_STATUS_RUNNING; /* Set stdout/stderr pipes to be non-blocking */ fcntl(process_handle->stdout_pipe, F_SETFL, O_NONBLOCK); fcntl(process_handle->stderr_pipe, F_SETFL, O_NONBLOCK); @@ -3513,10 +3579,52 @@ tor_spawn_background(const char *const filename, const char **argv, process_handle->stdout_handle = fdopen(process_handle->stdout_pipe, "r"); process_handle->stderr_handle = fdopen(process_handle->stderr_pipe, "r"); + *process_handle_out = process_handle; return process_handle->status; #endif // MS_WINDOWS } +/** Destroy all resources allocated by the process handle in + * <b>process_handle</b>. + * If <b>also_terminate_process</b> is true, also terminate the + * process of the process handle. */ +void +tor_process_handle_destroy(process_handle_t *process_handle, + int also_terminate_process) +{ + if (!process_handle) + return; + + if (also_terminate_process) { + if (tor_terminate_process(process_handle) < 0) { + log_notice(LD_GENERAL, "Failed to terminate process with PID '%d'", + tor_process_get_pid(process_handle)); + } else { + log_info(LD_GENERAL, "Terminated process with PID '%d'", + tor_process_get_pid(process_handle)); + } + } + + process_handle->status = PROCESS_STATUS_NOTRUNNING; + +#ifdef MS_WINDOWS + if (process_handle->stdout_pipe) + CloseHandle(process_handle->stdout_pipe); + + if (process_handle->stderr_pipe) + CloseHandle(process_handle->stderr_pipe); +#else + if (process_handle->stdout_handle) + fclose(process_handle->stdout_handle); + + if (process_handle->stderr_handle) + fclose(process_handle->stderr_handle); +#endif + + memset(process_handle, 0x0f, sizeof(process_handle_t)); + tor_free(process_handle); +} + /** Get the exit code of a process specified by <b>process_handle</b> and store * it in <b>exit_code</b>, if set to a non-NULL value. If <b>block</b> is set * to true, the call will block until the process has exited. Otherwise if @@ -3528,7 +3636,7 @@ tor_spawn_background(const char *const filename, const char **argv, * probably not work in Tor, because waitpid() is called in main.c to reap any * terminated child processes.*/ int -tor_get_exit_code(const process_handle_t process_handle, +tor_get_exit_code(const process_handle_t *process_handle, int block, int *exit_code) { #ifdef MS_WINDOWS @@ -3537,14 +3645,14 @@ tor_get_exit_code(const process_handle_t process_handle, if (block) { /* Wait for the process to exit */ - retval = WaitForSingleObject(process_handle.pid.hProcess, INFINITE); + retval = WaitForSingleObject(process_handle->pid.hProcess, INFINITE); if (retval != WAIT_OBJECT_0) { log_warn(LD_GENERAL, "WaitForSingleObject() failed (%d): %s", (int)retval, format_win32_error(GetLastError())); return PROCESS_EXIT_ERROR; } } else { - retval = WaitForSingleObject(process_handle.pid.hProcess, 0); + retval = WaitForSingleObject(process_handle->pid.hProcess, 0); if (WAIT_TIMEOUT == retval) { /* Process has not exited */ return PROCESS_EXIT_RUNNING; @@ -3556,7 +3664,7 @@ tor_get_exit_code(const process_handle_t process_handle, } if (exit_code != NULL) { - success = GetExitCodeProcess(process_handle.pid.hProcess, + success = GetExitCodeProcess(process_handle->pid.hProcess, (PDWORD)exit_code); if (!success) { log_warn(LD_GENERAL, "GetExitCodeProcess() failed: %s", @@ -3568,19 +3676,19 @@ tor_get_exit_code(const process_handle_t process_handle, int stat_loc; int retval; - retval = waitpid(process_handle.pid, &stat_loc, block?0:WNOHANG); + retval = waitpid(process_handle->pid, &stat_loc, block?0:WNOHANG); if (!block && 0 == retval) { /* Process has not exited */ return PROCESS_EXIT_RUNNING; - } else if (retval != process_handle.pid) { - log_warn(LD_GENERAL, "waitpid() failed for PID %d: %s", process_handle.pid, - strerror(errno)); + } else if (retval != process_handle->pid) { + log_warn(LD_GENERAL, "waitpid() failed for PID %d: %s", + process_handle->pid, strerror(errno)); return PROCESS_EXIT_ERROR; } if (!WIFEXITED(stat_loc)) { log_warn(LD_GENERAL, "Process %d did not exit normally", - process_handle.pid); + process_handle->pid); return PROCESS_EXIT_ERROR; } @@ -3682,7 +3790,6 @@ tor_read_all_handle(FILE *h, char *buf, size_t count, if (NULL == retval) { if (feof(h)) { log_debug(LD_GENERAL, "fgets() reached end of file"); - fclose(h); if (eof) *eof = 1; break; @@ -3695,7 +3802,6 @@ tor_read_all_handle(FILE *h, char *buf, size_t count, } else { log_warn(LD_GENERAL, "fgets() from handle failed: %s", strerror(errno)); - fclose(h); return -1; } } @@ -3855,12 +3961,10 @@ log_from_pipe(FILE *stream, int severity, const char *executable, r = get_string_from_pipe(stream, buf, sizeof(buf) - 1); if (r == IO_STREAM_CLOSED) { - fclose(stream); return 1; } else if (r == IO_STREAM_EAGAIN) { return 0; } else if (r == IO_STREAM_TERM) { - fclose(stream); return -1; } @@ -3968,7 +4072,7 @@ tor_check_port_forwarding(const char *filename, int dir_port, int or_port, /* Static variables are initialized to zero, so child_handle.status=0 * which corresponds to it not running on startup */ - static process_handle_t child_handle; + static process_handle_t *child_handle=NULL; static time_t time_to_run_helper = 0; int stdout_status, stderr_status, retval; @@ -3994,46 +4098,50 @@ tor_check_port_forwarding(const char *filename, int dir_port, int or_port, argv[9] = NULL; /* Start the child, if it is not already running */ - if (child_handle.status != PROCESS_STATUS_RUNNING && + if ((!child_handle || child_handle->status != PROCESS_STATUS_RUNNING) && time_to_run_helper < now) { + int status; + /* Assume tor-fw-helper will succeed, start it later*/ time_to_run_helper = now + TIME_TO_EXEC_FWHELPER_SUCCESS; + if (child_handle) { + tor_process_handle_destroy(child_handle, 1); + child_handle = NULL; + } + #ifdef MS_WINDOWS /* Passing NULL as lpApplicationName makes Windows search for the .exe */ - tor_spawn_background(NULL, argv, NULL, &child_handle); + status = tor_spawn_background(NULL, argv, NULL, &child_handle); #else - tor_spawn_background(filename, argv, NULL, &child_handle); + status = tor_spawn_background(filename, argv, NULL, &child_handle); #endif - if (PROCESS_STATUS_ERROR == child_handle.status) { + + if (PROCESS_STATUS_ERROR == status) { log_warn(LD_GENERAL, "Failed to start port forwarding helper %s", filename); time_to_run_helper = now + TIME_TO_EXEC_FWHELPER_FAIL; return; } -#ifdef MS_WINDOWS - log_info(LD_GENERAL, - "Started port forwarding helper (%s)", filename); -#else + log_info(LD_GENERAL, - "Started port forwarding helper (%s) with pid %d", filename, - child_handle.pid); -#endif + "Started port forwarding helper (%s) with pid '%d'", + filename, tor_process_get_pid(child_handle)); } /* If child is running, read from its stdout and stderr) */ - if (PROCESS_STATUS_RUNNING == child_handle.status) { + if (child_handle && PROCESS_STATUS_RUNNING == child_handle->status) { /* Read from stdout/stderr and log result */ retval = 0; #ifdef MS_WINDOWS - stdout_status = log_from_handle(child_handle.stdout_pipe, LOG_INFO); - stderr_status = log_from_handle(child_handle.stderr_pipe, LOG_WARN); + stdout_status = log_from_handle(child_handle->stdout_pipe, LOG_INFO); + stderr_status = log_from_handle(child_handle->stderr_pipe, LOG_WARN); /* If we got this far (on Windows), the process started */ retval = 0; #else - stdout_status = log_from_pipe(child_handle.stdout_handle, + stdout_status = log_from_pipe(child_handle->stdout_handle, LOG_INFO, filename, &retval); - stderr_status = log_from_pipe(child_handle.stderr_handle, + stderr_status = log_from_pipe(child_handle->stderr_handle, LOG_WARN, filename, &retval); #endif if (retval) { @@ -4046,7 +4154,7 @@ tor_check_port_forwarding(const char *filename, int dir_port, int or_port, /* There was a failure */ retval = -1; #ifdef MS_WINDOWS - else if (tor_get_exit_code(child_handle, 0, NULL) != + else if (!child_handle || tor_get_exit_code(child_handle, 0, NULL) != PROCESS_EXIT_RUNNING) { /* process has exited or there was an error */ /* TODO: Do something with the process return value */ @@ -4069,10 +4177,10 @@ tor_check_port_forwarding(const char *filename, int dir_port, int or_port, if (0 != retval) { if (1 == retval) { log_info(LD_GENERAL, "Port forwarding helper terminated"); - child_handle.status = PROCESS_STATUS_NOTRUNNING; + child_handle->status = PROCESS_STATUS_NOTRUNNING; } else { log_warn(LD_GENERAL, "Failed to read from port forwarding helper"); - child_handle.status = PROCESS_STATUS_ERROR; + child_handle->status = PROCESS_STATUS_ERROR; } /* TODO: The child might not actually be finished (maybe it failed or diff --git a/src/common/util.h b/src/common/util.h index bd471e3816..cbc56d0816 100644 --- a/src/common/util.h +++ b/src/common/util.h @@ -173,19 +173,15 @@ int n_bits_set_u8(uint8_t v); #define HEX_CHARACTERS "0123456789ABCDEFabcdef" void tor_strlower(char *s) ATTR_NONNULL((1)); void tor_strupper(char *s) ATTR_NONNULL((1)); -int tor_strisprint(const char *s) ATTR_PURE ATTR_NONNULL((1)); -int tor_strisnonupper(const char *s) ATTR_PURE ATTR_NONNULL((1)); -int strcmp_opt(const char *s1, const char *s2) ATTR_PURE; -int strcmpstart(const char *s1, const char *s2) ATTR_PURE ATTR_NONNULL((1,2)); -int strcmp_len(const char *s1, const char *s2, size_t len) - ATTR_PURE ATTR_NONNULL((1,2)); -int strcasecmpstart(const char *s1, const char *s2) - ATTR_PURE ATTR_NONNULL((1,2)); -int strcmpend(const char *s1, const char *s2) ATTR_PURE ATTR_NONNULL((1,2)); -int strcasecmpend(const char *s1, const char *s2) - ATTR_PURE ATTR_NONNULL((1,2)); -int fast_memcmpstart(const void *mem, size_t memlen, - const char *prefix) ATTR_PURE; +int tor_strisprint(const char *s) ATTR_NONNULL((1)); +int tor_strisnonupper(const char *s) ATTR_NONNULL((1)); +int strcmp_opt(const char *s1, const char *s2); +int strcmpstart(const char *s1, const char *s2) ATTR_NONNULL((1,2)); +int strcmp_len(const char *s1, const char *s2, size_t len) ATTR_NONNULL((1,2)); +int strcasecmpstart(const char *s1, const char *s2) ATTR_NONNULL((1,2)); +int strcmpend(const char *s1, const char *s2) ATTR_NONNULL((1,2)); +int strcasecmpend(const char *s1, const char *s2) ATTR_NONNULL((1,2)); +int fast_memcmpstart(const void *mem, size_t memlen, const char *prefix); void tor_strstrip(char *s, const char *strip) ATTR_NONNULL((1,2)); long tor_parse_long(const char *s, int base, long min, @@ -197,19 +193,19 @@ double tor_parse_double(const char *s, double min, double max, int *ok, uint64_t tor_parse_uint64(const char *s, int base, uint64_t min, uint64_t max, int *ok, char **next); const char *hex_str(const char *from, size_t fromlen) ATTR_NONNULL((1)); -const char *eat_whitespace(const char *s) ATTR_PURE; -const char *eat_whitespace_eos(const char *s, const char *eos) ATTR_PURE; -const char *eat_whitespace_no_nl(const char *s) ATTR_PURE; -const char *eat_whitespace_eos_no_nl(const char *s, const char *eos) ATTR_PURE; -const char *find_whitespace(const char *s) ATTR_PURE; -const char *find_whitespace_eos(const char *s, const char *eos) ATTR_PURE; -const char *find_str_at_start_of_line(const char *haystack, const char *needle) - ATTR_PURE; +const char *eat_whitespace(const char *s); +const char *eat_whitespace_eos(const char *s, const char *eos); +const char *eat_whitespace_no_nl(const char *s); +const char *eat_whitespace_eos_no_nl(const char *s, const char *eos); +const char *find_whitespace(const char *s); +const char *find_whitespace_eos(const char *s, const char *eos); +const char *find_str_at_start_of_line(const char *haystack, + const char *needle); int string_is_C_identifier(const char *string); -int tor_mem_is_zero(const char *mem, size_t len) ATTR_PURE; -int tor_digest_is_zero(const char *digest) ATTR_PURE; -int tor_digest256_is_zero(const char *digest) ATTR_PURE; +int tor_mem_is_zero(const char *mem, size_t len); +int tor_digest_is_zero(const char *digest); +int tor_digest256_is_zero(const char *digest); char *esc_for_log(const char *string) ATTR_MALLOC; const char *escaped(const char *string); struct smartlist_t; @@ -318,6 +314,7 @@ int check_private_dir(const char *dirname, cpd_check_t check, const char *effective_user); #define OPEN_FLAGS_REPLACE (O_WRONLY|O_CREAT|O_TRUNC) #define OPEN_FLAGS_APPEND (O_WRONLY|O_CREAT|O_APPEND) +#define OPEN_FLAGS_DONT_REPLACE (O_CREAT|O_EXCL|O_APPEND|O_WRONLY) typedef struct open_file_t open_file_t; int start_writing_to_file(const char *fname, int open_flags, int mode, open_file_t **data_out); @@ -339,6 +336,8 @@ int write_chunks_to_file(const char *fname, const struct smartlist_t *chunks, int bin); int append_bytes_to_file(const char *fname, const char *str, size_t len, int bin); +int write_bytes_to_new_file(const char *fname, const char *str, size_t len, + int bin); /** Flag for read_file_to_str: open the file in binary mode. */ #define RFTS_BIN 1 @@ -352,7 +351,7 @@ const char *parse_config_line_from_str(const char *line, char **key_out, char **value_out); char *expand_filename(const char *filename); struct smartlist_t *tor_listdir(const char *dirname); -int path_is_relative(const char *filename) ATTR_PURE; +int path_is_relative(const char *filename); /* Process helpers */ void start_daemon(void); @@ -363,10 +362,14 @@ void write_pidfile(char *filename); void tor_check_port_forwarding(const char *filename, int dir_port, int or_port, time_t now); -int tor_terminate_process(pid_t pid); -typedef struct process_handle_s process_handle_t; +typedef struct process_handle_t process_handle_t; int tor_spawn_background(const char *const filename, const char **argv, - const char **envp, process_handle_t *process_handle); +#ifdef MS_WINDOWS + LPVOID envp, +#else + const char **envp, +#endif + process_handle_t **process_handle_out); #define SPAWN_ERROR_MESSAGE "ERR: Failed to spawn background process - code " @@ -374,16 +377,16 @@ int tor_spawn_background(const char *const filename, const char **argv, HANDLE load_windows_system_library(const TCHAR *library_name); #endif -#ifdef UTIL_PRIVATE -/* Prototypes for private functions only used by util.c (and unit tests) */ - /* Values of process_handle_t.status. PROCESS_STATUS_NOTRUNNING must be * 0 because tor_check_port_forwarding depends on this being the initial * statue of the static instance of process_handle_t */ #define PROCESS_STATUS_NOTRUNNING 0 #define PROCESS_STATUS_RUNNING 1 #define PROCESS_STATUS_ERROR -1 -struct process_handle_s { + +#ifdef UTIL_PRIVATE +/*DOCDOC*/ +struct process_handle_t { int status; #ifdef MS_WINDOWS HANDLE stdout_pipe; @@ -397,12 +400,13 @@ struct process_handle_s { pid_t pid; #endif // MS_WINDOWS }; +#endif /* Return values of tor_get_exit_code() */ #define PROCESS_EXIT_RUNNING 1 #define PROCESS_EXIT_EXITED 0 #define PROCESS_EXIT_ERROR -1 -int tor_get_exit_code(const process_handle_t process_handle, +int tor_get_exit_code(const process_handle_t *process_handle, int block, int *exit_code); int tor_split_lines(struct smartlist_t *sl, char *buf, int len); #ifdef MS_WINDOWS @@ -419,6 +423,20 @@ ssize_t tor_read_all_from_process_stderr( const process_handle_t *process_handle, char *buf, size_t count); char *tor_join_win_cmdline(const char *argv[]); +int tor_process_get_pid(process_handle_t *process_handle); +#ifdef MS_WINDOWS +HANDLE tor_process_get_stdout_pipe(process_handle_t *process_handle); +#else +FILE *tor_process_get_stdout_pipe(process_handle_t *process_handle); +#endif + +int tor_terminate_process(process_handle_t *process_handle); +void tor_process_handle_destroy(process_handle_t *process_handle, + int also_terminate_process); + +#ifdef UTIL_PRIVATE +/* Prototypes for private functions only used by util.c (and unit tests) */ + void format_helper_exit_status(unsigned char child_state, int saved_errno, char *hex_errno); |