diff options
Diffstat (limited to 'src/common')
-rw-r--r-- | src/common/Makefile.am | 49 | ||||
-rw-r--r-- | src/common/Makefile.nmake | 20 | ||||
-rw-r--r-- | src/common/address.c | 316 | ||||
-rw-r--r-- | src/common/address.h | 40 | ||||
-rw-r--r-- | src/common/aes.c | 1253 | ||||
-rw-r--r-- | src/common/aes.h | 11 | ||||
-rw-r--r-- | src/common/compat.c | 521 | ||||
-rw-r--r-- | src/common/compat.h | 63 | ||||
-rw-r--r-- | src/common/compat_libevent.c | 137 | ||||
-rw-r--r-- | src/common/compat_libevent.h | 22 | ||||
-rw-r--r-- | src/common/container.c | 11 | ||||
-rw-r--r-- | src/common/container.h | 30 | ||||
-rw-r--r-- | src/common/crypto.c | 869 | ||||
-rw-r--r-- | src/common/crypto.h | 173 | ||||
-rw-r--r-- | src/common/ht.h | 99 | ||||
-rw-r--r-- | src/common/log.c | 37 | ||||
-rw-r--r-- | src/common/mempool.c | 3 | ||||
-rw-r--r-- | src/common/procmon.c | 10 | ||||
-rw-r--r-- | src/common/torgzip.c | 4 | ||||
-rw-r--r-- | src/common/torint.h | 9 | ||||
-rw-r--r-- | src/common/torlog.h | 5 | ||||
-rw-r--r-- | src/common/tortls.c | 1096 | ||||
-rw-r--r-- | src/common/tortls.h | 50 | ||||
-rw-r--r-- | src/common/tortls_states.h | 414 | ||||
-rw-r--r-- | src/common/util.c | 1620 | ||||
-rw-r--r-- | src/common/util.h | 175 |
26 files changed, 4733 insertions, 2304 deletions
diff --git a/src/common/Makefile.am b/src/common/Makefile.am index 6da1782c8..5e7684259 100644 --- a/src/common/Makefile.am +++ b/src/common/Makefile.am @@ -1,7 +1,7 @@ noinst_LIBRARIES = libor.a libor-crypto.a libor-event.a -EXTRA_DIST = common_sha1.i sha256.c +EXTRA_DIST = common_sha1.i sha256.c Makefile.nmake #CFLAGS = -Wall -Wpointer-arith -O2 @@ -11,18 +11,53 @@ else libor_extra_source= endif -libor_a_SOURCES = address.c log.c util.c compat.c container.c mempool.c \ - memarea.c di_ops.c procmon.c util_codedigest.c $(libor_extra_source) -libor_crypto_a_SOURCES = crypto.c aes.c tortls.c torgzip.c +libor_a_SOURCES = \ + address.c \ + compat.c \ + container.c \ + di_ops.c \ + log.c \ + memarea.c \ + mempool.c \ + procmon.c \ + util.c \ + util_codedigest.c \ + $(libor_extra_source) + +libor_crypto_a_SOURCES = \ + aes.c \ + crypto.c \ + torgzip.c \ + tortls.c + libor_event_a_SOURCES = compat_libevent.c -noinst_HEADERS = address.h torlog.h crypto.h util.h compat.h aes.h torint.h tortls.h strlcpy.c strlcat.c torgzip.h container.h ht.h mempool.h memarea.h ciphers.inc compat_libevent.h tortls_states.h di_ops.h procmon.h +noinst_HEADERS = \ + address.h \ + aes.h \ + ciphers.inc \ + compat.h \ + compat_libevent.h \ + container.h \ + crypto.h \ + di_ops.h \ + ht.h \ + memarea.h \ + mempool.h \ + procmon.h \ + strlcat.c \ + strlcpy.c \ + torgzip.h \ + torint.h \ + torlog.h \ + tortls.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/Makefile.nmake b/src/common/Makefile.nmake new file mode 100644 index 000000000..e54827367 --- /dev/null +++ b/src/common/Makefile.nmake @@ -0,0 +1,20 @@ +all: libor.lib libor-crypto.lib libor-event.lib + +CFLAGS = /I ..\win32 /I ..\..\..\build-alpha\include + +LIBOR_OBJECTS = address.obj compat.obj container.obj di_ops.obj \ + log.obj memarea.obj mempool.obj procmon.obj util.obj \ + util_codedigest.obj + +LIBOR_CRYPTO_OBJECTS = aes.obj crypto.obj torgzip.obj tortls.obj + +LIBOR_EVENT_OBJECTS = compat_libevent.obj + +libor.lib: $(LIBOR_OBJECTS) + lib $(LIBOR_OBJECTS) /out:libor.lib + +libor-crypto.lib: $(LIBOR_CRYPTO_OBJECTS) + lib $(LIBOR_CRYPTO_OBJECTS) /out:libor-crypto.lib + +libor-event.lib: $(LIBOR_EVENT_OBJECTS) + lib $(LIBOR_EVENT_OBJECTS) /out:libor-event.lib diff --git a/src/common/address.c b/src/common/address.c index 17bdea923..e444ef193 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 +#ifdef _WIN32 #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,12 +52,28 @@ #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> #include <string.h> #include <assert.h> +/* tor_addr_is_null() and maybe other functions rely on AF_UNSPEC being 0 to + * work correctly. Bail out here if we've found a platform where AF_UNSPEC + * isn't 0. */ +#if AF_UNSPEC != 0 +#error We rely on AF_UNSPEC being 0. Let us know about your platform, please! +#endif + /** Convert the tor_addr_t in <b>a</b>, with port in <b>port</b>, into a * sockaddr object in *<b>sa_out</b> of object size <b>len</b>. If not enough * room is available in sa_out, or on error, return 0. On success, return @@ -253,7 +275,7 @@ tor_addr_lookup(const char *name, uint16_t family, tor_addr_t *addr) ent = err ? NULL : &hent; #else ent = gethostbyname(name); -#ifdef MS_WINDOWS +#ifdef _WIN32 err = WSAGetLastError(); #else err = h_errno; @@ -269,7 +291,7 @@ tor_addr_lookup(const char *name, uint16_t family, tor_addr_t *addr) } return 0; } -#ifdef MS_WINDOWS +#ifdef _WIN32 return (err == WSATRY_AGAIN) ? 1 : -1; #else return (err == TRY_AGAIN) ? 1 : -1; @@ -333,7 +355,9 @@ tor_addr_is_internal(const tor_addr_t *addr, int for_listening) /* unknown address family... assume it's not safe for external use */ /* rather than tor_assert(0) */ - log_warn(LD_BUG, "tor_addr_is_internal() called with a non-IP address."); + log_warn(LD_BUG, "tor_addr_is_internal() called with a non-IP address of " + "type %d", (int)v_family); + tor_fragile_assert(); return 1; } @@ -343,22 +367,28 @@ tor_addr_is_internal(const tor_addr_t *addr, int for_listening) * brackets. */ const char * -tor_addr_to_str(char *dest, const tor_addr_t *addr, int len, int decorate) +tor_addr_to_str(char *dest, const tor_addr_t *addr, size_t len, int decorate) { const char *ptr; tor_assert(addr && dest); switch (tor_addr_family(addr)) { case AF_INET: - if (len<3) + /* Shortest addr x.x.x.x + \0 */ + if (len < 8) return NULL; - ptr = tor_inet_ntop(AF_INET, &addr->addr.in_addr, dest, len); + ptr = tor_inet_ntop(AF_INET, &addr->addr.in_addr, dest, len); break; case AF_INET6: + /* Shortest addr [ :: ] + \0 */ + if (len < (3 + (decorate ? 2 : 0))) + return NULL; + if (decorate) ptr = tor_inet_ntop(AF_INET6, &addr->addr.in6_addr, dest+1, len-2); else ptr = tor_inet_ntop(AF_INET6, &addr->addr.in6_addr, dest, len); + if (ptr && decorate) { *dest = '['; memcpy(dest+strlen(dest), "]", 2); @@ -384,7 +414,7 @@ tor_addr_to_str(char *dest, const tor_addr_t *addr, int len, int decorate) * IPv4 or IPv6 address too. */ int -tor_addr_parse_reverse_lookup_name(tor_addr_t *result, const char *address, +tor_addr_parse_PTR_name(tor_addr_t *result, const char *address, int family, int accept_regular) { if (!strcasecmpend(address, ".in-addr.arpa")) { @@ -455,7 +485,7 @@ tor_addr_parse_reverse_lookup_name(tor_addr_t *result, const char *address, if (accept_regular) { tor_addr_t tmp; - int r = tor_addr_from_str(&tmp, address); + int r = tor_addr_parse(&tmp, address); if (r < 0) return 0; if (r != family && family != AF_UNSPEC) @@ -470,13 +500,17 @@ tor_addr_parse_reverse_lookup_name(tor_addr_t *result, const char *address, return 0; } -/** Convert <b>addr</b> to an in-addr.arpa name or a .ip6.arpa name, and store - * the result in the <b>outlen</b>-byte buffer at <b>out</b>. Return 0 on - * success, -1 on failure. */ +/** Convert <b>addr</b> to an in-addr.arpa name or a .ip6.arpa name, + * and store the result in the <b>outlen</b>-byte buffer at + * <b>out</b>. Return the number of chars written to <b>out</b>, not + * including the trailing \0, on success. Returns -1 on failure. */ int -tor_addr_to_reverse_lookup_name(char *out, size_t outlen, - const tor_addr_t *addr) +tor_addr_to_PTR_name(char *out, size_t outlen, + const tor_addr_t *addr) { + tor_assert(out); + tor_assert(addr); + if (addr->family == AF_INET) { uint32_t a = tor_addr_to_ipv4h(addr); @@ -499,7 +533,7 @@ tor_addr_to_reverse_lookup_name(char *out, size_t outlen, *cp++ = '.'; } memcpy(cp, "ip6.arpa", 9); /* 8 characters plus NUL */ - return 0; + return 32 * 2 + 8; } return -1; } @@ -952,21 +986,39 @@ tor_dup_addr(const tor_addr_t *addr) } } -/** Return a string representing the address <b>addr</b>. This string is - * statically allocated, and must not be freed. Each call to - * <b>fmt_addr</b> invalidates the last result of the function. This - * function is not thread-safe. */ +/** Return a string representing the address <b>addr</b>. This string + * is statically allocated, and must not be freed. Each call to + * <b>fmt_addr_impl</b> invalidates the last result of the function. + * This function is not thread-safe. If <b>decorate</b> is set, add + * brackets to IPv6 addresses. + * + * It's better to use the wrapper macros of this function: + * <b>fmt_addr()</b> and <b>fmt_and_decorate_addr()</b>. + */ const char * -fmt_addr(const tor_addr_t *addr) +fmt_addr_impl(const tor_addr_t *addr, int decorate) { static char buf[TOR_ADDR_BUF_LEN]; if (!addr) return "<null>"; - if (tor_addr_to_str(buf, addr, sizeof(buf), 0)) + if (tor_addr_to_str(buf, addr, sizeof(buf), decorate)) return buf; else return "???"; } +/** Like fmt_addr(), but takes <b>addr</b> as a host-order IPv4 + * addresses. Also not thread-safe, also clobbers its return buffer on + * repeated calls. */ +const char * +fmt_addr32(uint32_t addr) +{ + static char buf[INET_NTOA_BUF_LEN]; + struct in_addr in; + in.s_addr = htonl(addr); + tor_inet_ntoa(&in, buf, sizeof(buf)); + return buf; +} + /** Convert the string in <b>src</b> to a tor_addr_t <b>addr</b>. The string * may be an IPv4 address, an IPv6 address, or an IPv6 address surrounded by * square brackets. @@ -974,7 +1026,7 @@ fmt_addr(const tor_addr_t *addr) * Return an address family on success, or -1 if an invalid address string is * provided. */ int -tor_addr_from_str(tor_addr_t *addr, const char *src) +tor_addr_parse(tor_addr_t *addr, const char *src) { char *tmp = NULL; /* Holds substring if we got a dotted quad. */ int result; @@ -1002,7 +1054,7 @@ tor_addr_from_str(tor_addr_t *addr, const char *src) * address as needed, and put the result in <b>addr_out</b> and (optionally) * <b>port_out</b>. Return 0 on success, negative on failure. */ int -tor_addr_port_parse(const char *s, tor_addr_t *addr_out, uint16_t *port_out) +tor_addr_port_lookup(const char *s, tor_addr_t *addr_out, uint16_t *port_out) { const char *port; tor_addr_t addr; @@ -1056,6 +1108,169 @@ tor_addr_port_parse(const char *s, tor_addr_t *addr_out, uint16_t *port_out) return -1; } +#ifdef _WIN32 +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_new(); + 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(_WIN32) + /* 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_new(); + 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_new(); + 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. @@ -1063,12 +1278,38 @@ tor_addr_port_parse(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 @@ -1138,6 +1379,20 @@ is_internal_IP(uint32_t ip, int for_listening) return tor_addr_is_internal(&myaddr, for_listening); } +/** Given an address of the form "host:port", try to divide it into its host + * ane port portions, setting *<b>address_out</b> to a newly allocated string + * holding the address portion and *<b>port_out</b> to the port (or 0 if no + * port is given). Return 0 on success, -1 on failure. */ +int +tor_addr_port_split(int severity, const char *addrport, + char **address_out, uint16_t *port_out) +{ + tor_assert(addrport); + tor_assert(address_out); + tor_assert(port_out); + return addr_port_lookup(severity, addrport, address_out, NULL, port_out); +} + /** Parse a string of the form "host[:port]" from <b>addrport</b>. If * <b>address</b> is provided, set *<b>address</b> to a copy of the * host portion of the string. If <b>addr</b> is provided, try to @@ -1149,7 +1404,7 @@ is_internal_IP(uint32_t ip, int for_listening) * Return 0 on success, -1 on failure. */ int -parse_addr_port(int severity, const char *addrport, char **address, +addr_port_lookup(int severity, const char *addrport, char **address, uint32_t *addr, uint16_t *port_out) { const char *colon; @@ -1159,7 +1414,7 @@ parse_addr_port(int severity, const char *addrport, char **address, tor_assert(addrport); - colon = strchr(addrport, ':'); + colon = strrchr(addrport, ':'); if (colon) { _address = tor_strndup(addrport, colon-addrport); _port = (int) tor_parse_long(colon+1,10,1,65535,NULL,NULL); @@ -1432,3 +1687,12 @@ get_interface_address(int severity, uint32_t *addr) return r; } +/** Return true if we can tell that <b>name</b> is a canonical name for the + * loopback address. */ +int +tor_addr_hostname_is_local(const char *name) +{ + return !strcasecmp(name, "localhost") || + !strcasecmp(name, "local") || + !strcasecmpend(name, ".local"); +} diff --git a/src/common/address.h b/src/common/address.h index 9a7656f69..2afec564b 100644 --- a/src/common/address.h +++ b/src/common/address.h @@ -31,6 +31,15 @@ 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; + +#define TOR_ADDR_NULL {AF_UNSPEC, {{0}}}; + 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); @@ -126,7 +135,15 @@ tor_addr_eq_ipv4h(const tor_addr_t *a, uint32_t u) int tor_addr_lookup(const char *name, uint16_t family, tor_addr_t *addr_out); char *tor_dup_addr(const tor_addr_t *addr) ATTR_MALLOC; -const char *fmt_addr(const tor_addr_t *addr); + +/** Wrapper function of fmt_addr_impl(). It does not decorate IPv6 + * addresses. */ +#define fmt_addr(a) fmt_addr_impl((a), 0) +/** Wrapper function of fmt_addr_impl(). It decorates IPv6 + * addresses. */ +#define fmt_and_decorate_addr(a) fmt_addr_impl((a), 1) +const char *fmt_addr_impl(const tor_addr_t *addr, int decorate); +const char * fmt_addr32(uint32_t addr); int get_interface_address6(int severity, sa_family_t family, tor_addr_t *addr); /** Flag to specify how to do a comparison between addresses. In an "exact" @@ -148,24 +165,24 @@ 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. */ #define REVERSE_LOOKUP_NAME_BUF_LEN 73 -int tor_addr_to_reverse_lookup_name(char *out, size_t outlen, +int tor_addr_to_PTR_name(char *out, size_t outlen, const tor_addr_t *addr); -int tor_addr_parse_reverse_lookup_name(tor_addr_t *result, const char *address, +int tor_addr_parse_PTR_name(tor_addr_t *result, const char *address, int family, int accept_regular); -int tor_addr_port_parse(const char *s, tor_addr_t *addr_out, +int tor_addr_port_lookup(const char *s, tor_addr_t *addr_out, uint16_t *port_out); int tor_addr_parse_mask_ports(const char *s, tor_addr_t *addr_out, maskbits_t *mask_out, uint16_t *port_min_out, uint16_t *port_max_out); -const char * tor_addr_to_str(char *dest, const tor_addr_t *addr, int len, +const char * tor_addr_to_str(char *dest, const tor_addr_t *addr, size_t len, int decorate); -int tor_addr_from_str(tor_addr_t *addr, const char *src); +int tor_addr_parse(tor_addr_t *addr, const char *src); void tor_addr_copy(tor_addr_t *dest, const tor_addr_t *src); void tor_addr_from_ipv4n(tor_addr_t *dest, uint32_t v4addr); /** Set <b>dest</b> to the IPv4 address encoded in <b>v4addr</b> in host @@ -180,9 +197,14 @@ void tor_addr_from_in6(tor_addr_t *dest, const struct in6_addr *in6); int tor_addr_is_null(const tor_addr_t *addr); int tor_addr_is_loopback(const tor_addr_t *addr); +int tor_addr_port_split(int severity, const char *addrport, + char **address_out, uint16_t *port_out); + +int tor_addr_hostname_is_local(const char *name); + /* IPv4 helpers */ -int is_internal_IP(uint32_t ip, int for_listening) ATTR_PURE; -int parse_addr_port(int severity, const char *addrport, char **address, +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, uint16_t *port_max_out); diff --git a/src/common/aes.c b/src/common/aes.c index c2fdeb594..1cb6b86a8 100644 --- a/src/common/aes.c +++ b/src/common/aes.c @@ -6,132 +6,144 @@ /** * \file aes.c - * \brief Implements the AES cipher (with 128-bit keys and blocks), - * and a counter-mode stream cipher on top of AES. This code is - * taken from the main Rijndael distribution. (We include this - * because many people are running older versions of OpenSSL without - * AES support.) + * \brief Implements a counter-mode stream cipher on top of AES. **/ #include "orconfig.h" + +#ifdef _WIN32 /*wrkard for dtls1.h >= 0.9.8m of "#include <winsock.h>"*/ + #ifndef _WIN32_WINNT + #define _WIN32_WINNT 0x0501 + #endif + #define WIN32_LEAN_AND_MEAN + #if defined(_MSC_VER) && (_MSC_VER < 1300) + #include <winsock.h> + #else + #include <winsock2.h> + #include <ws2tcpip.h> + #endif +#endif + #include <openssl/opensslv.h> #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 3 strategies for getting AES: Via OpenSSL's AES_encrypt function, - * via OpenSSL's EVP_EncryptUpdate function, or via the built-in AES - * implementation below. */ - -/** 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 -/** Defined iff we're using Tor's internal AES implementation, defined - * below. */ -#undef USE_BUILTIN_AES +#ifdef ANDROID +/* Android's OpenSSL seems to have removed all of its Engine support. */ +#define DISABLE_ENGINES +#endif -/* Figure out our CPU type. We use this to pick an AES implementation. - * Macros are as listed at http://predef.sourceforge.net/prearch.html +/* We have five strategies for implementing AES counter mode. + * + * Best with x86 and x86_64: Use EVP_aes_ctr128() and EVP_EncryptUpdate(). + * This is possible with OpenSSL 1.0.1, where the counter-mode implementation + * can use bit-sliced or vectorized AES or AESNI as appropriate. + * + * Otherwise: Pick the best possible AES block implementation that OpenSSL + * gives us, and the best possible counter-mode implementation, and combine + * them. */ -#if (defined(i386) || defined(__i386__) || defined(__i386) || defined(_X86_) \ - || defined(_M_IX86) || defined(__THW_INTEL__) || defined(__I86__)) -# define CPU_IS_X86 -#elif (defined(__amd64__) || defined(__amd64) || \ - defined(__x86_64__) || defined(__x86_64) || \ - defined(_M_X64)) -# define CPU_IS_X86_64 -#elif (defined(__ia64__) || defined(__ia64) || defined(_IA64) || \ - defined(_M_IA64)) -# define CPU_IS_IA64 -#elif (defined(__sparc__) || defined(__sparc)) -# define CPU_IS_SPARC -#elif (defined(__arm__) || defined (__TARGET_ARCH_ARM)) -# define CPU_IS_ARM -#endif +#if OPENSSL_VERSION_NUMBER >= OPENSSL_V_NOPATCH(1,0,1) && \ + (defined(__i386) || defined(__i386__) || defined(_M_IX86) || \ + defined(__x86_64) || defined(__x86_64__) || \ + defined(_M_AMD64) || defined(_M_X64) || defined(__INTEL__)) \ -/* Here we pick which to use, if none is force-defined. See - * http://archives.seul.org/or/dev/Feb-2007/msg00045.html - * for a summary of the most recent benchmarking results that led to this - * nutty decision tree. -*/ -#if (!defined(USE_BUILTIN_AES) && \ - !defined(USE_OPENSSL_AES) && \ - !defined(USE_OPENSSL_EVP)) +#define USE_EVP_AES_CTR -/* OpenSSL 0.9.7 was the first to support AES. It was slower than our - * built-in implementation. - * OpenSSL 0.9.8 added assembly implementations for i386 and ia64. - * Either the i386 stuff isn't used for x86-64, or it isn't faster. - * OpenSSL 0.9.9 (not yet out) has added assembly implementations for - * x86_64 (aka amd64), sparc9, and arm +#endif + +/* We have 2 strategies for getting the AES block cipher: Via OpenSSL's + * AES_encrypt function, or via OpenSSL's EVP_EncryptUpdate function. * - * Note: the "f" at the end of OpenSSL version numbers below means - * "release". */ -# if defined(CPU_IS_X86) || defined(CPU_IS_IA64) -# if OPENSSL_VERSION_NUMBER >= 0x0090800fL -# define USE_OPENSSL_AES -# endif -# endif + * 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. + */ -# if defined(CPU_IS_X86_64) || defined(CPU_IS_ARM) || defined(CPU_IS_SPARC) -# if OPENSSL_VERSION_NUMBER >= 0x0090900fL -# define USE_OPENSSL_AES -# endif -# endif +/* We have 2 strategies for getting a plug-in 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.) + */ -/* Otherwise, use the built-in implementation below. */ -# ifndef USE_OPENSSL_AES -# define USE_BUILTIN_AES -# endif -#endif /* endif need to pick a method */ +#ifdef USE_EVP_AES_CTR -/* Include OpenSSL headers as needed. */ -#ifdef USE_OPENSSL_AES -# include <openssl/aes.h> -#endif -#ifdef USE_OPENSSL_EVP -# include <openssl/evp.h> -#endif +struct aes_cnt_cipher { + EVP_CIPHER_CTX evp; +}; -/* Figure out which AES optimizations to use. */ -#ifdef USE_BUILTIN_AES -/** If this is defined, we take advantage of the fact that AES treats its - * input as a set of 4 32-bit words, so that there is no need to encode and - * decode the 128-bit counter before every block encryption */ -# define USE_RIJNDAEL_COUNTER_OPTIMIZATION -# if 0 && (defined(__powerpc__) || defined(__powerpc64__)) -/* XXXX do more experimentation before concluding this is actually - * a good idea. */ -# define FULL_UNROLL -# endif -#endif +aes_cnt_cipher_t * +aes_new_cipher(const char *key, const char *iv) +{ + aes_cnt_cipher_t *cipher; + cipher = tor_malloc_zero(sizeof(aes_cnt_cipher_t)); + EVP_EncryptInit(&cipher->evp, EVP_aes_128_ctr(), + (const unsigned char*)key, (const unsigned char *)iv); + return cipher; +} +void +aes_cipher_free(aes_cnt_cipher_t *cipher) +{ + if (!cipher) + return; + EVP_CIPHER_CTX_cleanup(&cipher->evp); + memset(cipher, 0, sizeof(aes_cnt_cipher_t)); + tor_free(cipher); +} +void +aes_crypt(aes_cnt_cipher_t *cipher, const char *input, size_t len, + char *output) +{ + int outl; -/*======================================================================*/ -/* From rijndael-alg-fst.h */ + tor_assert(len < INT_MAX); -typedef uint64_t u64; -typedef uint32_t u32; -typedef uint8_t u8; + EVP_EncryptUpdate(&cipher->evp, (unsigned char*)output, + &outl, (const unsigned char *)input, (int)len); +} +void +aes_crypt_inplace(aes_cnt_cipher_t *cipher, char *data, size_t len) +{ + int outl; -#ifdef USE_BUILTIN_AES -#define MAXNR 14 + tor_assert(len < INT_MAX); -static int rijndaelKeySetupEnc(u32 rk[/*4*(Nr + 1)*/], - const u8 cipherKey[], int keyBits); -#ifdef USE_RIJNDAEL_COUNTER_OPTIMIZATION -static void rijndaelEncrypt(const u32 rk[/*4*(Nr + 1)*/], int Nr, - u32 ctr3, u32 ctr2, - u32 ctr1, u32 ctr0, u8 ct[16]); + EVP_EncryptUpdate(&cipher->evp, (unsigned char*)data, + &outl, (unsigned char*)data, (int)len); +} +int +evaluate_evp_for_aes(int force_val) +{ + (void) force_val; + log_notice(LD_CRYPTO, "This version of OpenSSL has a known-good EVP " + "counter-mode implementation. Using it."); + return 0; +} +int +evaluate_ctr_for_aes(void) +{ + return 0; +} #else -static void rijndaelEncrypt(const u32 rk[/*4*(Nr + 1)*/], int Nr, - const u8 pt[16], u8 ct[16]); -#endif -#endif /*======================================================================*/ /* Interface to AES code, and counter implementation */ @@ -139,42 +151,129 @@ static void rijndaelEncrypt(const u32 rk[/*4*(Nr + 1)*/], int Nr, /** 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; -#else - u32 rk[4*(MAXNR+1)]; - int nr; -#endif + union { + EVP_CIPHER_CTX evp; + AES_KEY aes; + } key; -#if !defined(WORDS_BIGENDIAN) || defined(USE_RIJNDAEL_COUNTER_OPTIMIZATION) +#if !defined(WORDS_BIGENDIAN) #define USING_COUNTER_VARS /** These four values, together, implement a 128-bit counter, with * counter0 as the low-order word and counter3 as the high-order word. */ - u32 counter3; - u32 counter2; - u32 counter1; - u32 counter0; + uint32_t counter3; + uint32_t counter2; + uint32_t counter1; + uint32_t counter0; #endif -#ifndef USE_RIJNDAEL_COUNTER_OPTIMIZATION -#define USING_COUNTER_BUFS union { /** The counter, in big-endian order, as bytes. */ - u8 buf[16]; + uint8_t buf[16]; /** The counter, in big-endian order, as big-endian words. Note that * on big-endian platforms, this is redundant with counter3...0, * so we just use these values instead. */ - u32 buf32[4]; + uint32_t buf32[4]; } ctr_buf; -#endif + /** The encrypted value of ctr_buf. */ - u8 buf[16]; + uint8_t buf[16]; /** Our current stream position within buf. */ - u8 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 @@ -194,34 +293,32 @@ _aes_fill_buf(aes_cnt_cipher_t *cipher) * 3) changing the counter position was not trivial, last time I looked. * None of these issues are insurmountable in principle. */ -#if defined(USE_BUILTIN_AES) && defined(USE_RIJNDAEL_COUNTER_OPTIMIZATION) - rijndaelEncrypt(cipher->rk, cipher->nr, - cipher->counter3, cipher->counter2, - cipher->counter1, cipher->counter0, cipher->buf); -#else -#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); -#else - rijndaelEncrypt(cipher->rk, cipher->nr, cipher->ctr_buf.buf, cipher->buf); -#endif -#endif } +static void aes_set_key(aes_cnt_cipher_t *cipher, const char *key, + int key_bits); +static void aes_set_iv(aes_cnt_cipher_t *cipher, const char *iv); + /** - * Return a newly allocated counter-mode AES128 cipher implementation. + * Return a newly allocated counter-mode AES128 cipher implementation, + * using the 128-bit key <b>key</b> and the 128-bit IV <b>iv</b>. */ aes_cnt_cipher_t* -aes_new_cipher(void) +aes_new_cipher(const char *key, const char *iv) { aes_cnt_cipher_t* result = tor_malloc_zero(sizeof(aes_cnt_cipher_t)); + aes_set_key(result, key, 128); + aes_set_iv(result, iv); + return result; } @@ -229,53 +326,58 @@ aes_new_cipher(void) * <b>key_bits</b> bits long (must be 128, 192, or 256). Also resets * the counter to 0. */ -void +static 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)); -#else - cipher->nr = rijndaelKeySetupEnc(cipher->rk, (const unsigned char*)key, - key_bits); -#endif + #ifdef USING_COUNTER_VARS cipher->counter0 = 0; cipher->counter1 = 0; cipher->counter2 = 0; cipher->counter3 = 0; #endif -#ifdef USING_COUNTER_BUFS + memset(cipher->ctr_buf.buf, 0, sizeof(cipher->ctr_buf.buf)); -#endif 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> */ void -aes_free_cipher(aes_cnt_cipher_t *cipher) +aes_cipher_free(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); } -#if defined(USING_COUNTER_VARS) && defined(USING_COUNTER_BUFS) +#if defined(USING_COUNTER_VARS) #define UPDATE_CTR_BUF(c, n) STMT_BEGIN \ (c)->ctr_buf.buf32[3-(n)] = htonl((c)->counter ## n); \ STMT_END @@ -283,6 +385,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. @@ -291,32 +405,56 @@ void aes_crypt(aes_cnt_cipher_t *cipher, const char *input, size_t len, char *output) { - - /* 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; } - *(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); } } @@ -327,38 +465,42 @@ 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) { - - /* 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); +#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; + + 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); } } /** Reset the 128-bit counter of <b>cipher</b> to the 16-bit big-endian value * in <b>iv</b>. */ -void +static void aes_set_iv(aes_cnt_cipher_t *cipher, const char *iv) { #ifdef USING_COUNTER_VARS @@ -368,705 +510,12 @@ aes_set_iv(aes_cnt_cipher_t *cipher, const char *iv) cipher->counter0 = ntohl(get_uint32(iv+12)); #endif cipher->pos = 0; -#ifndef USE_RIJNDAEL_COUNTER_OPTIMIZATION memcpy(cipher->ctr_buf.buf, iv, 16); -#endif - - _aes_fill_buf(cipher); -} - -#ifdef USE_BUILTIN_AES -/*======================================================================*/ -/* From rijndael-alg-fst.c */ - -/** - * rijndael-alg-fst.c - * - * @version 3.0 (December 2000) - * - * Optimized ANSI C code for the Rijndael cipher (now AES) - * - * @author Vincent Rijmen <vincent.rijmen@esat.kuleuven.ac.be> - * @author Antoon Bosselaers <antoon.bosselaers@esat.kuleuven.ac.be> - * @author Paulo Barreto <paulo.barreto@terra.com.br> - * - * This code is hereby placed in the public domain. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS - * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR - * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE - * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, - * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -/* -Te0[x] = S [x].[02, 01, 01, 03]; -Te1[x] = S [x].[03, 02, 01, 01]; -Te2[x] = S [x].[01, 03, 02, 01]; -Te3[x] = S [x].[01, 01, 03, 02]; -Te4[x] = S [x].[01, 01, 01, 01]; - -Td0[x] = Si[x].[0e, 09, 0d, 0b]; -Td1[x] = Si[x].[0b, 0e, 09, 0d]; -Td2[x] = Si[x].[0d, 0b, 0e, 09]; -Td3[x] = Si[x].[09, 0d, 0b, 0e]; -Td4[x] = Si[x].[01, 01, 01, 01]; -*/ - -static const u32 Te0[256] = { - 0xc66363a5U, 0xf87c7c84U, 0xee777799U, 0xf67b7b8dU, - 0xfff2f20dU, 0xd66b6bbdU, 0xde6f6fb1U, 0x91c5c554U, - 0x60303050U, 0x02010103U, 0xce6767a9U, 0x562b2b7dU, - 0xe7fefe19U, 0xb5d7d762U, 0x4dababe6U, 0xec76769aU, - 0x8fcaca45U, 0x1f82829dU, 0x89c9c940U, 0xfa7d7d87U, - 0xeffafa15U, 0xb25959ebU, 0x8e4747c9U, 0xfbf0f00bU, - 0x41adadecU, 0xb3d4d467U, 0x5fa2a2fdU, 0x45afafeaU, - 0x239c9cbfU, 0x53a4a4f7U, 0xe4727296U, 0x9bc0c05bU, - 0x75b7b7c2U, 0xe1fdfd1cU, 0x3d9393aeU, 0x4c26266aU, - 0x6c36365aU, 0x7e3f3f41U, 0xf5f7f702U, 0x83cccc4fU, - 0x6834345cU, 0x51a5a5f4U, 0xd1e5e534U, 0xf9f1f108U, - 0xe2717193U, 0xabd8d873U, 0x62313153U, 0x2a15153fU, - 0x0804040cU, 0x95c7c752U, 0x46232365U, 0x9dc3c35eU, - 0x30181828U, 0x379696a1U, 0x0a05050fU, 0x2f9a9ab5U, - 0x0e070709U, 0x24121236U, 0x1b80809bU, 0xdfe2e23dU, - 0xcdebeb26U, 0x4e272769U, 0x7fb2b2cdU, 0xea75759fU, - 0x1209091bU, 0x1d83839eU, 0x582c2c74U, 0x341a1a2eU, - 0x361b1b2dU, 0xdc6e6eb2U, 0xb45a5aeeU, 0x5ba0a0fbU, - 0xa45252f6U, 0x763b3b4dU, 0xb7d6d661U, 0x7db3b3ceU, - 0x5229297bU, 0xdde3e33eU, 0x5e2f2f71U, 0x13848497U, - 0xa65353f5U, 0xb9d1d168U, 0x00000000U, 0xc1eded2cU, - 0x40202060U, 0xe3fcfc1fU, 0x79b1b1c8U, 0xb65b5bedU, - 0xd46a6abeU, 0x8dcbcb46U, 0x67bebed9U, 0x7239394bU, - 0x944a4adeU, 0x984c4cd4U, 0xb05858e8U, 0x85cfcf4aU, - 0xbbd0d06bU, 0xc5efef2aU, 0x4faaaae5U, 0xedfbfb16U, - 0x864343c5U, 0x9a4d4dd7U, 0x66333355U, 0x11858594U, - 0x8a4545cfU, 0xe9f9f910U, 0x04020206U, 0xfe7f7f81U, - 0xa05050f0U, 0x783c3c44U, 0x259f9fbaU, 0x4ba8a8e3U, - 0xa25151f3U, 0x5da3a3feU, 0x804040c0U, 0x058f8f8aU, - 0x3f9292adU, 0x219d9dbcU, 0x70383848U, 0xf1f5f504U, - 0x63bcbcdfU, 0x77b6b6c1U, 0xafdada75U, 0x42212163U, - 0x20101030U, 0xe5ffff1aU, 0xfdf3f30eU, 0xbfd2d26dU, - 0x81cdcd4cU, 0x180c0c14U, 0x26131335U, 0xc3ecec2fU, - 0xbe5f5fe1U, 0x359797a2U, 0x884444ccU, 0x2e171739U, - 0x93c4c457U, 0x55a7a7f2U, 0xfc7e7e82U, 0x7a3d3d47U, - 0xc86464acU, 0xba5d5de7U, 0x3219192bU, 0xe6737395U, - 0xc06060a0U, 0x19818198U, 0x9e4f4fd1U, 0xa3dcdc7fU, - 0x44222266U, 0x542a2a7eU, 0x3b9090abU, 0x0b888883U, - 0x8c4646caU, 0xc7eeee29U, 0x6bb8b8d3U, 0x2814143cU, - 0xa7dede79U, 0xbc5e5ee2U, 0x160b0b1dU, 0xaddbdb76U, - 0xdbe0e03bU, 0x64323256U, 0x743a3a4eU, 0x140a0a1eU, - 0x924949dbU, 0x0c06060aU, 0x4824246cU, 0xb85c5ce4U, - 0x9fc2c25dU, 0xbdd3d36eU, 0x43acacefU, 0xc46262a6U, - 0x399191a8U, 0x319595a4U, 0xd3e4e437U, 0xf279798bU, - 0xd5e7e732U, 0x8bc8c843U, 0x6e373759U, 0xda6d6db7U, - 0x018d8d8cU, 0xb1d5d564U, 0x9c4e4ed2U, 0x49a9a9e0U, - 0xd86c6cb4U, 0xac5656faU, 0xf3f4f407U, 0xcfeaea25U, - 0xca6565afU, 0xf47a7a8eU, 0x47aeaee9U, 0x10080818U, - 0x6fbabad5U, 0xf0787888U, 0x4a25256fU, 0x5c2e2e72U, - 0x381c1c24U, 0x57a6a6f1U, 0x73b4b4c7U, 0x97c6c651U, - 0xcbe8e823U, 0xa1dddd7cU, 0xe874749cU, 0x3e1f1f21U, - 0x964b4bddU, 0x61bdbddcU, 0x0d8b8b86U, 0x0f8a8a85U, - 0xe0707090U, 0x7c3e3e42U, 0x71b5b5c4U, 0xcc6666aaU, - 0x904848d8U, 0x06030305U, 0xf7f6f601U, 0x1c0e0e12U, - 0xc26161a3U, 0x6a35355fU, 0xae5757f9U, 0x69b9b9d0U, - 0x17868691U, 0x99c1c158U, 0x3a1d1d27U, 0x279e9eb9U, - 0xd9e1e138U, 0xebf8f813U, 0x2b9898b3U, 0x22111133U, - 0xd26969bbU, 0xa9d9d970U, 0x078e8e89U, 0x339494a7U, - 0x2d9b9bb6U, 0x3c1e1e22U, 0x15878792U, 0xc9e9e920U, - 0x87cece49U, 0xaa5555ffU, 0x50282878U, 0xa5dfdf7aU, - 0x038c8c8fU, 0x59a1a1f8U, 0x09898980U, 0x1a0d0d17U, - 0x65bfbfdaU, 0xd7e6e631U, 0x844242c6U, 0xd06868b8U, - 0x824141c3U, 0x299999b0U, 0x5a2d2d77U, 0x1e0f0f11U, - 0x7bb0b0cbU, 0xa85454fcU, 0x6dbbbbd6U, 0x2c16163aU, -}; -static const u32 Te1[256] = { - 0xa5c66363U, 0x84f87c7cU, 0x99ee7777U, 0x8df67b7bU, - 0x0dfff2f2U, 0xbdd66b6bU, 0xb1de6f6fU, 0x5491c5c5U, - 0x50603030U, 0x03020101U, 0xa9ce6767U, 0x7d562b2bU, - 0x19e7fefeU, 0x62b5d7d7U, 0xe64dababU, 0x9aec7676U, - 0x458fcacaU, 0x9d1f8282U, 0x4089c9c9U, 0x87fa7d7dU, - 0x15effafaU, 0xebb25959U, 0xc98e4747U, 0x0bfbf0f0U, - 0xec41adadU, 0x67b3d4d4U, 0xfd5fa2a2U, 0xea45afafU, - 0xbf239c9cU, 0xf753a4a4U, 0x96e47272U, 0x5b9bc0c0U, - 0xc275b7b7U, 0x1ce1fdfdU, 0xae3d9393U, 0x6a4c2626U, - 0x5a6c3636U, 0x417e3f3fU, 0x02f5f7f7U, 0x4f83ccccU, - 0x5c683434U, 0xf451a5a5U, 0x34d1e5e5U, 0x08f9f1f1U, - 0x93e27171U, 0x73abd8d8U, 0x53623131U, 0x3f2a1515U, - 0x0c080404U, 0x5295c7c7U, 0x65462323U, 0x5e9dc3c3U, - 0x28301818U, 0xa1379696U, 0x0f0a0505U, 0xb52f9a9aU, - 0x090e0707U, 0x36241212U, 0x9b1b8080U, 0x3ddfe2e2U, - 0x26cdebebU, 0x694e2727U, 0xcd7fb2b2U, 0x9fea7575U, - 0x1b120909U, 0x9e1d8383U, 0x74582c2cU, 0x2e341a1aU, - 0x2d361b1bU, 0xb2dc6e6eU, 0xeeb45a5aU, 0xfb5ba0a0U, - 0xf6a45252U, 0x4d763b3bU, 0x61b7d6d6U, 0xce7db3b3U, - 0x7b522929U, 0x3edde3e3U, 0x715e2f2fU, 0x97138484U, - 0xf5a65353U, 0x68b9d1d1U, 0x00000000U, 0x2cc1ededU, - 0x60402020U, 0x1fe3fcfcU, 0xc879b1b1U, 0xedb65b5bU, - 0xbed46a6aU, 0x468dcbcbU, 0xd967bebeU, 0x4b723939U, - 0xde944a4aU, 0xd4984c4cU, 0xe8b05858U, 0x4a85cfcfU, - 0x6bbbd0d0U, 0x2ac5efefU, 0xe54faaaaU, 0x16edfbfbU, - 0xc5864343U, 0xd79a4d4dU, 0x55663333U, 0x94118585U, - 0xcf8a4545U, 0x10e9f9f9U, 0x06040202U, 0x81fe7f7fU, - 0xf0a05050U, 0x44783c3cU, 0xba259f9fU, 0xe34ba8a8U, - 0xf3a25151U, 0xfe5da3a3U, 0xc0804040U, 0x8a058f8fU, - 0xad3f9292U, 0xbc219d9dU, 0x48703838U, 0x04f1f5f5U, - 0xdf63bcbcU, 0xc177b6b6U, 0x75afdadaU, 0x63422121U, - 0x30201010U, 0x1ae5ffffU, 0x0efdf3f3U, 0x6dbfd2d2U, - 0x4c81cdcdU, 0x14180c0cU, 0x35261313U, 0x2fc3ececU, - 0xe1be5f5fU, 0xa2359797U, 0xcc884444U, 0x392e1717U, - 0x5793c4c4U, 0xf255a7a7U, 0x82fc7e7eU, 0x477a3d3dU, - 0xacc86464U, 0xe7ba5d5dU, 0x2b321919U, 0x95e67373U, - 0xa0c06060U, 0x98198181U, 0xd19e4f4fU, 0x7fa3dcdcU, - 0x66442222U, 0x7e542a2aU, 0xab3b9090U, 0x830b8888U, - 0xca8c4646U, 0x29c7eeeeU, 0xd36bb8b8U, 0x3c281414U, - 0x79a7dedeU, 0xe2bc5e5eU, 0x1d160b0bU, 0x76addbdbU, - 0x3bdbe0e0U, 0x56643232U, 0x4e743a3aU, 0x1e140a0aU, - 0xdb924949U, 0x0a0c0606U, 0x6c482424U, 0xe4b85c5cU, - 0x5d9fc2c2U, 0x6ebdd3d3U, 0xef43acacU, 0xa6c46262U, - 0xa8399191U, 0xa4319595U, 0x37d3e4e4U, 0x8bf27979U, - 0x32d5e7e7U, 0x438bc8c8U, 0x596e3737U, 0xb7da6d6dU, - 0x8c018d8dU, 0x64b1d5d5U, 0xd29c4e4eU, 0xe049a9a9U, - 0xb4d86c6cU, 0xfaac5656U, 0x07f3f4f4U, 0x25cfeaeaU, - 0xafca6565U, 0x8ef47a7aU, 0xe947aeaeU, 0x18100808U, - 0xd56fbabaU, 0x88f07878U, 0x6f4a2525U, 0x725c2e2eU, - 0x24381c1cU, 0xf157a6a6U, 0xc773b4b4U, 0x5197c6c6U, - 0x23cbe8e8U, 0x7ca1ddddU, 0x9ce87474U, 0x213e1f1fU, - 0xdd964b4bU, 0xdc61bdbdU, 0x860d8b8bU, 0x850f8a8aU, - 0x90e07070U, 0x427c3e3eU, 0xc471b5b5U, 0xaacc6666U, - 0xd8904848U, 0x05060303U, 0x01f7f6f6U, 0x121c0e0eU, - 0xa3c26161U, 0x5f6a3535U, 0xf9ae5757U, 0xd069b9b9U, - 0x91178686U, 0x5899c1c1U, 0x273a1d1dU, 0xb9279e9eU, - 0x38d9e1e1U, 0x13ebf8f8U, 0xb32b9898U, 0x33221111U, - 0xbbd26969U, 0x70a9d9d9U, 0x89078e8eU, 0xa7339494U, - 0xb62d9b9bU, 0x223c1e1eU, 0x92158787U, 0x20c9e9e9U, - 0x4987ceceU, 0xffaa5555U, 0x78502828U, 0x7aa5dfdfU, - 0x8f038c8cU, 0xf859a1a1U, 0x80098989U, 0x171a0d0dU, - 0xda65bfbfU, 0x31d7e6e6U, 0xc6844242U, 0xb8d06868U, - 0xc3824141U, 0xb0299999U, 0x775a2d2dU, 0x111e0f0fU, - 0xcb7bb0b0U, 0xfca85454U, 0xd66dbbbbU, 0x3a2c1616U, -}; -static const u32 Te2[256] = { - 0x63a5c663U, 0x7c84f87cU, 0x7799ee77U, 0x7b8df67bU, - 0xf20dfff2U, 0x6bbdd66bU, 0x6fb1de6fU, 0xc55491c5U, - 0x30506030U, 0x01030201U, 0x67a9ce67U, 0x2b7d562bU, - 0xfe19e7feU, 0xd762b5d7U, 0xabe64dabU, 0x769aec76U, - 0xca458fcaU, 0x829d1f82U, 0xc94089c9U, 0x7d87fa7dU, - 0xfa15effaU, 0x59ebb259U, 0x47c98e47U, 0xf00bfbf0U, - 0xadec41adU, 0xd467b3d4U, 0xa2fd5fa2U, 0xafea45afU, - 0x9cbf239cU, 0xa4f753a4U, 0x7296e472U, 0xc05b9bc0U, - 0xb7c275b7U, 0xfd1ce1fdU, 0x93ae3d93U, 0x266a4c26U, - 0x365a6c36U, 0x3f417e3fU, 0xf702f5f7U, 0xcc4f83ccU, - 0x345c6834U, 0xa5f451a5U, 0xe534d1e5U, 0xf108f9f1U, - 0x7193e271U, 0xd873abd8U, 0x31536231U, 0x153f2a15U, - 0x040c0804U, 0xc75295c7U, 0x23654623U, 0xc35e9dc3U, - 0x18283018U, 0x96a13796U, 0x050f0a05U, 0x9ab52f9aU, - 0x07090e07U, 0x12362412U, 0x809b1b80U, 0xe23ddfe2U, - 0xeb26cdebU, 0x27694e27U, 0xb2cd7fb2U, 0x759fea75U, - 0x091b1209U, 0x839e1d83U, 0x2c74582cU, 0x1a2e341aU, - 0x1b2d361bU, 0x6eb2dc6eU, 0x5aeeb45aU, 0xa0fb5ba0U, - 0x52f6a452U, 0x3b4d763bU, 0xd661b7d6U, 0xb3ce7db3U, - 0x297b5229U, 0xe33edde3U, 0x2f715e2fU, 0x84971384U, - 0x53f5a653U, 0xd168b9d1U, 0x00000000U, 0xed2cc1edU, - 0x20604020U, 0xfc1fe3fcU, 0xb1c879b1U, 0x5bedb65bU, - 0x6abed46aU, 0xcb468dcbU, 0xbed967beU, 0x394b7239U, - 0x4ade944aU, 0x4cd4984cU, 0x58e8b058U, 0xcf4a85cfU, - 0xd06bbbd0U, 0xef2ac5efU, 0xaae54faaU, 0xfb16edfbU, - 0x43c58643U, 0x4dd79a4dU, 0x33556633U, 0x85941185U, - 0x45cf8a45U, 0xf910e9f9U, 0x02060402U, 0x7f81fe7fU, - 0x50f0a050U, 0x3c44783cU, 0x9fba259fU, 0xa8e34ba8U, - 0x51f3a251U, 0xa3fe5da3U, 0x40c08040U, 0x8f8a058fU, - 0x92ad3f92U, 0x9dbc219dU, 0x38487038U, 0xf504f1f5U, - 0xbcdf63bcU, 0xb6c177b6U, 0xda75afdaU, 0x21634221U, - 0x10302010U, 0xff1ae5ffU, 0xf30efdf3U, 0xd26dbfd2U, - 0xcd4c81cdU, 0x0c14180cU, 0x13352613U, 0xec2fc3ecU, - 0x5fe1be5fU, 0x97a23597U, 0x44cc8844U, 0x17392e17U, - 0xc45793c4U, 0xa7f255a7U, 0x7e82fc7eU, 0x3d477a3dU, - 0x64acc864U, 0x5de7ba5dU, 0x192b3219U, 0x7395e673U, - 0x60a0c060U, 0x81981981U, 0x4fd19e4fU, 0xdc7fa3dcU, - 0x22664422U, 0x2a7e542aU, 0x90ab3b90U, 0x88830b88U, - 0x46ca8c46U, 0xee29c7eeU, 0xb8d36bb8U, 0x143c2814U, - 0xde79a7deU, 0x5ee2bc5eU, 0x0b1d160bU, 0xdb76addbU, - 0xe03bdbe0U, 0x32566432U, 0x3a4e743aU, 0x0a1e140aU, - 0x49db9249U, 0x060a0c06U, 0x246c4824U, 0x5ce4b85cU, - 0xc25d9fc2U, 0xd36ebdd3U, 0xacef43acU, 0x62a6c462U, - 0x91a83991U, 0x95a43195U, 0xe437d3e4U, 0x798bf279U, - 0xe732d5e7U, 0xc8438bc8U, 0x37596e37U, 0x6db7da6dU, - 0x8d8c018dU, 0xd564b1d5U, 0x4ed29c4eU, 0xa9e049a9U, - 0x6cb4d86cU, 0x56faac56U, 0xf407f3f4U, 0xea25cfeaU, - 0x65afca65U, 0x7a8ef47aU, 0xaee947aeU, 0x08181008U, - 0xbad56fbaU, 0x7888f078U, 0x256f4a25U, 0x2e725c2eU, - 0x1c24381cU, 0xa6f157a6U, 0xb4c773b4U, 0xc65197c6U, - 0xe823cbe8U, 0xdd7ca1ddU, 0x749ce874U, 0x1f213e1fU, - 0x4bdd964bU, 0xbddc61bdU, 0x8b860d8bU, 0x8a850f8aU, - 0x7090e070U, 0x3e427c3eU, 0xb5c471b5U, 0x66aacc66U, - 0x48d89048U, 0x03050603U, 0xf601f7f6U, 0x0e121c0eU, - 0x61a3c261U, 0x355f6a35U, 0x57f9ae57U, 0xb9d069b9U, - 0x86911786U, 0xc15899c1U, 0x1d273a1dU, 0x9eb9279eU, - 0xe138d9e1U, 0xf813ebf8U, 0x98b32b98U, 0x11332211U, - 0x69bbd269U, 0xd970a9d9U, 0x8e89078eU, 0x94a73394U, - 0x9bb62d9bU, 0x1e223c1eU, 0x87921587U, 0xe920c9e9U, - 0xce4987ceU, 0x55ffaa55U, 0x28785028U, 0xdf7aa5dfU, - 0x8c8f038cU, 0xa1f859a1U, 0x89800989U, 0x0d171a0dU, - 0xbfda65bfU, 0xe631d7e6U, 0x42c68442U, 0x68b8d068U, - 0x41c38241U, 0x99b02999U, 0x2d775a2dU, 0x0f111e0fU, - 0xb0cb7bb0U, 0x54fca854U, 0xbbd66dbbU, 0x163a2c16U, -}; -static const u32 Te3[256] = { - - 0x6363a5c6U, 0x7c7c84f8U, 0x777799eeU, 0x7b7b8df6U, - 0xf2f20dffU, 0x6b6bbdd6U, 0x6f6fb1deU, 0xc5c55491U, - 0x30305060U, 0x01010302U, 0x6767a9ceU, 0x2b2b7d56U, - 0xfefe19e7U, 0xd7d762b5U, 0xababe64dU, 0x76769aecU, - 0xcaca458fU, 0x82829d1fU, 0xc9c94089U, 0x7d7d87faU, - 0xfafa15efU, 0x5959ebb2U, 0x4747c98eU, 0xf0f00bfbU, - 0xadadec41U, 0xd4d467b3U, 0xa2a2fd5fU, 0xafafea45U, - 0x9c9cbf23U, 0xa4a4f753U, 0x727296e4U, 0xc0c05b9bU, - 0xb7b7c275U, 0xfdfd1ce1U, 0x9393ae3dU, 0x26266a4cU, - 0x36365a6cU, 0x3f3f417eU, 0xf7f702f5U, 0xcccc4f83U, - 0x34345c68U, 0xa5a5f451U, 0xe5e534d1U, 0xf1f108f9U, - 0x717193e2U, 0xd8d873abU, 0x31315362U, 0x15153f2aU, - 0x04040c08U, 0xc7c75295U, 0x23236546U, 0xc3c35e9dU, - 0x18182830U, 0x9696a137U, 0x05050f0aU, 0x9a9ab52fU, - 0x0707090eU, 0x12123624U, 0x80809b1bU, 0xe2e23ddfU, - 0xebeb26cdU, 0x2727694eU, 0xb2b2cd7fU, 0x75759feaU, - 0x09091b12U, 0x83839e1dU, 0x2c2c7458U, 0x1a1a2e34U, - 0x1b1b2d36U, 0x6e6eb2dcU, 0x5a5aeeb4U, 0xa0a0fb5bU, - 0x5252f6a4U, 0x3b3b4d76U, 0xd6d661b7U, 0xb3b3ce7dU, - 0x29297b52U, 0xe3e33eddU, 0x2f2f715eU, 0x84849713U, - 0x5353f5a6U, 0xd1d168b9U, 0x00000000U, 0xeded2cc1U, - 0x20206040U, 0xfcfc1fe3U, 0xb1b1c879U, 0x5b5bedb6U, - 0x6a6abed4U, 0xcbcb468dU, 0xbebed967U, 0x39394b72U, - 0x4a4ade94U, 0x4c4cd498U, 0x5858e8b0U, 0xcfcf4a85U, - 0xd0d06bbbU, 0xefef2ac5U, 0xaaaae54fU, 0xfbfb16edU, - 0x4343c586U, 0x4d4dd79aU, 0x33335566U, 0x85859411U, - 0x4545cf8aU, 0xf9f910e9U, 0x02020604U, 0x7f7f81feU, - 0x5050f0a0U, 0x3c3c4478U, 0x9f9fba25U, 0xa8a8e34bU, - 0x5151f3a2U, 0xa3a3fe5dU, 0x4040c080U, 0x8f8f8a05U, - 0x9292ad3fU, 0x9d9dbc21U, 0x38384870U, 0xf5f504f1U, - 0xbcbcdf63U, 0xb6b6c177U, 0xdada75afU, 0x21216342U, - 0x10103020U, 0xffff1ae5U, 0xf3f30efdU, 0xd2d26dbfU, - 0xcdcd4c81U, 0x0c0c1418U, 0x13133526U, 0xecec2fc3U, - 0x5f5fe1beU, 0x9797a235U, 0x4444cc88U, 0x1717392eU, - 0xc4c45793U, 0xa7a7f255U, 0x7e7e82fcU, 0x3d3d477aU, - 0x6464acc8U, 0x5d5de7baU, 0x19192b32U, 0x737395e6U, - 0x6060a0c0U, 0x81819819U, 0x4f4fd19eU, 0xdcdc7fa3U, - 0x22226644U, 0x2a2a7e54U, 0x9090ab3bU, 0x8888830bU, - 0x4646ca8cU, 0xeeee29c7U, 0xb8b8d36bU, 0x14143c28U, - 0xdede79a7U, 0x5e5ee2bcU, 0x0b0b1d16U, 0xdbdb76adU, - 0xe0e03bdbU, 0x32325664U, 0x3a3a4e74U, 0x0a0a1e14U, - 0x4949db92U, 0x06060a0cU, 0x24246c48U, 0x5c5ce4b8U, - 0xc2c25d9fU, 0xd3d36ebdU, 0xacacef43U, 0x6262a6c4U, - 0x9191a839U, 0x9595a431U, 0xe4e437d3U, 0x79798bf2U, - 0xe7e732d5U, 0xc8c8438bU, 0x3737596eU, 0x6d6db7daU, - 0x8d8d8c01U, 0xd5d564b1U, 0x4e4ed29cU, 0xa9a9e049U, - 0x6c6cb4d8U, 0x5656faacU, 0xf4f407f3U, 0xeaea25cfU, - 0x6565afcaU, 0x7a7a8ef4U, 0xaeaee947U, 0x08081810U, - 0xbabad56fU, 0x787888f0U, 0x25256f4aU, 0x2e2e725cU, - 0x1c1c2438U, 0xa6a6f157U, 0xb4b4c773U, 0xc6c65197U, - 0xe8e823cbU, 0xdddd7ca1U, 0x74749ce8U, 0x1f1f213eU, - 0x4b4bdd96U, 0xbdbddc61U, 0x8b8b860dU, 0x8a8a850fU, - 0x707090e0U, 0x3e3e427cU, 0xb5b5c471U, 0x6666aaccU, - 0x4848d890U, 0x03030506U, 0xf6f601f7U, 0x0e0e121cU, - 0x6161a3c2U, 0x35355f6aU, 0x5757f9aeU, 0xb9b9d069U, - 0x86869117U, 0xc1c15899U, 0x1d1d273aU, 0x9e9eb927U, - 0xe1e138d9U, 0xf8f813ebU, 0x9898b32bU, 0x11113322U, - 0x6969bbd2U, 0xd9d970a9U, 0x8e8e8907U, 0x9494a733U, - 0x9b9bb62dU, 0x1e1e223cU, 0x87879215U, 0xe9e920c9U, - 0xcece4987U, 0x5555ffaaU, 0x28287850U, 0xdfdf7aa5U, - 0x8c8c8f03U, 0xa1a1f859U, 0x89898009U, 0x0d0d171aU, - 0xbfbfda65U, 0xe6e631d7U, 0x4242c684U, 0x6868b8d0U, - 0x4141c382U, 0x9999b029U, 0x2d2d775aU, 0x0f0f111eU, - 0xb0b0cb7bU, 0x5454fca8U, 0xbbbbd66dU, 0x16163a2cU, -}; -static const u32 Te4[256] = { - 0x63636363U, 0x7c7c7c7cU, 0x77777777U, 0x7b7b7b7bU, - 0xf2f2f2f2U, 0x6b6b6b6bU, 0x6f6f6f6fU, 0xc5c5c5c5U, - 0x30303030U, 0x01010101U, 0x67676767U, 0x2b2b2b2bU, - 0xfefefefeU, 0xd7d7d7d7U, 0xababababU, 0x76767676U, - 0xcacacacaU, 0x82828282U, 0xc9c9c9c9U, 0x7d7d7d7dU, - 0xfafafafaU, 0x59595959U, 0x47474747U, 0xf0f0f0f0U, - 0xadadadadU, 0xd4d4d4d4U, 0xa2a2a2a2U, 0xafafafafU, - 0x9c9c9c9cU, 0xa4a4a4a4U, 0x72727272U, 0xc0c0c0c0U, - 0xb7b7b7b7U, 0xfdfdfdfdU, 0x93939393U, 0x26262626U, - 0x36363636U, 0x3f3f3f3fU, 0xf7f7f7f7U, 0xccccccccU, - 0x34343434U, 0xa5a5a5a5U, 0xe5e5e5e5U, 0xf1f1f1f1U, - 0x71717171U, 0xd8d8d8d8U, 0x31313131U, 0x15151515U, - 0x04040404U, 0xc7c7c7c7U, 0x23232323U, 0xc3c3c3c3U, - 0x18181818U, 0x96969696U, 0x05050505U, 0x9a9a9a9aU, - 0x07070707U, 0x12121212U, 0x80808080U, 0xe2e2e2e2U, - 0xebebebebU, 0x27272727U, 0xb2b2b2b2U, 0x75757575U, - 0x09090909U, 0x83838383U, 0x2c2c2c2cU, 0x1a1a1a1aU, - 0x1b1b1b1bU, 0x6e6e6e6eU, 0x5a5a5a5aU, 0xa0a0a0a0U, - 0x52525252U, 0x3b3b3b3bU, 0xd6d6d6d6U, 0xb3b3b3b3U, - 0x29292929U, 0xe3e3e3e3U, 0x2f2f2f2fU, 0x84848484U, - 0x53535353U, 0xd1d1d1d1U, 0x00000000U, 0xededededU, - 0x20202020U, 0xfcfcfcfcU, 0xb1b1b1b1U, 0x5b5b5b5bU, - 0x6a6a6a6aU, 0xcbcbcbcbU, 0xbebebebeU, 0x39393939U, - 0x4a4a4a4aU, 0x4c4c4c4cU, 0x58585858U, 0xcfcfcfcfU, - 0xd0d0d0d0U, 0xefefefefU, 0xaaaaaaaaU, 0xfbfbfbfbU, - 0x43434343U, 0x4d4d4d4dU, 0x33333333U, 0x85858585U, - 0x45454545U, 0xf9f9f9f9U, 0x02020202U, 0x7f7f7f7fU, - 0x50505050U, 0x3c3c3c3cU, 0x9f9f9f9fU, 0xa8a8a8a8U, - 0x51515151U, 0xa3a3a3a3U, 0x40404040U, 0x8f8f8f8fU, - 0x92929292U, 0x9d9d9d9dU, 0x38383838U, 0xf5f5f5f5U, - 0xbcbcbcbcU, 0xb6b6b6b6U, 0xdadadadaU, 0x21212121U, - 0x10101010U, 0xffffffffU, 0xf3f3f3f3U, 0xd2d2d2d2U, - 0xcdcdcdcdU, 0x0c0c0c0cU, 0x13131313U, 0xececececU, - 0x5f5f5f5fU, 0x97979797U, 0x44444444U, 0x17171717U, - 0xc4c4c4c4U, 0xa7a7a7a7U, 0x7e7e7e7eU, 0x3d3d3d3dU, - 0x64646464U, 0x5d5d5d5dU, 0x19191919U, 0x73737373U, - 0x60606060U, 0x81818181U, 0x4f4f4f4fU, 0xdcdcdcdcU, - 0x22222222U, 0x2a2a2a2aU, 0x90909090U, 0x88888888U, - 0x46464646U, 0xeeeeeeeeU, 0xb8b8b8b8U, 0x14141414U, - 0xdedededeU, 0x5e5e5e5eU, 0x0b0b0b0bU, 0xdbdbdbdbU, - 0xe0e0e0e0U, 0x32323232U, 0x3a3a3a3aU, 0x0a0a0a0aU, - 0x49494949U, 0x06060606U, 0x24242424U, 0x5c5c5c5cU, - 0xc2c2c2c2U, 0xd3d3d3d3U, 0xacacacacU, 0x62626262U, - 0x91919191U, 0x95959595U, 0xe4e4e4e4U, 0x79797979U, - 0xe7e7e7e7U, 0xc8c8c8c8U, 0x37373737U, 0x6d6d6d6dU, - 0x8d8d8d8dU, 0xd5d5d5d5U, 0x4e4e4e4eU, 0xa9a9a9a9U, - 0x6c6c6c6cU, 0x56565656U, 0xf4f4f4f4U, 0xeaeaeaeaU, - 0x65656565U, 0x7a7a7a7aU, 0xaeaeaeaeU, 0x08080808U, - 0xbabababaU, 0x78787878U, 0x25252525U, 0x2e2e2e2eU, - 0x1c1c1c1cU, 0xa6a6a6a6U, 0xb4b4b4b4U, 0xc6c6c6c6U, - 0xe8e8e8e8U, 0xddddddddU, 0x74747474U, 0x1f1f1f1fU, - 0x4b4b4b4bU, 0xbdbdbdbdU, 0x8b8b8b8bU, 0x8a8a8a8aU, - 0x70707070U, 0x3e3e3e3eU, 0xb5b5b5b5U, 0x66666666U, - 0x48484848U, 0x03030303U, 0xf6f6f6f6U, 0x0e0e0e0eU, - 0x61616161U, 0x35353535U, 0x57575757U, 0xb9b9b9b9U, - 0x86868686U, 0xc1c1c1c1U, 0x1d1d1d1dU, 0x9e9e9e9eU, - 0xe1e1e1e1U, 0xf8f8f8f8U, 0x98989898U, 0x11111111U, - 0x69696969U, 0xd9d9d9d9U, 0x8e8e8e8eU, 0x94949494U, - 0x9b9b9b9bU, 0x1e1e1e1eU, 0x87878787U, 0xe9e9e9e9U, - 0xcecececeU, 0x55555555U, 0x28282828U, 0xdfdfdfdfU, - 0x8c8c8c8cU, 0xa1a1a1a1U, 0x89898989U, 0x0d0d0d0dU, - 0xbfbfbfbfU, 0xe6e6e6e6U, 0x42424242U, 0x68686868U, - 0x41414141U, 0x99999999U, 0x2d2d2d2dU, 0x0f0f0f0fU, - 0xb0b0b0b0U, 0x54545454U, 0xbbbbbbbbU, 0x16161616U, -}; - -static const u32 rcon[] = { - 0x01000000, 0x02000000, 0x04000000, 0x08000000, - 0x10000000, 0x20000000, 0x40000000, 0x80000000, - 0x1B000000, 0x36000000, /* for 128-bit blocks, Rijndael never uses more than 10 rcon values */ -}; - -#define SWAP(x) (_lrotl(x, 8) & 0x00ff00ff | _lrotr(x, 8) & 0xff00ff00) - -#ifdef _MSC_VER -#define GETU32(p) SWAP(*((u32 *)(p))) -#define PUTU32(ct, st) { *((u32 *)(ct)) = SWAP((st)); } -#else -#define GETU32(pt) (((u32)(pt)[0] << 24) ^ ((u32)(pt)[1] << 16) ^ ((u32)(pt)[2] << 8) ^ ((u32)(pt)[3])) -#define PUTU32(ct, st) { (ct)[0] = (u8)((st) >> 24); (ct)[1] = (u8)((st) >> 16); (ct)[2] = (u8)((st) >> 8); (ct)[3] = (u8)(st); } -#endif - -/** - * Expand the cipher key into the encryption key schedule. - * - * @return the number of rounds for the given cipher key size. - */ -static int -rijndaelKeySetupEnc(u32 rk[/*4*(Nr + 1)*/], const u8 cipherKey[], int keyBits) -{ - int i = 0; - u32 temp; - - rk[0] = GETU32(cipherKey ); - rk[1] = GETU32(cipherKey + 4); - rk[2] = GETU32(cipherKey + 8); - rk[3] = GETU32(cipherKey + 12); - if (keyBits == 128) { - for (;;) { - temp = rk[3]; - rk[4] = rk[0] ^ - (Te4[(temp >> 16) & 0xff] & 0xff000000) ^ - (Te4[(temp >> 8) & 0xff] & 0x00ff0000) ^ - (Te4[(temp ) & 0xff] & 0x0000ff00) ^ - (Te4[(temp >> 24) ] & 0x000000ff) ^ - rcon[i]; - rk[5] = rk[1] ^ rk[4]; - rk[6] = rk[2] ^ rk[5]; - rk[7] = rk[3] ^ rk[6]; - if (++i == 10) { - return 10; - } - rk += 4; - } - } - rk[4] = GETU32(cipherKey + 16); - rk[5] = GETU32(cipherKey + 20); - if (keyBits == 192) { - for (;;) { - temp = rk[ 5]; - rk[ 6] = rk[ 0] ^ - (Te4[(temp >> 16) & 0xff] & 0xff000000) ^ - (Te4[(temp >> 8) & 0xff] & 0x00ff0000) ^ - (Te4[(temp ) & 0xff] & 0x0000ff00) ^ - (Te4[(temp >> 24) ] & 0x000000ff) ^ - rcon[i]; - rk[ 7] = rk[ 1] ^ rk[ 6]; - rk[ 8] = rk[ 2] ^ rk[ 7]; - rk[ 9] = rk[ 3] ^ rk[ 8]; - if (++i == 8) { - return 12; - } - rk[10] = rk[ 4] ^ rk[ 9]; - rk[11] = rk[ 5] ^ rk[10]; - rk += 6; - } - } - rk[6] = GETU32(cipherKey + 24); - rk[7] = GETU32(cipherKey + 28); - if (keyBits == 256) { - for (;;) { - temp = rk[ 7]; - rk[ 8] = rk[ 0] ^ - (Te4[(temp >> 16) & 0xff] & 0xff000000) ^ - (Te4[(temp >> 8) & 0xff] & 0x00ff0000) ^ - (Te4[(temp ) & 0xff] & 0x0000ff00) ^ - (Te4[(temp >> 24) ] & 0x000000ff) ^ - rcon[i]; - rk[ 9] = rk[ 1] ^ rk[ 8]; - rk[10] = rk[ 2] ^ rk[ 9]; - rk[11] = rk[ 3] ^ rk[10]; - if (++i == 7) { - return 14; - } - temp = rk[11]; - rk[12] = rk[ 4] ^ - (Te4[(temp >> 24) ] & 0xff000000) ^ - (Te4[(temp >> 16) & 0xff] & 0x00ff0000) ^ - (Te4[(temp >> 8) & 0xff] & 0x0000ff00) ^ - (Te4[(temp ) & 0xff] & 0x000000ff); - rk[13] = rk[ 5] ^ rk[12]; - rk[14] = rk[ 6] ^ rk[13]; - rk[15] = rk[ 7] ^ rk[14]; - - rk += 8; - } - } - return 0; -} -#ifdef USE_RIJNDAEL_COUNTER_OPTIMIZATION -static void -rijndaelEncrypt(const u32 rk[/*4*(Nr + 1)*/], int Nr, u32 ctr3, u32 ctr2, u32 ctr1, u32 ctr0, u8 ct[16]) -#else -static void -rijndaelEncrypt(const u32 rk[/*4*(Nr + 1)*/], int Nr, const u8 pt[16], u8 ct[16]) -#endif -{ - u32 s0, s1, s2, s3, t0, t1, t2, t3; -#ifndef FULL_UNROLL - int r; -#endif /* ?FULL_UNROLL */ - - /* - * map byte array block to cipher state - * and add initial round key: - */ -#ifdef USE_RIJNDAEL_COUNTER_OPTIMIZATION - s0 = ctr3 ^ rk[0]; - s1 = ctr2 ^ rk[1]; - s2 = ctr1 ^ rk[2]; - s3 = ctr0 ^ rk[3]; -#else - s0 = GETU32(pt ) ^ rk[0]; - s1 = GETU32(pt + 4) ^ rk[1]; - s2 = GETU32(pt + 8) ^ rk[2]; - s3 = GETU32(pt + 12) ^ rk[3]; +#ifdef CAN_USE_OPENSSL_CTR + if (!should_use_openssl_CTR) #endif - -#ifdef FULL_UNROLL - /* round 1: */ - t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >> 8) & 0xff] ^ Te3[s3 & 0xff] ^ rk[ 4]; - t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >> 8) & 0xff] ^ Te3[s0 & 0xff] ^ rk[ 5]; - t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >> 8) & 0xff] ^ Te3[s1 & 0xff] ^ rk[ 6]; - t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >> 8) & 0xff] ^ Te3[s2 & 0xff] ^ rk[ 7]; - /* round 2: */ - s0 = Te0[t0 >> 24] ^ Te1[(t1 >> 16) & 0xff] ^ Te2[(t2 >> 8) & 0xff] ^ Te3[t3 & 0xff] ^ rk[ 8]; - s1 = Te0[t1 >> 24] ^ Te1[(t2 >> 16) & 0xff] ^ Te2[(t3 >> 8) & 0xff] ^ Te3[t0 & 0xff] ^ rk[ 9]; - s2 = Te0[t2 >> 24] ^ Te1[(t3 >> 16) & 0xff] ^ Te2[(t0 >> 8) & 0xff] ^ Te3[t1 & 0xff] ^ rk[10]; - s3 = Te0[t3 >> 24] ^ Te1[(t0 >> 16) & 0xff] ^ Te2[(t1 >> 8) & 0xff] ^ Te3[t2 & 0xff] ^ rk[11]; - /* round 3: */ - t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >> 8) & 0xff] ^ Te3[s3 & 0xff] ^ rk[12]; - t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >> 8) & 0xff] ^ Te3[s0 & 0xff] ^ rk[13]; - t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >> 8) & 0xff] ^ Te3[s1 & 0xff] ^ rk[14]; - t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >> 8) & 0xff] ^ Te3[s2 & 0xff] ^ rk[15]; - /* round 4: */ - s0 = Te0[t0 >> 24] ^ Te1[(t1 >> 16) & 0xff] ^ Te2[(t2 >> 8) & 0xff] ^ Te3[t3 & 0xff] ^ rk[16]; - s1 = Te0[t1 >> 24] ^ Te1[(t2 >> 16) & 0xff] ^ Te2[(t3 >> 8) & 0xff] ^ Te3[t0 & 0xff] ^ rk[17]; - s2 = Te0[t2 >> 24] ^ Te1[(t3 >> 16) & 0xff] ^ Te2[(t0 >> 8) & 0xff] ^ Te3[t1 & 0xff] ^ rk[18]; - s3 = Te0[t3 >> 24] ^ Te1[(t0 >> 16) & 0xff] ^ Te2[(t1 >> 8) & 0xff] ^ Te3[t2 & 0xff] ^ rk[19]; - /* round 5: */ - t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >> 8) & 0xff] ^ Te3[s3 & 0xff] ^ rk[20]; - t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >> 8) & 0xff] ^ Te3[s0 & 0xff] ^ rk[21]; - t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >> 8) & 0xff] ^ Te3[s1 & 0xff] ^ rk[22]; - t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >> 8) & 0xff] ^ Te3[s2 & 0xff] ^ rk[23]; - /* round 6: */ - s0 = Te0[t0 >> 24] ^ Te1[(t1 >> 16) & 0xff] ^ Te2[(t2 >> 8) & 0xff] ^ Te3[t3 & 0xff] ^ rk[24]; - s1 = Te0[t1 >> 24] ^ Te1[(t2 >> 16) & 0xff] ^ Te2[(t3 >> 8) & 0xff] ^ Te3[t0 & 0xff] ^ rk[25]; - s2 = Te0[t2 >> 24] ^ Te1[(t3 >> 16) & 0xff] ^ Te2[(t0 >> 8) & 0xff] ^ Te3[t1 & 0xff] ^ rk[26]; - s3 = Te0[t3 >> 24] ^ Te1[(t0 >> 16) & 0xff] ^ Te2[(t1 >> 8) & 0xff] ^ Te3[t2 & 0xff] ^ rk[27]; - /* round 7: */ - t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >> 8) & 0xff] ^ Te3[s3 & 0xff] ^ rk[28]; - t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >> 8) & 0xff] ^ Te3[s0 & 0xff] ^ rk[29]; - t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >> 8) & 0xff] ^ Te3[s1 & 0xff] ^ rk[30]; - t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >> 8) & 0xff] ^ Te3[s2 & 0xff] ^ rk[31]; - /* round 8: */ - s0 = Te0[t0 >> 24] ^ Te1[(t1 >> 16) & 0xff] ^ Te2[(t2 >> 8) & 0xff] ^ Te3[t3 & 0xff] ^ rk[32]; - s1 = Te0[t1 >> 24] ^ Te1[(t2 >> 16) & 0xff] ^ Te2[(t3 >> 8) & 0xff] ^ Te3[t0 & 0xff] ^ rk[33]; - s2 = Te0[t2 >> 24] ^ Te1[(t3 >> 16) & 0xff] ^ Te2[(t0 >> 8) & 0xff] ^ Te3[t1 & 0xff] ^ rk[34]; - s3 = Te0[t3 >> 24] ^ Te1[(t0 >> 16) & 0xff] ^ Te2[(t1 >> 8) & 0xff] ^ Te3[t2 & 0xff] ^ rk[35]; - /* round 9: */ - t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >> 8) & 0xff] ^ Te3[s3 & 0xff] ^ rk[36]; - t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >> 8) & 0xff] ^ Te3[s0 & 0xff] ^ rk[37]; - t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >> 8) & 0xff] ^ Te3[s1 & 0xff] ^ rk[38]; - t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >> 8) & 0xff] ^ Te3[s2 & 0xff] ^ rk[39]; - if (Nr > 10) { - /* round 10: */ - s0 = Te0[t0 >> 24] ^ Te1[(t1 >> 16) & 0xff] ^ Te2[(t2 >> 8) & 0xff] ^ Te3[t3 & 0xff] ^ rk[40]; - s1 = Te0[t1 >> 24] ^ Te1[(t2 >> 16) & 0xff] ^ Te2[(t3 >> 8) & 0xff] ^ Te3[t0 & 0xff] ^ rk[41]; - s2 = Te0[t2 >> 24] ^ Te1[(t3 >> 16) & 0xff] ^ Te2[(t0 >> 8) & 0xff] ^ Te3[t1 & 0xff] ^ rk[42]; - s3 = Te0[t3 >> 24] ^ Te1[(t0 >> 16) & 0xff] ^ Te2[(t1 >> 8) & 0xff] ^ Te3[t2 & 0xff] ^ rk[43]; - /* round 11: */ - t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >> 8) & 0xff] ^ Te3[s3 & 0xff] ^ rk[44]; - t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >> 8) & 0xff] ^ Te3[s0 & 0xff] ^ rk[45]; - t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >> 8) & 0xff] ^ Te3[s1 & 0xff] ^ rk[46]; - t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >> 8) & 0xff] ^ Te3[s2 & 0xff] ^ rk[47]; - if (Nr > 12) { - /* round 12: */ - s0 = Te0[t0 >> 24] ^ Te1[(t1 >> 16) & 0xff] ^ Te2[(t2 >> 8) & 0xff] ^ Te3[t3 & 0xff] ^ rk[48]; - s1 = Te0[t1 >> 24] ^ Te1[(t2 >> 16) & 0xff] ^ Te2[(t3 >> 8) & 0xff] ^ Te3[t0 & 0xff] ^ rk[49]; - s2 = Te0[t2 >> 24] ^ Te1[(t3 >> 16) & 0xff] ^ Te2[(t0 >> 8) & 0xff] ^ Te3[t1 & 0xff] ^ rk[50]; - s3 = Te0[t3 >> 24] ^ Te1[(t0 >> 16) & 0xff] ^ Te2[(t1 >> 8) & 0xff] ^ Te3[t2 & 0xff] ^ rk[51]; - /* round 13: */ - t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >> 8) & 0xff] ^ Te3[s3 & 0xff] ^ rk[52]; - t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >> 8) & 0xff] ^ Te3[s0 & 0xff] ^ rk[53]; - t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >> 8) & 0xff] ^ Te3[s1 & 0xff] ^ rk[54]; - t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >> 8) & 0xff] ^ Te3[s2 & 0xff] ^ rk[55]; - } - } - rk += Nr << 2; -#else /* !FULL_UNROLL */ - /* - * Nr - 1 full rounds: - */ - r = Nr >> 1; - for (;;) { - t0 = - Te0[(s0 >> 24) ] ^ - Te1[(s1 >> 16) & 0xff] ^ - Te2[(s2 >> 8) & 0xff] ^ - Te3[(s3 ) & 0xff] ^ - rk[4]; - t1 = - Te0[(s1 >> 24) ] ^ - Te1[(s2 >> 16) & 0xff] ^ - Te2[(s3 >> 8) & 0xff] ^ - Te3[(s0 ) & 0xff] ^ - rk[5]; - t2 = - Te0[(s2 >> 24) ] ^ - Te1[(s3 >> 16) & 0xff] ^ - Te2[(s0 >> 8) & 0xff] ^ - Te3[(s1 ) & 0xff] ^ - rk[6]; - t3 = - Te0[(s3 >> 24) ] ^ - Te1[(s0 >> 16) & 0xff] ^ - Te2[(s1 >> 8) & 0xff] ^ - Te3[(s2 ) & 0xff] ^ - rk[7]; - - rk += 8; - if (--r == 0) { - break; - } - - s0 = - Te0[(t0 >> 24) ] ^ - Te1[(t1 >> 16) & 0xff] ^ - Te2[(t2 >> 8) & 0xff] ^ - Te3[(t3 ) & 0xff] ^ - rk[0]; - s1 = - Te0[(t1 >> 24) ] ^ - Te1[(t2 >> 16) & 0xff] ^ - Te2[(t3 >> 8) & 0xff] ^ - Te3[(t0 ) & 0xff] ^ - rk[1]; - s2 = - Te0[(t2 >> 24) ] ^ - Te1[(t3 >> 16) & 0xff] ^ - Te2[(t0 >> 8) & 0xff] ^ - Te3[(t1 ) & 0xff] ^ - rk[2]; - s3 = - Te0[(t3 >> 24) ] ^ - Te1[(t0 >> 16) & 0xff] ^ - Te2[(t1 >> 8) & 0xff] ^ - Te3[(t2 ) & 0xff] ^ - rk[3]; - } -#endif /* ?FULL_UNROLL */ - /* - * apply last round and - * map cipher state to byte array block: - */ - s0 = - (Te4[(t0 >> 24) ] & 0xff000000) ^ - (Te4[(t1 >> 16) & 0xff] & 0x00ff0000) ^ - (Te4[(t2 >> 8) & 0xff] & 0x0000ff00) ^ - (Te4[(t3 ) & 0xff] & 0x000000ff) ^ - rk[0]; - PUTU32(ct , s0); - s1 = - (Te4[(t1 >> 24) ] & 0xff000000) ^ - (Te4[(t2 >> 16) & 0xff] & 0x00ff0000) ^ - (Te4[(t3 >> 8) & 0xff] & 0x0000ff00) ^ - (Te4[(t0 ) & 0xff] & 0x000000ff) ^ - rk[1]; - PUTU32(ct + 4, s1); - s2 = - (Te4[(t2 >> 24) ] & 0xff000000) ^ - (Te4[(t3 >> 16) & 0xff] & 0x00ff0000) ^ - (Te4[(t0 >> 8) & 0xff] & 0x0000ff00) ^ - (Te4[(t1 ) & 0xff] & 0x000000ff) ^ - rk[2]; - PUTU32(ct + 8, s2); - s3 = - (Te4[(t3 >> 24) ] & 0xff000000) ^ - (Te4[(t0 >> 16) & 0xff] & 0x00ff0000) ^ - (Te4[(t1 >> 8) & 0xff] & 0x0000ff00) ^ - (Te4[(t2 ) & 0xff] & 0x000000ff) ^ - rk[3]; - PUTU32(ct + 12, s3); + _aes_fill_buf(cipher); } -#endif -#ifdef AES_BENCHMARK -int -main(int c, char **v) -{ - int i; - char blob[509]; /* the size of a cell payload. */ - char blob_out[509]; - aes_cnt_cipher_t *cipher = aes_new_cipher(); - aes_set_key(cipher, "aesbenchmarkkey!", 128); - memset(blob, 'z', sizeof(blob)); - - for (i=0;i<1000000; ++i) { - aes_crypt(cipher, blob, sizeof(blob), blob_out); - } - return 0; -} #endif - diff --git a/src/common/aes.h b/src/common/aes.h index eb633dbcc..04b424ec7 100644 --- a/src/common/aes.h +++ b/src/common/aes.h @@ -13,18 +13,17 @@ * \brief Headers for aes.c */ -#include "torint.h" - struct aes_cnt_cipher; typedef struct aes_cnt_cipher aes_cnt_cipher_t; -aes_cnt_cipher_t* aes_new_cipher(void); -void aes_free_cipher(aes_cnt_cipher_t *cipher); -void aes_set_key(aes_cnt_cipher_t *cipher, const char *key, int key_bits); +aes_cnt_cipher_t* aes_new_cipher(const char *key, const char *iv); +void aes_cipher_free(aes_cnt_cipher_t *cipher); void aes_crypt(aes_cnt_cipher_t *cipher, const char *input, size_t len, char *output); 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 a4e50747c..d40e5036f 100644 --- a/src/common/compat.c +++ b/src/common/compat.c @@ -16,12 +16,16 @@ * We also need it to make memmem get defined (where available) */ /* XXXX023 We should just use AC_USE_SYSTEM_EXTENSIONS in our autoconf, - * and get this (and other important stuff!) automatically */ + * and get this (and other important stuff!) automatically. Once we do that, + * make sure to also change the extern char **environ detection in + * configure.in, because whether that is declared or not depends on whether + * we have _GNU_SOURCE defined! Maybe that means that once we take this out, + * we can also take out the configure check. */ #define _GNU_SOURCE #include "compat.h" -#ifdef MS_WINDOWS +#ifdef _WIN32 #include <process.h> #include <windows.h> #include <sys/locking.h> @@ -51,6 +55,9 @@ #ifdef HAVE_ARPA_INET_H #include <arpa/inet.h> #endif +#ifdef HAVE_CRT_EXTERNS_H +#include <crt_externs.h> +#endif #ifndef HAVE_GETTIMEOFDAY #ifdef HAVE_FTIME @@ -58,6 +65,14 @@ #endif #endif +/* Includes for the process attaching prevention */ +#if defined(HAVE_SYS_PRCTL_H) && defined(__linux__) +#include <sys/prctl.h> +#elif defined(__APPLE__) +#include <sys/types.h> +#include <sys/ptrace.h> +#endif + #ifdef HAVE_NETDB_H #include <netdb.h> #endif @@ -103,6 +118,43 @@ #include "strlcat.c" #endif +/** As open(path, flags, mode), but return an fd with the close-on-exec mode + * set. */ +int +tor_open_cloexec(const char *path, int flags, unsigned mode) +{ + int fd; +#ifdef O_CLOEXEC + fd = open(path, flags|O_CLOEXEC, mode); + if (fd >= 0) + return fd; + /* If we got an error, see if it is EINVAL. EINVAL might indicate that, + * even though we were built on a system with O_CLOEXEC support, we + * are running on one without. */ + if (errno != EINVAL) + return -1; +#endif + + fd = open(path, flags, mode); +#ifdef FD_CLOEXEC + if (fd >= 0) + fcntl(fd, F_SETFD, FD_CLOEXEC); +#endif + return fd; +} + +/** DOCDOC */ +FILE * +tor_fopen_cloexec(const char *path, const char *mode) +{ + FILE *result = fopen(path, mode); +#ifdef FD_CLOEXEC + if (result != NULL) + fcntl(fileno(result), F_SETFD, FD_CLOEXEC); +#endif + return result; +} + #ifdef HAVE_SYS_MMAN_H /** Try to create a memory mapping for <b>filename</b> and return it. On * failure, return NULL. Sets errno properly, using ERANGE to mean @@ -118,7 +170,7 @@ tor_mmap_file(const char *filename) tor_assert(filename); - fd = open(filename, O_RDONLY, 0); + fd = tor_open_cloexec(filename, O_RDONLY, 0); if (fd<0) { int save_errno = errno; int severity = (errno == ENOENT) ? LOG_INFO : LOG_WARN; @@ -168,7 +220,7 @@ tor_munmap_file(tor_mmap_t *handle) munmap((char*)handle->data, handle->mapping_size); tor_free(handle); } -#elif defined(MS_WINDOWS) +#elif defined(_WIN32) tor_mmap_t * tor_mmap_file(const char *filename) { @@ -304,7 +356,7 @@ tor_vsnprintf(char *str, size_t size, const char *format, va_list args) return -1; /* no place for the NUL */ if (size > SIZE_T_CEILING) return -1; -#ifdef MS_WINDOWS +#ifdef _WIN32 r = _vsnprintf(str, size, format, args); #else r = vsnprintf(str, size, format, args); @@ -502,25 +554,43 @@ const char TOR_TOLOWER_TABLE[256] = { 240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255, }; +/** Helper for tor_strtok_r_impl: Advances cp past all characters in + * <b>sep</b>, and returns its new value. */ +static char * +strtok_helper(char *cp, const char *sep) +{ + if (sep[1]) { + while (*cp && strchr(sep, *cp)) + ++cp; + } else { + while (*cp && *cp == *sep) + ++cp; + } + return cp; +} + /** Implementation of strtok_r for platforms whose coders haven't figured out * how to write one. Hey guys! You can use this code here for free! */ char * tor_strtok_r_impl(char *str, const char *sep, char **lasts) { char *cp, *start; - if (str) + tor_assert(*sep); + if (str) { + str = strtok_helper(str, sep); + if (!*str) + return NULL; start = cp = *lasts = str; - else if (!*lasts) + } else if (!*lasts || !**lasts) { return NULL; - else + } else { start = cp = *lasts; + } - tor_assert(*sep); if (sep[1]) { while (*cp && !strchr(sep, *cp)) ++cp; } else { - tor_assert(strlen(sep) == 1); cp = strchr(cp, *sep); } @@ -528,12 +598,12 @@ tor_strtok_r_impl(char *str, const char *sep, char **lasts) *lasts = NULL; } else { *cp++ = '\0'; - *lasts = cp; + *lasts = strtok_helper(cp, sep); } return start; } -#ifdef MS_WINDOWS +#ifdef _WIN32 /** Take a filename and return a pointer to its final element. This * function is called on __FILE__ to fix a MSVC nit where __FILE__ * contains the full path to the file. This is bad, because it @@ -633,7 +703,7 @@ set_uint64(void *cp, uint64_t v) int replace_file(const char *from, const char *to) { -#ifndef MS_WINDOWS +#ifndef _WIN32 return rename(from,to); #else switch (file_status(to)) @@ -695,14 +765,14 @@ tor_lockfile_lock(const char *filename, int blocking, int *locked_out) *locked_out = 0; log_info(LD_FS, "Locking \"%s\"", filename); - fd = open(filename, O_RDWR|O_CREAT|O_TRUNC, 0600); + fd = tor_open_cloexec(filename, O_RDWR|O_CREAT|O_TRUNC, 0600); if (fd < 0) { log_warn(LD_FS,"Couldn't open \"%s\" for locking: %s", filename, strerror(errno)); return NULL; } -#ifdef WIN32 +#ifdef _WIN32 _lseek(fd, 0, SEEK_SET); if (_locking(fd, blocking ? _LK_LOCK : _LK_NBLCK, 1) < 0) { if (errno != EACCES && errno != EDEADLOCK) @@ -751,7 +821,7 @@ tor_lockfile_unlock(tor_lockfile_t *lockfile) tor_assert(lockfile); log_info(LD_FS, "Unlocking \"%s\"", lockfile->filename); -#ifdef WIN32 +#ifdef _WIN32 _lseek(lockfile->fd, 0, SEEK_SET); if (_locking(lockfile->fd, _LK_UNLCK, 1) < 0) { log_warn(LD_FS,"Error unlocking \"%s\": %s", lockfile->filename, @@ -787,7 +857,7 @@ tor_lockfile_unlock(tor_lockfile_t *lockfile) off_t tor_fd_getpos(int fd) { -#ifdef WIN32 +#ifdef _WIN32 return (off_t) _lseek(fd, 0, SEEK_CUR); #else return (off_t) lseek(fd, 0, SEEK_CUR); @@ -798,7 +868,7 @@ tor_fd_getpos(int fd) int tor_fd_seekend(int fd) { -#ifdef WIN32 +#ifdef _WIN32 return _lseek(fd, 0, SEEK_END) < 0 ? -1 : 0; #else return lseek(fd, 0, SEEK_END) < 0 ? -1 : 0; @@ -851,7 +921,7 @@ tor_close_socket(tor_socket_t s) * tor_close_socket to close sockets, and always using close() on * files. */ -#if defined(MS_WINDOWS) +#if defined(_WIN32) r = closesocket(s); #else r = close(s); @@ -872,7 +942,7 @@ tor_close_socket(tor_socket_t s) } else { int err = tor_socket_errno(-1); log_info(LD_NET, "Close returned an error: %s", tor_socket_strerror(err)); -#ifdef WIN32 +#ifdef _WIN32 if (err != WSAENOTSOCK) --n_sockets_open; #else @@ -922,13 +992,33 @@ mark_socket_open(tor_socket_t s) tor_socket_t tor_open_socket(int domain, int type, int protocol) { - tor_socket_t s = socket(domain, type, protocol); - if (SOCKET_OK(s)) { - socket_accounting_lock(); - ++n_sockets_open; - mark_socket_open(s); - socket_accounting_unlock(); - } + tor_socket_t s; +#ifdef SOCK_CLOEXEC + s = socket(domain, type|SOCK_CLOEXEC, protocol); + if (SOCKET_OK(s)) + goto socket_ok; + /* If we got an error, see if it is EINVAL. EINVAL might indicate that, + * even though we were built on a system with SOCK_CLOEXEC support, we + * are running on one without. */ + if (errno != EINVAL) + return s; +#endif /* SOCK_CLOEXEC */ + + s = socket(domain, type, protocol); + if (! SOCKET_OK(s)) + return s; + +#if defined(FD_CLOEXEC) + fcntl(s, F_SETFD, FD_CLOEXEC); +#endif + + goto socket_ok; /* So that socket_ok will not be unused. */ + + socket_ok: + socket_accounting_lock(); + ++n_sockets_open; + mark_socket_open(s); + socket_accounting_unlock(); return s; } @@ -936,13 +1026,34 @@ tor_open_socket(int domain, int type, int protocol) tor_socket_t tor_accept_socket(tor_socket_t sockfd, struct sockaddr *addr, socklen_t *len) { - tor_socket_t s = accept(sockfd, addr, len); - if (SOCKET_OK(s)) { - socket_accounting_lock(); - ++n_sockets_open; - mark_socket_open(s); - socket_accounting_unlock(); - } + tor_socket_t s; +#if defined(HAVE_ACCEPT4) && defined(SOCK_CLOEXEC) + s = accept4(sockfd, addr, len, SOCK_CLOEXEC); + if (SOCKET_OK(s)) + goto socket_ok; + /* If we got an error, see if it is ENOSYS. ENOSYS indicates that, + * even though we were built on a system with accept4 support, we + * are running on one without. Also, check for EINVAL, which indicates that + * we are missing SOCK_CLOEXEC support. */ + if (errno != EINVAL && errno != ENOSYS) + return s; +#endif + + s = accept(sockfd, addr, len); + if (!SOCKET_OK(s)) + return s; + +#if defined(FD_CLOEXEC) + fcntl(s, F_SETFD, FD_CLOEXEC); +#endif + + goto socket_ok; /* So that socket_ok will not be unused. */ + + socket_ok: + socket_accounting_lock(); + ++n_sockets_open; + mark_socket_open(s); + socket_accounting_unlock(); return s; } @@ -962,7 +1073,7 @@ get_n_open_sockets(void) void set_socket_nonblocking(tor_socket_t socket) { -#if defined(MS_WINDOWS) +#if defined(_WIN32) unsigned long nonblocking = 1; ioctlsocket(socket, FIONBIO, (unsigned long*) &nonblocking); #else @@ -991,22 +1102,45 @@ int tor_socketpair(int family, int type, int protocol, tor_socket_t fd[2]) { //don't use win32 socketpairs (they are always bad) -#if defined(HAVE_SOCKETPAIR) && !defined(MS_WINDOWS) +#if defined(HAVE_SOCKETPAIR) && !defined(_WIN32) int r; + +#ifdef SOCK_CLOEXEC + r = socketpair(family, type|SOCK_CLOEXEC, protocol, fd); + if (r == 0) + goto sockets_ok; + /* If we got an error, see if it is EINVAL. EINVAL might indicate that, + * even though we were built on a system with SOCK_CLOEXEC support, we + * are running on one without. */ + if (errno != EINVAL) + return -errno; +#endif + r = socketpair(family, type, protocol, fd); - if (r == 0) { - socket_accounting_lock(); - if (fd[0] >= 0) { - ++n_sockets_open; - mark_socket_open(fd[0]); - } - if (fd[1] >= 0) { - ++n_sockets_open; - mark_socket_open(fd[1]); - } - socket_accounting_unlock(); + if (r < 0) + return -errno; + +#if defined(FD_CLOEXEC) + if (SOCKET_OK(fd[0])) + fcntl(fd[0], F_SETFD, FD_CLOEXEC); + if (SOCKET_OK(fd[1])) + fcntl(fd[1], F_SETFD, FD_CLOEXEC); +#endif + goto sockets_ok; /* So that sockets_ok will not be unused. */ + + sockets_ok: + socket_accounting_lock(); + if (SOCKET_OK(fd[0])) { + ++n_sockets_open; + mark_socket_open(fd[0]); + } + if (SOCKET_OK(fd[1])) { + ++n_sockets_open; + mark_socket_open(fd[1]); } - return r < 0 ? -errno : r; + socket_accounting_unlock(); + + return 0; #else /* This socketpair does not work when localhost is down. So * it's really not the same thing at all. But it's close enough @@ -1026,7 +1160,7 @@ tor_socketpair(int family, int type, int protocol, tor_socket_t fd[2]) || family != AF_UNIX #endif ) { -#ifdef MS_WINDOWS +#ifdef _WIN32 return -WSAEAFNOSUPPORT; #else return -EAFNOSUPPORT; @@ -1037,7 +1171,7 @@ tor_socketpair(int family, int type, int protocol, tor_socket_t fd[2]) } listener = tor_open_socket(AF_INET, type, 0); - if (listener < 0) + if (!SOCKET_OK(listener)) return -tor_socket_errno(-1); memset(&listen_addr, 0, sizeof(listen_addr)); listen_addr.sin_family = AF_INET; @@ -1050,7 +1184,7 @@ tor_socketpair(int family, int type, int protocol, tor_socket_t fd[2]) goto tidy_up_and_fail; connector = tor_open_socket(AF_INET, type, 0); - if (connector < 0) + if (!SOCKET_OK(connector)) goto tidy_up_and_fail; /* We want to find out the port number to connect to. */ size = sizeof(connect_addr); @@ -1065,7 +1199,7 @@ tor_socketpair(int family, int type, int protocol, tor_socket_t fd[2]) size = sizeof(listen_addr); acceptor = tor_accept_socket(listener, (struct sockaddr *) &listen_addr, &size); - if (acceptor < 0) + if (!SOCKET_OK(acceptor)) goto tidy_up_and_fail; if (size != sizeof(listen_addr)) goto abort_tidy_up_and_fail; @@ -1086,7 +1220,7 @@ tor_socketpair(int family, int type, int protocol, tor_socket_t fd[2]) return 0; abort_tidy_up_and_fail: -#ifdef MS_WINDOWS +#ifdef _WIN32 saved_errno = WSAECONNABORTED; #else saved_errno = ECONNABORTED; /* I hope this is portable and appropriate. */ @@ -1128,7 +1262,7 @@ set_max_file_descriptors(rlim_t limit, int *max_out) #if defined(CYGWIN) || defined(__CYGWIN__) const char *platform = "Cygwin"; const unsigned long MAX_CONNECTIONS = 3200; -#elif defined(MS_WINDOWS) +#elif defined(_WIN32) const char *platform = "Windows"; const unsigned long MAX_CONNECTIONS = 15000; #else @@ -1211,7 +1345,7 @@ set_max_file_descriptors(rlim_t limit, int *max_out) return 0; } -#ifndef MS_WINDOWS +#ifndef _WIN32 /** Log details of current user and group credentials. Return 0 on * success. Logs and return -1 on failure. */ @@ -1288,31 +1422,19 @@ log_credential_status(void) return -1; } else { int i, retval = 0; - char *strgid; char *s = NULL; - smartlist_t *elts = smartlist_create(); + smartlist_t *elts = smartlist_new(); for (i = 0; i<ngids; i++) { - strgid = tor_malloc(11); - if (tor_snprintf(strgid, 11, "%u", (unsigned)sup_gids[i]) < 0) { - log_warn(LD_GENERAL, "Error printing supplementary GIDs"); - tor_free(strgid); - retval = -1; - goto error; - } - smartlist_add(elts, strgid); + smartlist_add_asprintf(elts, "%u", (unsigned)sup_gids[i]); } s = smartlist_join_strings(elts, " ", 0, NULL); log_fn(CREDENTIAL_LOG_LEVEL, LD_GENERAL, "Supplementary groups are: %s",s); - error: tor_free(s); - SMARTLIST_FOREACH(elts, char *, cp, - { - tor_free(cp); - }); + SMARTLIST_FOREACH(elts, char *, cp, tor_free(cp)); smartlist_free(elts); tor_free(sup_gids); @@ -1329,7 +1451,7 @@ log_credential_status(void) int switch_id(const char *user) { -#ifndef MS_WINDOWS +#ifndef _WIN32 struct passwd *pw = NULL; uid_t old_uid; gid_t old_gid; @@ -1464,6 +1586,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. */ @@ -1488,7 +1662,7 @@ get_parent_directory(char *fname) char *cp; int at_end = 1; tor_assert(fname); -#ifdef MS_WINDOWS +#ifdef _WIN32 /* If we start with, say, c:, then don't consider that the start of the path */ if (fname[0] && fname[1] == ':') { @@ -1505,7 +1679,7 @@ get_parent_directory(char *fname) at_end = 1; while (--cp > fname) { int is_sep = (*cp == '/' -#ifdef MS_WINDOWS +#ifdef _WIN32 || *cp == '\\' #endif ); @@ -1520,6 +1694,64 @@ 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 _WIN32 + char *absfname_malloced = _fullpath(NULL, fname, 1); + + /* We don't want to assume that tor_free can free a string allocated + * with malloc. On failure, return fname (it's better than nothing). */ + char *absfname = tor_strdup(absfname_malloced ? absfname_malloced : fname); + if (absfname_malloced) free(absfname_malloced); + + return absfname; +#else + char 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 +} + +#ifndef HAVE__NSGETENVIRON +#ifndef HAVE_EXTERN_ENVIRON_DECLARED +/* Some platforms declare environ under some circumstances, others don't. */ +extern char **environ; +#endif +#endif + +/** Return the current environment. This is a portable replacement for + * 'environ'. */ +char ** +get_environment(void) +{ +#ifdef HAVE__NSGETENVIRON + /* This is for compatibility between OSX versions. Otherwise (for example) + * when we do a mostly-static build on OSX 10.7, the resulting binary won't + * work on OSX 10.6. */ + return *_NSGetEnviron(); +#else + return environ; +#endif +} + /** Set *addr to the IP address (in dotted-quad notation) stored in c. * Return 1 on success, 0 if c is badly formatted. (Like inet_aton(c,addr), * but works on Windows and Solaris.) @@ -1577,7 +1809,7 @@ tor_inet_ntop(int af, const void *src, char *dst, size_t len) addr->s6_addr[12], addr->s6_addr[13], addr->s6_addr[14], addr->s6_addr[15]); } - if (strlen(buf) > len) + if ((strlen(buf) + 1) > len) /* +1 for \0 */ return NULL; strlcpy(dst, buf, len); return dst; @@ -1618,7 +1850,7 @@ tor_inet_ntop(int af, const void *src, char *dst, size_t len) } } *cp = '\0'; - if (strlen(buf) > len) + if ((strlen(buf) + 1) > len) /* +1 for \0 */ return NULL; strlcpy(dst, buf, len); return dst; @@ -1678,24 +1910,30 @@ tor_inet_pton(int af, const char *src, void *dst) return 0; if (TOR_ISXDIGIT(*src)) { char *next; + ssize_t len; long r = strtol(src, &next, 16); - if (next > 4+src) - return 0; - if (next == src) - return 0; - if (r<0 || r>65536) + tor_assert(next != NULL); + tor_assert(next != src); + + len = *next == '\0' ? eow - src : next - src; + if (len > 4) return 0; + if (len > 1 && !TOR_ISXDIGIT(src[1])) + return 0; /* 0x is not valid */ + tor_assert(r >= 0); + tor_assert(r < 65536); words[i++] = (uint16_t)r; setWords++; src = next; if (*src != ':' && src != eow) return 0; ++src; - } else if (*src == ':' && i > 0 && gapPos==-1) { + } else if (*src == ':' && i > 0 && gapPos == -1) { gapPos = i; ++src; - } else if (*src == ':' && i == 0 && src[1] == ':' && gapPos==-1) { + } else if (*src == ':' && i == 0 && src+1 < eow && src[1] == ':' && + gapPos == -1) { gapPos = i; src += 2; } else { @@ -1755,7 +1993,7 @@ tor_lookup_hostname(const char *name, uint32_t *addr) void tor_init_weak_random(unsigned seed) { -#ifdef MS_WINDOWS +#ifdef _WIN32 srand(seed); #else srandom(seed); @@ -1768,7 +2006,7 @@ tor_init_weak_random(unsigned seed) long tor_weak_random(void) { -#ifdef MS_WINDOWS +#ifdef _WIN32 return rand(); #else return random(); @@ -1792,17 +2030,14 @@ get_uname(void) #ifdef HAVE_UNAME if (uname(&u) != -1) { /* (Linux says 0 is success, Solaris says 1 is success) */ - tor_snprintf(uname_result, sizeof(uname_result), "%s %s", - u.sysname, u.machine); + strlcpy(uname_result, u.sysname, sizeof(uname_result)); } else #endif { -#ifdef MS_WINDOWS +#ifdef _WIN32 OSVERSIONINFOEX info; int i; const char *plat = NULL; - const char *extra = NULL; - char acsd[MAX_PATH] = {0}; static struct { unsigned major; unsigned minor; const char *version; } win_version_table[] = { @@ -1827,20 +2062,11 @@ get_uname(void) uname_result_is_set = 1; return uname_result; } -#ifdef UNICODE - wcstombs(acsd, info.szCSDVersion, MAX_PATH); -#else - strlcpy(acsd, info.szCSDVersion, sizeof(acsd)); -#endif if (info.dwMajorVersion == 4 && info.dwMinorVersion == 0) { if (info.dwPlatformId == VER_PLATFORM_WIN32_NT) plat = "Windows NT 4.0"; else plat = "Windows 95"; - if (acsd[1] == 'B') - extra = "OSR2 (B)"; - else if (acsd[1] == 'C') - extra = "OSR2 (C)"; } else { for (i=0; win_version_table[i].major>0; ++i) { if (win_version_table[i].major == info.dwMajorVersion && @@ -1850,39 +2076,25 @@ get_uname(void) } } } - if (plat && !strcmp(plat, "Windows 98")) { - if (acsd[1] == 'A') - extra = "SE (A)"; - else if (acsd[1] == 'B') - extra = "SE (B)"; - } if (plat) { - if (!extra) - extra = acsd; - tor_snprintf(uname_result, sizeof(uname_result), "%s %s", - plat, extra); + strlcpy(uname_result, plat, sizeof(uname_result)); } else { if (info.dwMajorVersion > 6 || (info.dwMajorVersion==6 && info.dwMinorVersion>2)) tor_snprintf(uname_result, sizeof(uname_result), - "Very recent version of Windows [major=%d,minor=%d] %s", - (int)info.dwMajorVersion,(int)info.dwMinorVersion, - acsd); + "Very recent version of Windows [major=%d,minor=%d]", + (int)info.dwMajorVersion,(int)info.dwMinorVersion); else tor_snprintf(uname_result, sizeof(uname_result), - "Unrecognized version of Windows [major=%d,minor=%d] %s", - (int)info.dwMajorVersion,(int)info.dwMinorVersion, - acsd); + "Unrecognized version of Windows [major=%d,minor=%d]", + (int)info.dwMajorVersion,(int)info.dwMinorVersion); } #if !defined (WINCE) -#ifdef VER_SUITE_BACKOFFICE - if (info.wProductType == VER_NT_DOMAIN_CONTROLLER) { - strlcat(uname_result, " [domain controller]", sizeof(uname_result)); - } else if (info.wProductType == VER_NT_SERVER) { - strlcat(uname_result, " [server]", sizeof(uname_result)); - } else if (info.wProductType == VER_NT_WORKSTATION) { - strlcat(uname_result, " [workstation]", sizeof(uname_result)); - } +#ifdef VER_NT_SERVER + if (info.wProductType == VER_NT_SERVER || + info.wProductType == VER_NT_DOMAIN_CONTROLLER) { + strlcat(uname_result, " [server]", sizeof(uname_result)); + } #endif #endif #else @@ -1995,13 +2207,59 @@ spawn_exit(void) #endif } +/** Implementation logic for compute_num_cpus(). */ +static int +compute_num_cpus_impl(void) +{ +#ifdef _WIN32 + SYSTEM_INFO info; + memset(&info, 0, sizeof(info)); + GetSystemInfo(&info); + if (info.dwNumberOfProcessors >= 1 && info.dwNumberOfProcessors < INT_MAX) + return (int)info.dwNumberOfProcessors; + else + return -1; +#elif defined(HAVE_SYSCONF) && defined(_SC_NPROCESSORS_CONF) + long cpus = sysconf(_SC_NPROCESSORS_CONF); + if (cpus >= 1 && cpus < INT_MAX) + return (int)cpus; + else + return -1; +#else + return -1; +#endif +} + +#define MAX_DETECTABLE_CPUS 16 + +/** Return how many CPUs we are running with. We assume that nobody is + * using hot-swappable CPUs, so we don't recompute this after the first + * time. Return -1 if we don't know how to tell the number of CPUs on this + * system. + */ +int +compute_num_cpus(void) +{ + static int num_cpus = -2; + if (num_cpus == -2) { + num_cpus = compute_num_cpus_impl(); + tor_assert(num_cpus != -2); + if (num_cpus > MAX_DETECTABLE_CPUS) + log_notice(LD_GENERAL, "Wow! I detected that you have %d CPUs. I " + "will not autodetect any more than %d, though. If you " + "want to configure more, set NumCPUs in your torrc", + num_cpus, MAX_DETECTABLE_CPUS); + } + return num_cpus; +} + /** Set *timeval to the current time of day. On error, log and terminate. * (Same as gettimeofday(timeval,NULL), but never returns -1.) */ void tor_gettimeofday(struct timeval *timeval) { -#ifdef MS_WINDOWS +#ifdef _WIN32 /* Epoch bias copied from perl: number of units between windows epoch and * Unix epoch. */ #define EPOCH_BIAS U64_LITERAL(116444736000000000) @@ -2046,7 +2304,7 @@ tor_gettimeofday(struct timeval *timeval) return; } -#if defined(TOR_IS_MULTITHREADED) && !defined(MS_WINDOWS) +#if defined(TOR_IS_MULTITHREADED) && !defined(_WIN32) /** Defined iff we need to add locks when defining fake versions of reentrant * versions of time-related functions. */ #define TIME_FNS_NEED_LOCKS @@ -2396,7 +2654,7 @@ tor_cond_new(void) { tor_cond_t *cond = tor_malloc_zero(sizeof(tor_cond_t)); InitializeCriticalSection(&cond->mutex); - cond->events = smartlist_create(); + cond->events = smartlist_new(); return cond; } void @@ -2590,7 +2848,7 @@ in_main_thread(void) * should call tor_socket_errno <em>at most once</em> on the failing * socket to get the error. */ -#if defined(MS_WINDOWS) +#if defined(_WIN32) int tor_socket_errno(tor_socket_t sock) { @@ -2606,7 +2864,7 @@ tor_socket_errno(tor_socket_t sock) } #endif -#if defined(MS_WINDOWS) +#if defined(_WIN32) #define E(code, s) { code, (s " [" #code " ]") } struct { int code; const char *msg; } windows_socket_errors[] = { E(WSAEINTR, "Interrupted function call"), @@ -2688,7 +2946,7 @@ tor_socket_strerror(int e) int network_init(void) { -#ifdef MS_WINDOWS +#ifdef _WIN32 /* This silly exercise is necessary before windows will allow * gethostbyname to work. */ WSADATA WSAData; @@ -2698,6 +2956,11 @@ network_init(void) log_warn(LD_NET,"Error initializing windows network layer: code was %d",r); return -1; } + if (sizeof(SOCKET) != sizeof(tor_socket_t)) { + log_warn(LD_BUG,"The tor_socket_t type does not match SOCKET in size; Tor " + "might not work. (Sizes are %d and %d respectively.)", + (int)sizeof(tor_socket_t), (int)sizeof(SOCKET)); + } /* WSAData.iMaxSockets might show the max sockets we're allowed to use. * We might use it to complain if we're trying to be a server but have * too few sockets available. */ @@ -2705,7 +2968,7 @@ network_init(void) return 0; } -#ifdef MS_WINDOWS +#ifdef _WIN32 /** Return a newly allocated string describing the windows system error code * <b>err</b>. Note that error codes are different from errno. Error codes * come from GetLastError() when a winapi call fails. errno is set only when diff --git a/src/common/compat.h b/src/common/compat.h index fc70caf50..7edd889ee 100644 --- a/src/common/compat.h +++ b/src/common/compat.h @@ -8,9 +8,10 @@ #include "orconfig.h" #include "torint.h" -#ifdef MS_WINDOWS -#define WIN32_WINNT 0x400 -#define _WIN32_WINNT 0x400 +#ifdef _WIN32 +#ifndef _WIN32_WINNT +#define _WIN32_WINNT 0x0501 +#endif #define WIN32_LEAN_AND_MEAN #if defined(_MSC_VER) && (_MSC_VER < 1300) #include <winsock.h> @@ -31,7 +32,7 @@ #ifdef HAVE_STRING_H #include <string.h> #endif -#if defined(HAVE_PTHREAD_H) && !defined(MS_WINDOWS) +#if defined(HAVE_PTHREAD_H) && !defined(_WIN32) #include <pthread.h> #endif #include <stdarg.h> @@ -41,9 +42,6 @@ #ifdef HAVE_SYS_SOCKET_H #include <sys/socket.h> #endif -#ifdef HAVE_SYS_TYPES_H -#include <sys/types.h> -#endif #ifdef HAVE_NETINET_IN_H #include <netinet/in.h> #endif @@ -51,6 +49,8 @@ #include <netinet6/in6.h> #endif +#include <stdio.h> + #if defined (WINCE) #include <fcntl.h> #include <io.h> @@ -86,7 +86,7 @@ #endif /* inline is __inline on windows. */ -#ifdef MS_WINDOWS +#ifdef _WIN32 #define INLINE __inline #else #define INLINE inline @@ -132,7 +132,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)) @@ -165,7 +164,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 @@ -195,7 +193,7 @@ extern INLINE double U64_TO_DBL(uint64_t x) { #endif /* ===== String compatibility */ -#ifdef MS_WINDOWS +#ifdef _WIN32 /* Windows names string functions differently from most other platforms. */ #define strncasecmp _strnicmp #define strcasecmp _stricmp @@ -248,7 +246,7 @@ typedef struct tor_mmap_t { #ifdef HAVE_SYS_MMAN_H size_t mapping_size; /**< Size of the actual mapping. (This is this file * size, rounded up to the nearest page.) */ -#elif defined MS_WINDOWS +#elif defined _WIN32 HANDLE file_handle; HANDLE mmap_handle; #endif @@ -269,9 +267,9 @@ int tor_vasprintf(char **strp, const char *fmt, va_list args) CHECK_PRINTF(2,0); 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) { @@ -307,7 +305,7 @@ char *tor_strtok_r_impl(char *str, const char *sep, char **lasts); #define tor_strtok_r(str, sep, lasts) tor_strtok_r_impl(str, sep, lasts) #endif -#ifdef MS_WINDOWS +#ifdef _WIN32 #define _SHORT_FILE_ (tor_fix_source_file(__FILE__)) const char *tor_fix_source_file(const char *fname); #else @@ -372,6 +370,9 @@ struct tm *tor_gmtime_r(const time_t *timep, struct tm *result); #endif /* ===== File compatibility */ +int tor_open_cloexec(const char *path, int flags, unsigned mode); +FILE *tor_fopen_cloexec(const char *path, const char *mode); + int replace_file(const char *from, const char *to); int touch_file(const char *fname); @@ -383,7 +384,7 @@ void tor_lockfile_unlock(tor_lockfile_t *lockfile); off_t tor_fd_getpos(int fd); int tor_fd_seekend(int fd); -#ifdef MS_WINDOWS +#ifdef _WIN32 #define PATH_SEPARATOR "\\" #else #define PATH_SEPARATOR "/" @@ -395,12 +396,20 @@ int tor_fd_seekend(int fd); typedef int socklen_t; #endif -#ifdef MS_WINDOWS +#ifdef _WIN32 +/* XXX Actually, this should arguably be SOCKET; we use intptr_t here so that + * any inadvertant checks for the socket being <= 0 or > 0 will probably + * still work. */ #define tor_socket_t intptr_t #define SOCKET_OK(s) ((SOCKET)(s) != INVALID_SOCKET) +#define TOR_INVALID_SOCKET INVALID_SOCKET #else +/** Type used for a network socket. */ #define tor_socket_t int +/** Macro: true iff 's' is a possible value for a valid initialized socket. */ #define SOCKET_OK(s) ((s) >= 0) +/** Error/uninitialized value for a tor_socket_t. */ +#define TOR_INVALID_SOCKET (-1) #endif int tor_close_socket(tor_socket_t s); @@ -488,7 +497,7 @@ int network_init(void); * errnos against expected values, and use tor_socket_errno to find * the actual errno after a socket operation fails. */ -#if defined(MS_WINDOWS) +#if defined(_WIN32) /** Return true if e is EAGAIN or the local equivalent. */ #define ERRNO_IS_EAGAIN(e) ((e) == EAGAIN || (e) == WSAEWOULDBLOCK) /** Return true if e is EINPROGRESS or the local equivalent. */ @@ -541,9 +550,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)); @@ -561,17 +570,21 @@ 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); + +char **get_environment(void); int spawn_func(void (*func)(void *), void *data); void spawn_exit(void) ATTR_NORETURN; -#if defined(ENABLE_THREADS) && defined(MS_WINDOWS) +#if defined(ENABLE_THREADS) && defined(_WIN32) #define USE_WIN32_THREADS #define TOR_IS_MULTITHREADED 1 #elif (defined(ENABLE_THREADS) && defined(HAVE_PTHREAD_H) && \ @@ -582,6 +595,8 @@ void spawn_exit(void) ATTR_NORETURN; #undef TOR_IS_MULTITHREADED #endif +int compute_num_cpus(void); + /* Because we use threads instead of processes on most platforms (Windows, * Linux, etc), we need locking for them. On platforms with poor thread * support or broken gethostbyname_r, these functions are no-ops. */ @@ -651,14 +666,14 @@ void tor_cond_signal_all(tor_cond_t *cond); #endif /* Platform-specific helpers. */ -#ifdef MS_WINDOWS +#ifdef _WIN32 char *format_win32_error(DWORD err); #endif /*for some reason my compiler doesn't have these version flags defined a nice homework assignment for someone one day is to define the rest*/ //these are the values as given on MSDN -#ifdef MS_WINDOWS +#ifdef _WIN32 #ifndef VER_SUITE_EMBEDDEDNT #define VER_SUITE_EMBEDDEDNT 0x00000040 diff --git a/src/common/compat_libevent.c b/src/common/compat_libevent.c index ddb2da68a..70e3baf9d 100644 --- a/src/common/compat_libevent.c +++ b/src/common/compat_libevent.c @@ -19,6 +19,10 @@ #ifdef HAVE_EVENT2_EVENT_H #include <event2/event.h> +#include <event2/thread.h> +#ifdef USE_BUFFEREVENTS +#include <event2/bufferevent.h> +#endif #else #include <event.h> #endif @@ -163,11 +167,24 @@ struct event_base *the_event_base = NULL; #endif #endif +#ifdef USE_BUFFEREVENTS +static int using_iocp_bufferevents = 0; +static void tor_libevent_set_tick_timeout(int msec_per_tick); + +int +tor_libevent_using_iocp_bufferevents(void) +{ + return using_iocp_bufferevents; +} +#endif + /** Initialize the Libevent library and set up the event base. */ void -tor_libevent_initialize(void) +tor_libevent_initialize(tor_libevent_cfg *torcfg) { tor_assert(the_event_base == NULL); + /* some paths below don't use torcfg, so avoid unused variable warnings */ + (void)torcfg; #ifdef __APPLE__ if (MACOSX_KQUEUE_IS_BROKEN || @@ -178,17 +195,65 @@ tor_libevent_initialize(void) #ifdef HAVE_EVENT2_EVENT_H { - struct event_config *cfg = event_config_new(); + int attempts = 0; + int using_threads; + struct event_config *cfg; + + retry: + ++attempts; + using_threads = 0; + cfg = event_config_new(); tor_assert(cfg); - /* In 0.2.2, we don't use locking at all. Telling Libevent not to try to - * turn it on can avoid a needless socketpair() attempt. - */ - event_config_set_flag(cfg, EVENT_BASE_FLAG_NOLOCK); +#if defined(_WIN32) && defined(USE_BUFFEREVENTS) + if (! torcfg->disable_iocp) { + evthread_use_windows_threads(); + event_config_set_flag(cfg, EVENT_BASE_FLAG_STARTUP_IOCP); + using_iocp_bufferevents = 1; + using_threads = 1; + } else { + using_iocp_bufferevents = 0; + } +#endif + + if (!using_threads) { + /* Telling Libevent not to try to turn locking on can avoid a needless + * socketpair() attempt. */ + event_config_set_flag(cfg, EVENT_BASE_FLAG_NOLOCK); + } + +#if defined(LIBEVENT_VERSION_NUMBER) && LIBEVENT_VERSION_NUMBER >= V(2,0,7) + if (torcfg->num_cpus > 0) + event_config_set_num_cpus_hint(cfg, torcfg->num_cpus); +#endif + +#if LIBEVENT_VERSION_NUMBER >= V(2,0,9) + /* We can enable changelist support with epoll, since we don't give + * Libevent any dup'd fds. This lets us avoid some syscalls. */ + event_config_set_flag(cfg, EVENT_BASE_FLAG_EPOLL_USE_CHANGELIST); +#endif the_event_base = event_base_new_with_config(cfg); event_config_free(cfg); + + if (using_threads && the_event_base == NULL && attempts < 2) { + /* This could be a socketpair() failure, which can happen sometimes on + * windows boxes with obnoxious firewall rules. Downgrade and try + * again. */ +#if defined(_WIN32) && defined(USE_BUFFEREVENTS) + if (torcfg->disable_iocp == 0) { + log_warn(LD_GENERAL, "Unable to initialize Libevent. Trying again " + "with IOCP disabled."); + } else +#endif + { + log_warn(LD_GENERAL, "Unable to initialize Libevent. Trying again."); + } + + torcfg->disable_iocp = 1; + goto retry; + } } #else the_event_base = event_init(); @@ -212,6 +277,10 @@ tor_libevent_initialize(void) "You have a *VERY* old version of libevent. It is likely to be buggy; " "please build Tor with a more recent version."); #endif + +#ifdef USE_BUFFEREVENTS + tor_libevent_set_tick_timeout(torcfg->msec_per_tick); +#endif } /** Return the current Libevent event base that we're set up to use. */ @@ -257,7 +326,7 @@ tor_decode_libevent_version(const char *v) /* Try the new preferred "1.4.11-stable" format. * Also accept "1.4.14b-stable". */ - fields = sscanf(v, "%u.%u.%u%c%c", &major, &minor, &patchlevel, &c, &e); + fields = tor_sscanf(v, "%u.%u.%u%c%c", &major, &minor, &patchlevel, &c, &e); if (fields == 3 || ((fields == 4 || fields == 5 ) && (c == '-' || c == '_')) || (fields == 5 && TOR_ISALPHA(c) && (e == '-' || e == '_'))) { @@ -265,7 +334,7 @@ tor_decode_libevent_version(const char *v) } /* Try the old "1.3e" format. */ - fields = sscanf(v, "%u.%u%c%c", &major, &minor, &c, &extra); + fields = tor_sscanf(v, "%u.%u%c%c", &major, &minor, &c, &extra); if (fields == 3 && TOR_ISALPHA(c)) { return V_OLD(major, minor, c); } else if (fields == 2) { @@ -569,3 +638,55 @@ periodic_timer_free(periodic_timer_t *timer) tor_free(timer); } +#ifdef USE_BUFFEREVENTS +static const struct timeval *one_tick = NULL; +/** + * Return a special timeout to be passed whenever libevent's O(1) timeout + * implementation should be used. Only use this when the timer is supposed + * to fire after msec_per_tick ticks have elapsed. +*/ +const struct timeval * +tor_libevent_get_one_tick_timeout(void) +{ + tor_assert(one_tick); + return one_tick; +} + +/** Initialize the common timeout that we'll use to refill the buckets every + * time a tick elapses. */ +static void +tor_libevent_set_tick_timeout(int msec_per_tick) +{ + struct event_base *base = tor_libevent_get_base(); + struct timeval tv; + + tor_assert(! one_tick); + tv.tv_sec = msec_per_tick / 1000; + tv.tv_usec = (msec_per_tick % 1000) * 1000; + one_tick = event_base_init_common_timeout(base, &tv); +} + +static struct bufferevent * +tor_get_root_bufferevent(struct bufferevent *bev) +{ + struct bufferevent *u; + while ((u = bufferevent_get_underlying(bev)) != NULL) + bev = u; + return bev; +} + +int +tor_set_bufferevent_rate_limit(struct bufferevent *bev, + struct ev_token_bucket_cfg *cfg) +{ + return bufferevent_set_rate_limit(tor_get_root_bufferevent(bev), cfg); +} + +int +tor_add_bufferevent_to_rate_limit_group(struct bufferevent *bev, + struct bufferevent_rate_limit_group *g) +{ + return bufferevent_add_to_rate_limit_group(tor_get_root_bufferevent(bev), g); +} +#endif + diff --git a/src/common/compat_libevent.h b/src/common/compat_libevent.h index 89b256396..024729717 100644 --- a/src/common/compat_libevent.h +++ b/src/common/compat_libevent.h @@ -8,6 +8,11 @@ struct event; struct event_base; +#ifdef USE_BUFFEREVENTS +struct bufferevent; +struct ev_token_bucket_cfg; +struct bufferevent_rate_limit_group; +#endif #ifdef HAVE_EVENT2_EVENT_H #include <event2/util.h> @@ -54,7 +59,13 @@ struct timeval; int tor_event_base_loopexit(struct event_base *base, struct timeval *tv); #endif -void tor_libevent_initialize(void); +typedef struct tor_libevent_cfg { + int disable_iocp; + int num_cpus; + int msec_per_tick; +} tor_libevent_cfg; + +void tor_libevent_initialize(tor_libevent_cfg *cfg); struct event_base *tor_libevent_get_base(void); const char *tor_libevent_get_method(void); void tor_check_libevent_version(const char *m, int server, @@ -62,5 +73,14 @@ void tor_check_libevent_version(const char *m, int server, void tor_check_libevent_header_compatibility(void); const char *tor_libevent_get_version_str(void); +#ifdef USE_BUFFEREVENTS +const struct timeval *tor_libevent_get_one_tick_timeout(void); +int tor_libevent_using_iocp_bufferevents(void); +int tor_set_bufferevent_rate_limit(struct bufferevent *bev, + struct ev_token_bucket_cfg *cfg); +int tor_add_bufferevent_to_rate_limit_group(struct bufferevent *bev, + struct bufferevent_rate_limit_group *g); +#endif + #endif diff --git a/src/common/container.c b/src/common/container.c index 5f5322237..23ec9d3c8 100644 --- a/src/common/container.c +++ b/src/common/container.c @@ -29,7 +29,7 @@ /** Allocate and return an empty smartlist. */ smartlist_t * -smartlist_create(void) +smartlist_new(void) { smartlist_t *sl = tor_malloc(sizeof(smartlist_t)); sl->num_used = 0; @@ -296,7 +296,6 @@ smartlist_subtract(smartlist_t *sl1, const smartlist_t *sl2) /** Remove the <b>idx</b>th element of sl; if idx is not the last * element, swap the last element of sl into the <b>idx</b>th space. - * Return the old value of the <b>idx</b>th element. */ void smartlist_del(smartlist_t *sl, int idx) @@ -1028,7 +1027,7 @@ digestmap_set(digestmap_t *map, const char *key, void *val) * the hash table that we do in the unoptimized code above. (Each of * HT_INSERT and HT_FIND calls HT_SET_HASH and HT_FIND_P.) */ - _HT_FIND_OR_INSERT(digestmap_impl, node, digestmap_entry_hash, &(map->head), + HT_FIND_OR_INSERT_(digestmap_impl, node, digestmap_entry_hash, &(map->head), digestmap_entry_t, &search, ptr, { /* we found an entry. */ @@ -1042,7 +1041,7 @@ digestmap_set(digestmap_t *map, const char *key, void *val) tor_malloc_zero(sizeof(digestmap_entry_t)); memcpy(newent->key, key, DIGEST_LEN); newent->val = val; - _HT_FOI_INSERT(node, &(map->head), &search, newent, ptr); + HT_FOI_INSERT_(node, &(map->head), &search, newent, ptr); return NULL; }); #endif @@ -1355,14 +1354,14 @@ digestmap_free(digestmap_t *map, void (*free_val)(void*)) void strmap_assert_ok(const strmap_t *map) { - tor_assert(!_strmap_impl_HT_REP_IS_BAD(&map->head)); + tor_assert(!strmap_impl_HT_REP_IS_BAD_(&map->head)); } /** Fail with an assertion error if anything has gone wrong with the internal * representation of <b>map</b>. */ void digestmap_assert_ok(const digestmap_t *map) { - tor_assert(!_digestmap_impl_HT_REP_IS_BAD(&map->head)); + tor_assert(!digestmap_impl_HT_REP_IS_BAD_(&map->head)); } /** Return true iff <b>map</b> has no entries. */ diff --git a/src/common/container.h b/src/common/container.h index 4a6eba789..4e14ab4e3 100644 --- a/src/common/container.h +++ b/src/common/container.h @@ -26,7 +26,7 @@ typedef struct smartlist_t { /** @} */ } smartlist_t; -smartlist_t *smartlist_create(void); +smartlist_t *smartlist_new(void); void smartlist_free(smartlist_t *sl); void smartlist_clear(smartlist_t *sl); void smartlist_add(smartlist_t *sl, void *element); @@ -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 88e48ef84..8feac95ac 100644 --- a/src/common/crypto.c +++ b/src/common/crypto.c @@ -12,9 +12,10 @@ #include "orconfig.h" -#ifdef MS_WINDOWS -#define WIN32_WINNT 0x400 -#define _WIN32_WINNT 0x400 +#ifdef _WIN32 +#ifndef _WIN32_WINNT +#define _WIN32_WINNT 0x0501 +#endif #define WIN32_LEAN_AND_MEAN #include <windows.h> #include <wincrypt.h> @@ -56,18 +57,19 @@ #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 -#include <openssl/engine.h> - #ifdef ANDROID /* Android's OpenSSL seems to have removed all of its Engine support. */ #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 @@ -104,23 +106,24 @@ static int _n_openssl_mutexes = 0; #endif /** A public key, or a public/private key-pair. */ -struct crypto_pk_env_t +struct crypto_pk_t { int refs; /**< reference count, so we don't have to copy keys */ RSA *key; /**< The key itself */ }; /** Key and stream information for a stream cipher. */ -struct crypto_cipher_env_t +struct crypto_cipher_t { char key[CIPHER_KEY_LEN]; /**< The raw key. */ + char iv[CIPHER_IV_LEN]; /**< The initial IV. */ aes_cnt_cipher_t *cipher; /**< The key in format usable for counter-mode AES * encryption */ }; /** A structure to hold the first half (x, g^x) of a Diffie-Hellman handshake * while we're waiting for the second.*/ -struct crypto_dh_env_t { +struct crypto_dh_t { DH *dh; /**< The openssl DH object */ }; @@ -134,7 +137,6 @@ crypto_get_rsa_padding_overhead(int padding) { switch (padding) { - case RSA_NO_PADDING: return 0; case RSA_PKCS1_OAEP_PADDING: return 42; case RSA_PKCS1_PADDING: return 11; default: tor_assert(0); return -1; @@ -148,7 +150,6 @@ crypto_get_rsa_padding(int padding) { switch (padding) { - case PK_NO_PADDING: return RSA_NO_PADDING; case PK_PKCS1_PADDING: return RSA_PKCS1_PADDING; case PK_PKCS1_OAEP_PADDING: return RSA_PKCS1_OAEP_PADDING; default: tor_assert(0); return -1; @@ -274,6 +275,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; @@ -286,61 +291,30 @@ 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) +/** used by tortls.c: wrap an RSA* in a crypto_pk_t. */ +crypto_pk_t * +_crypto_new_pk_from_rsa(RSA *rsa) { - crypto_pk_env_t *env; + crypto_pk_t *env; tor_assert(rsa); - env = tor_malloc(sizeof(crypto_pk_env_t)); + env = tor_malloc(sizeof(crypto_pk_t)); env->refs = 1; env->key = rsa; return env; } /** Helper, used by tor-checkkey.c and tor-gencert.c. Return the RSA from a - * crypto_pk_env_t. */ + * crypto_pk_t. */ RSA * -_crypto_pk_env_get_rsa(crypto_pk_env_t *env) +_crypto_pk_get_rsa(crypto_pk_t *env) { return env->key; } -/** used by tortls.c: get an equivalent EVP_PKEY* for a crypto_pk_env_t. Iff +/** used by tortls.c: get an equivalent EVP_PKEY* for a crypto_pk_t. Iff * private is set, include the private-key portion of the key. */ EVP_PKEY * -_crypto_pk_env_get_evp_pkey(crypto_pk_env_t *env, int private) +_crypto_pk_get_evp_pkey(crypto_pk_t *env, int private) { RSA *key = NULL; EVP_PKEY *pkey = NULL; @@ -365,10 +339,10 @@ _crypto_pk_env_get_evp_pkey(crypto_pk_env_t *env, int private) return NULL; } -/** Used by tortls.c: Get the DH* from a crypto_dh_env_t. +/** Used by tortls.c: Get the DH* from a crypto_dh_t. */ DH * -_crypto_dh_env_get_dh(crypto_dh_env_t *dh) +_crypto_dh_get_dh(crypto_dh_t *dh) { return dh->dh; } @@ -376,21 +350,21 @@ _crypto_dh_env_get_dh(crypto_dh_env_t *dh) /** Allocate and return storage for a public key. The key itself will not yet * be set. */ -crypto_pk_env_t * -crypto_new_pk_env(void) +crypto_pk_t * +crypto_pk_new(void) { RSA *rsa; rsa = RSA_new(); tor_assert(rsa); - return _crypto_new_pk_env_rsa(rsa); + return _crypto_new_pk_from_rsa(rsa); } /** Release a reference to an asymmetric key; when all the references * are released, free the key. */ void -crypto_free_pk_env(crypto_pk_env_t *env) +crypto_pk_free(crypto_pk_t *env) { if (!env) return; @@ -405,61 +379,50 @@ crypto_free_pk_env(crypto_pk_env_t *env) tor_free(env); } -/** Create a new symmetric cipher for a given key and encryption flag - * (1=encrypt, 0=decrypt). Return the crypto object on success; NULL - * on failure. +/** Allocate and return a new symmetric cipher using the provided key and iv. + * The key is CIPHER_KEY_LEN bytes; the IV is CIPHER_IV_LEN bytes. If you + * provide NULL in place of either one, it is generated at random. */ -crypto_cipher_env_t * -crypto_create_init_cipher(const char *key, int encrypt_mode) +crypto_cipher_t * +crypto_cipher_new_with_iv(const char *key, const char *iv) { - int r; - crypto_cipher_env_t *crypto = NULL; + crypto_cipher_t *env; - if (! (crypto = crypto_new_cipher_env())) { - log_warn(LD_CRYPTO, "Unable to allocate crypto object"); - return NULL; - } - - crypto_cipher_set_key(crypto, key); + env = tor_malloc_zero(sizeof(crypto_cipher_t)); - if (encrypt_mode) - r = crypto_cipher_encrypt_init_cipher(crypto); + if (key == NULL) + crypto_rand(env->key, CIPHER_KEY_LEN); else - r = crypto_cipher_decrypt_init_cipher(crypto); + memcpy(env->key, key, CIPHER_KEY_LEN); + if (iv == NULL) + crypto_rand(env->iv, CIPHER_IV_LEN); + else + memcpy(env->iv, iv, CIPHER_IV_LEN); - if (r) - goto error; - return crypto; + env->cipher = aes_new_cipher(env->key, env->iv); - error: - if (crypto) - crypto_free_cipher_env(crypto); - return NULL; + return env; } -/** Allocate and return a new symmetric cipher. - */ -crypto_cipher_env_t * -crypto_new_cipher_env(void) +crypto_cipher_t * +crypto_cipher_new(const char *key) { - crypto_cipher_env_t *env; - - env = tor_malloc_zero(sizeof(crypto_cipher_env_t)); - env->cipher = aes_new_cipher(); - return env; + char zeroiv[CIPHER_IV_LEN]; + memset(zeroiv, 0, sizeof(zeroiv)); + return crypto_cipher_new_with_iv(key, zeroiv); } /** Free a symmetric cipher. */ void -crypto_free_cipher_env(crypto_cipher_env_t *env) +crypto_cipher_free(crypto_cipher_t *env) { if (!env) return; tor_assert(env->cipher); - aes_free_cipher(env->cipher); - memset(env, 0, sizeof(crypto_cipher_env_t)); + aes_cipher_free(env->cipher); + memset(env, 0, sizeof(crypto_cipher_t)); tor_free(env); } @@ -469,13 +432,13 @@ crypto_free_cipher_env(crypto_cipher_env_t *env) * Return 0 on success, -1 on failure. */ int -crypto_pk_generate_key_with_bits(crypto_pk_env_t *env, int bits) +crypto_pk_generate_key_with_bits(crypto_pk_t *env, int bits) { tor_assert(env); 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 @@ -516,7 +479,7 @@ crypto_pk_generate_key_with_bits(crypto_pk_env_t *env, int bits) */ /* Used here, and used for testing. */ int -crypto_pk_read_private_key_from_string(crypto_pk_env_t *env, +crypto_pk_read_private_key_from_string(crypto_pk_t *env, const char *s, ssize_t len) { BIO *b; @@ -548,7 +511,7 @@ crypto_pk_read_private_key_from_string(crypto_pk_env_t *env, * <b>keyfile</b> into <b>env</b>. Return 0 on success, -1 on failure. */ int -crypto_pk_read_private_key_from_filename(crypto_pk_env_t *env, +crypto_pk_read_private_key_from_filename(crypto_pk_t *env, const char *keyfile) { char *contents; @@ -577,7 +540,7 @@ crypto_pk_read_private_key_from_filename(crypto_pk_env_t *env, /** Helper function to implement crypto_pk_write_*_key_to_string. */ static int -crypto_pk_write_key_to_string_impl(crypto_pk_env_t *env, char **dest, +crypto_pk_write_key_to_string_impl(crypto_pk_t *env, char **dest, size_t *len, int is_public) { BUF_MEM *buf; @@ -625,7 +588,7 @@ crypto_pk_write_key_to_string_impl(crypto_pk_env_t *env, char **dest, * failure, return -1. */ int -crypto_pk_write_public_key_to_string(crypto_pk_env_t *env, char **dest, +crypto_pk_write_public_key_to_string(crypto_pk_t *env, char **dest, size_t *len) { return crypto_pk_write_key_to_string_impl(env, dest, len, 1); @@ -637,7 +600,7 @@ crypto_pk_write_public_key_to_string(crypto_pk_env_t *env, char **dest, * failure, return -1. */ int -crypto_pk_write_private_key_to_string(crypto_pk_env_t *env, char **dest, +crypto_pk_write_private_key_to_string(crypto_pk_t *env, char **dest, size_t *len) { return crypto_pk_write_key_to_string_impl(env, dest, len, 0); @@ -648,7 +611,7 @@ crypto_pk_write_private_key_to_string(crypto_pk_env_t *env, char **dest, * failure. */ int -crypto_pk_read_public_key_from_string(crypto_pk_env_t *env, const char *src, +crypto_pk_read_public_key_from_string(crypto_pk_t *env, const char *src, size_t len) { BIO *b; @@ -679,7 +642,7 @@ crypto_pk_read_public_key_from_string(crypto_pk_env_t *env, const char *src, * PEM-encoded. Return 0 on success, -1 on failure. */ int -crypto_pk_write_private_key_to_filename(crypto_pk_env_t *env, +crypto_pk_write_private_key_to_filename(crypto_pk_t *env, const char *fname) { BIO *bio; @@ -713,7 +676,7 @@ crypto_pk_write_private_key_to_filename(crypto_pk_env_t *env, /** Return true iff <b>env</b> has a valid key. */ int -crypto_pk_check_key(crypto_pk_env_t *env) +crypto_pk_check_key(crypto_pk_t *env) { int r; tor_assert(env); @@ -727,7 +690,7 @@ crypto_pk_check_key(crypto_pk_env_t *env) /** Return true iff <b>key</b> contains the private-key portion of the RSA * key. */ int -crypto_pk_key_is_private(const crypto_pk_env_t *key) +crypto_pk_key_is_private(const crypto_pk_t *key) { tor_assert(key); return PRIVATE_KEY_OK(key); @@ -737,7 +700,7 @@ crypto_pk_key_is_private(const crypto_pk_env_t *key) * equals 65537. */ int -crypto_pk_public_exponent_ok(crypto_pk_env_t *env) +crypto_pk_public_exponent_ok(crypto_pk_t *env) { tor_assert(env); tor_assert(env->key); @@ -749,7 +712,7 @@ crypto_pk_public_exponent_ok(crypto_pk_env_t *env) * if a==b, and 1 if a\>b. */ int -crypto_pk_cmp_keys(crypto_pk_env_t *a, crypto_pk_env_t *b) +crypto_pk_cmp_keys(crypto_pk_t *a, crypto_pk_t *b) { int result; @@ -769,7 +732,7 @@ crypto_pk_cmp_keys(crypto_pk_env_t *a, crypto_pk_env_t *b) /** Return the size of the public key modulus in <b>env</b>, in bytes. */ size_t -crypto_pk_keysize(crypto_pk_env_t *env) +crypto_pk_keysize(crypto_pk_t *env) { tor_assert(env); tor_assert(env->key); @@ -779,7 +742,7 @@ crypto_pk_keysize(crypto_pk_env_t *env) /** Return the size of the public key modulus of <b>env</b>, in bits. */ int -crypto_pk_num_bits(crypto_pk_env_t *env) +crypto_pk_num_bits(crypto_pk_t *env) { tor_assert(env); tor_assert(env->key); @@ -790,8 +753,8 @@ crypto_pk_num_bits(crypto_pk_env_t *env) /** Increase the reference count of <b>env</b>, and return it. */ -crypto_pk_env_t * -crypto_pk_dup_key(crypto_pk_env_t *env) +crypto_pk_t * +crypto_pk_dup_key(crypto_pk_t *env) { tor_assert(env); tor_assert(env->key); @@ -801,8 +764,8 @@ crypto_pk_dup_key(crypto_pk_env_t *env) } /** Make a real honest-to-goodness copy of <b>env</b>, and return it. */ -crypto_pk_env_t * -crypto_pk_copy_full(crypto_pk_env_t *env) +crypto_pk_t * +crypto_pk_copy_full(crypto_pk_t *env) { RSA *new_key; int privatekey = 0; @@ -825,7 +788,7 @@ crypto_pk_copy_full(crypto_pk_env_t *env) return NULL; } - return _crypto_new_pk_env_rsa(new_key); + return _crypto_new_pk_from_rsa(new_key); } /** Encrypt <b>fromlen</b> bytes from <b>from</b> with the public key @@ -837,7 +800,7 @@ crypto_pk_copy_full(crypto_pk_env_t *env) * at least the length of the modulus of <b>env</b>. */ int -crypto_pk_public_encrypt(crypto_pk_env_t *env, char *to, size_t tolen, +crypto_pk_public_encrypt(crypto_pk_t *env, char *to, size_t tolen, const char *from, size_t fromlen, int padding) { int r; @@ -866,7 +829,7 @@ crypto_pk_public_encrypt(crypto_pk_env_t *env, char *to, size_t tolen, * at least the length of the modulus of <b>env</b>. */ int -crypto_pk_private_decrypt(crypto_pk_env_t *env, char *to, +crypto_pk_private_decrypt(crypto_pk_t *env, char *to, size_t tolen, const char *from, size_t fromlen, int padding, int warnOnFailure) @@ -903,7 +866,7 @@ crypto_pk_private_decrypt(crypto_pk_env_t *env, char *to, * at least the length of the modulus of <b>env</b>. */ int -crypto_pk_public_checksig(crypto_pk_env_t *env, char *to, +crypto_pk_public_checksig(crypto_pk_t *env, char *to, size_t tolen, const char *from, size_t fromlen) { @@ -930,7 +893,7 @@ crypto_pk_public_checksig(crypto_pk_env_t *env, char *to, * SHA1(data). Else return -1. */ int -crypto_pk_public_checksig_digest(crypto_pk_env_t *env, const char *data, +crypto_pk_public_checksig_digest(crypto_pk_t *env, const char *data, size_t datalen, const char *sig, size_t siglen) { char digest[DIGEST_LEN]; @@ -948,7 +911,7 @@ crypto_pk_public_checksig_digest(crypto_pk_env_t *env, const char *data, log_warn(LD_BUG, "couldn't compute digest"); return -1; } - buflen = crypto_pk_keysize(env)+1; + buflen = crypto_pk_keysize(env); buf = tor_malloc(buflen); r = crypto_pk_public_checksig(env,buf,buflen,sig,siglen); if (r != DIGEST_LEN) { @@ -975,7 +938,7 @@ crypto_pk_public_checksig_digest(crypto_pk_env_t *env, const char *data, * at least the length of the modulus of <b>env</b>. */ int -crypto_pk_private_sign(crypto_pk_env_t *env, char *to, size_t tolen, +crypto_pk_private_sign(crypto_pk_t *env, char *to, size_t tolen, const char *from, size_t fromlen) { int r; @@ -1007,7 +970,7 @@ crypto_pk_private_sign(crypto_pk_env_t *env, char *to, size_t tolen, * at least the length of the modulus of <b>env</b>. */ int -crypto_pk_private_sign_digest(crypto_pk_env_t *env, char *to, size_t tolen, +crypto_pk_private_sign_digest(crypto_pk_t *env, char *to, size_t tolen, const char *from, size_t fromlen) { int r; @@ -1023,9 +986,6 @@ crypto_pk_private_sign_digest(crypto_pk_env_t *env, char *to, size_t tolen, * bytes of data from <b>from</b>, with padding type 'padding', * storing the results on <b>to</b>. * - * If no padding is used, the public key must be at least as large as - * <b>from</b>. - * * Returns the number of bytes written on success, -1 on failure. * * The encrypted data consists of: @@ -1037,7 +997,7 @@ crypto_pk_private_sign_digest(crypto_pk_env_t *env, char *to, size_t tolen, * the source data encrypted in AES-CTR mode with the symmetric key. */ int -crypto_pk_public_hybrid_encrypt(crypto_pk_env_t *env, +crypto_pk_public_hybrid_encrypt(crypto_pk_t *env, char *to, size_t tolen, const char *from, size_t fromlen, @@ -1045,7 +1005,7 @@ crypto_pk_public_hybrid_encrypt(crypto_pk_env_t *env, { int overhead, outlen, r; size_t pkeylen, symlen; - crypto_cipher_env_t *cipher = NULL; + crypto_cipher_t *cipher = NULL; char *buf = NULL; tor_assert(env); @@ -1056,9 +1016,6 @@ crypto_pk_public_hybrid_encrypt(crypto_pk_env_t *env, overhead = crypto_get_rsa_padding_overhead(crypto_get_rsa_padding(padding)); pkeylen = crypto_pk_keysize(env); - if (padding == PK_NO_PADDING && fromlen < pkeylen) - return -1; - if (!force && fromlen+overhead <= pkeylen) { /* It all fits in a single encrypt. */ return crypto_pk_public_encrypt(env,to, @@ -1068,20 +1025,8 @@ crypto_pk_public_hybrid_encrypt(crypto_pk_env_t *env, tor_assert(tolen >= fromlen + overhead + CIPHER_KEY_LEN); tor_assert(tolen >= pkeylen); - cipher = crypto_new_cipher_env(); - if (!cipher) return -1; - if (crypto_cipher_generate_key(cipher)<0) - goto err; - /* You can't just run around RSA-encrypting any bitstream: if it's - * greater than the RSA key, then OpenSSL will happily encrypt, and - * later decrypt to the wrong value. So we set the first bit of - * 'cipher->key' to 0 if we aren't padding. This means that our - * symmetric key is really only 127 bits. - */ - if (padding == PK_NO_PADDING) - cipher->key[0] &= 0x7f; - if (crypto_cipher_encrypt_init_cipher(cipher)<0) - goto err; + cipher = crypto_cipher_new(NULL); /* generate a new key. */ + buf = tor_malloc(pkeylen+1); memcpy(buf, cipher->key, CIPHER_KEY_LEN); memcpy(buf+CIPHER_KEY_LEN, from, pkeylen-overhead-CIPHER_KEY_LEN); @@ -1099,21 +1044,20 @@ crypto_pk_public_hybrid_encrypt(crypto_pk_env_t *env, if (r<0) goto err; memset(buf, 0, pkeylen); tor_free(buf); - crypto_free_cipher_env(cipher); + crypto_cipher_free(cipher); tor_assert(outlen+symlen < INT_MAX); return (int)(outlen + symlen); err: - if (buf) { - memset(buf, 0, pkeylen); - tor_free(buf); - } - if (cipher) crypto_free_cipher_env(cipher); + + memset(buf, 0, pkeylen); + tor_free(buf); + crypto_cipher_free(cipher); return -1; } /** Invert crypto_pk_public_hybrid_encrypt. */ int -crypto_pk_private_hybrid_decrypt(crypto_pk_env_t *env, +crypto_pk_private_hybrid_decrypt(crypto_pk_t *env, char *to, size_t tolen, const char *from, @@ -1122,7 +1066,7 @@ crypto_pk_private_hybrid_decrypt(crypto_pk_env_t *env, { int outlen, r; size_t pkeylen; - crypto_cipher_env_t *cipher = NULL; + crypto_cipher_t *cipher = NULL; char *buf = NULL; tor_assert(fromlen < SIZE_T_CEILING); @@ -1133,8 +1077,8 @@ crypto_pk_private_hybrid_decrypt(crypto_pk_env_t *env, warnOnFailure); } - buf = tor_malloc(pkeylen+1); - outlen = crypto_pk_private_decrypt(env,buf,pkeylen+1,from,pkeylen,padding, + buf = tor_malloc(pkeylen); + outlen = crypto_pk_private_decrypt(env,buf,pkeylen,from,pkeylen,padding, warnOnFailure); if (outlen<0) { log_fn(warnOnFailure?LOG_WARN:LOG_DEBUG, LD_CRYPTO, @@ -1146,7 +1090,7 @@ crypto_pk_private_hybrid_decrypt(crypto_pk_env_t *env, "No room for a symmetric key"); goto err; } - cipher = crypto_create_init_cipher(buf, 0); + cipher = crypto_cipher_new(buf); if (!cipher) { goto err; } @@ -1158,13 +1102,13 @@ crypto_pk_private_hybrid_decrypt(crypto_pk_env_t *env, goto err; memset(buf,0,pkeylen); tor_free(buf); - crypto_free_cipher_env(cipher); + crypto_cipher_free(cipher); tor_assert(outlen + fromlen < INT_MAX); return (int)(outlen + (fromlen-pkeylen)); err: memset(buf,0,pkeylen); tor_free(buf); - if (cipher) crypto_free_cipher_env(cipher); + crypto_cipher_free(cipher); return -1; } @@ -1172,7 +1116,7 @@ crypto_pk_private_hybrid_decrypt(crypto_pk_env_t *env, * Return -1 on error, or the number of characters used on success. */ int -crypto_pk_asn1_encode(crypto_pk_env_t *pk, char *dest, size_t dest_len) +crypto_pk_asn1_encode(crypto_pk_t *pk, char *dest, size_t dest_len) { int len; unsigned char *buf, *cp; @@ -1197,14 +1141,11 @@ crypto_pk_asn1_encode(crypto_pk_env_t *pk, char *dest, size_t dest_len) /** Decode an ASN.1-encoded public key from <b>str</b>; return the result on * success and NULL on failure. */ -crypto_pk_env_t * +crypto_pk_t * crypto_pk_asn1_decode(const char *str, size_t len) { RSA *rsa; unsigned char *buf; - /* This ifdef suppresses a type warning. Take out the first case once - * everybody is using OpenSSL 0.9.7 or later. - */ const unsigned char *cp; cp = buf = tor_malloc(len); memcpy(buf,str,len); @@ -1214,7 +1155,7 @@ crypto_pk_asn1_decode(const char *str, size_t len) crypto_log_errors(LOG_WARN,"decoding public key"); return NULL; } - return _crypto_new_pk_env_rsa(rsa); + return _crypto_new_pk_from_rsa(rsa); } /** Given a private or public key <b>pk</b>, put a SHA1 hash of the @@ -1222,7 +1163,7 @@ crypto_pk_asn1_decode(const char *str, size_t len) * Return 0 on success, -1 on failure. */ int -crypto_pk_get_digest(crypto_pk_env_t *pk, char *digest_out) +crypto_pk_get_digest(crypto_pk_t *pk, char *digest_out) { unsigned char *buf, *bufp; int len; @@ -1245,6 +1186,32 @@ crypto_pk_get_digest(crypto_pk_env_t *pk, char *digest_out) return 0; } +/** Compute all digests of the DER encoding of <b>pk</b>, and store them + * in <b>digests_out</b>. Return 0 on success, -1 on failure. */ +int +crypto_pk_get_all_digests(crypto_pk_t *pk, digests_t *digests_out) +{ + unsigned char *buf, *bufp; + int len; + + len = i2d_RSAPublicKey(pk->key, NULL); + if (len < 0) + return -1; + buf = bufp = tor_malloc(len+1); + len = i2d_RSAPublicKey(pk->key, &bufp); + if (len < 0) { + crypto_log_errors(LOG_WARN,"encoding public key"); + tor_free(buf); + return -1; + } + if (crypto_digest_all(digests_out, (char*)buf, len) < 0) { + tor_free(buf); + return -1; + } + tor_free(buf); + return 0; +} + /** Copy <b>in</b> to the <b>outlen</b>-byte buffer <b>out</b>, adding spaces * every four spaces. */ /* static */ void @@ -1276,7 +1243,7 @@ add_spaces_to_fp(char *out, size_t outlen, const char *in) * If <b>add_space</b> is false, omit the spaces. */ int -crypto_pk_get_fingerprint(crypto_pk_env_t *pk, char *fp_out, int add_space) +crypto_pk_get_fingerprint(crypto_pk_t *pk, char *fp_out, int add_space) { char digest[DIGEST_LEN]; char hexdigest[HEX_DIGEST_LEN+1]; @@ -1311,87 +1278,20 @@ crypto_pk_check_fingerprint_syntax(const char *s) /* symmetric crypto */ -/** Generate a new random key for the symmetric cipher in <b>env</b>. - * Return 0 on success, -1 on failure. Does not initialize the cipher. - */ -int -crypto_cipher_generate_key(crypto_cipher_env_t *env) -{ - tor_assert(env); - - return crypto_rand(env->key, CIPHER_KEY_LEN); -} - -/** Set the symmetric key for the cipher in <b>env</b> to the first - * CIPHER_KEY_LEN bytes of <b>key</b>. Does not initialize the cipher. - */ -void -crypto_cipher_set_key(crypto_cipher_env_t *env, const char *key) -{ - tor_assert(env); - tor_assert(key); - - memcpy(env->key, key, CIPHER_KEY_LEN); -} - -/** Generate an initialization vector for our AES-CTR cipher; store it - * in the first CIPHER_IV_LEN bytes of <b>iv_out</b>. */ -void -crypto_cipher_generate_iv(char *iv_out) -{ - crypto_rand(iv_out, CIPHER_IV_LEN); -} - -/** Adjust the counter of <b>env</b> to point to the first byte of the block - * corresponding to the encryption of the CIPHER_IV_LEN bytes at - * <b>iv</b>. */ -int -crypto_cipher_set_iv(crypto_cipher_env_t *env, const char *iv) -{ - tor_assert(env); - tor_assert(iv); - aes_set_iv(env->cipher, iv); - return 0; -} - /** Return a pointer to the key set for the cipher in <b>env</b>. */ const char * -crypto_cipher_get_key(crypto_cipher_env_t *env) +crypto_cipher_get_key(crypto_cipher_t *env) { return env->key; } -/** Initialize the cipher in <b>env</b> for encryption. Return 0 on - * success, -1 on failure. - */ -int -crypto_cipher_encrypt_init_cipher(crypto_cipher_env_t *env) -{ - tor_assert(env); - - aes_set_key(env->cipher, env->key, CIPHER_KEY_LEN*8); - return 0; -} - -/** Initialize the cipher in <b>env</b> for decryption. Return 0 on - * success, -1 on failure. - */ -int -crypto_cipher_decrypt_init_cipher(crypto_cipher_env_t *env) -{ - tor_assert(env); - - aes_set_key(env->cipher, env->key, CIPHER_KEY_LEN*8); - return 0; -} - /** Encrypt <b>fromlen</b> bytes from <b>from</b> using the cipher * <b>env</b>; on success, store the result to <b>to</b> and return 0. * On failure, return -1. */ int -crypto_cipher_encrypt(crypto_cipher_env_t *env, char *to, +crypto_cipher_encrypt(crypto_cipher_t *env, char *to, const char *from, size_t fromlen) { tor_assert(env); @@ -1410,7 +1310,7 @@ crypto_cipher_encrypt(crypto_cipher_env_t *env, char *to, * On failure, return -1. */ int -crypto_cipher_decrypt(crypto_cipher_env_t *env, char *to, +crypto_cipher_decrypt(crypto_cipher_t *env, char *to, const char *from, size_t fromlen) { tor_assert(env); @@ -1426,7 +1326,7 @@ crypto_cipher_decrypt(crypto_cipher_env_t *env, char *to, * on success, return 0. On failure, return -1. */ int -crypto_cipher_crypt_inplace(crypto_cipher_env_t *env, char *buf, size_t len) +crypto_cipher_crypt_inplace(crypto_cipher_t *env, char *buf, size_t len) { tor_assert(len < SIZE_T_CEILING); aes_crypt_inplace(env->cipher, buf, len); @@ -1434,20 +1334,17 @@ crypto_cipher_crypt_inplace(crypto_cipher_env_t *env, char *buf, size_t len) } /** Encrypt <b>fromlen</b> bytes (at least 1) from <b>from</b> with the key in - * <b>cipher</b> to the buffer in <b>to</b> of length + * <b>key</b> to the buffer in <b>to</b> of length * <b>tolen</b>. <b>tolen</b> must be at least <b>fromlen</b> plus * CIPHER_IV_LEN bytes for the initialization vector. On success, return the * number of bytes written, on failure, return -1. - * - * This function adjusts the current position of the counter in <b>cipher</b> - * to immediately after the encrypted data. */ int -crypto_cipher_encrypt_with_iv(crypto_cipher_env_t *cipher, +crypto_cipher_encrypt_with_iv(const char *key, char *to, size_t tolen, const char *from, size_t fromlen) { - tor_assert(cipher); + crypto_cipher_t *cipher; tor_assert(from); tor_assert(to); tor_assert(fromlen < INT_MAX); @@ -1457,28 +1354,27 @@ crypto_cipher_encrypt_with_iv(crypto_cipher_env_t *cipher, if (tolen < fromlen + CIPHER_IV_LEN) return -1; - crypto_cipher_generate_iv(to); - if (crypto_cipher_set_iv(cipher, to)<0) - return -1; + cipher = crypto_cipher_new_with_iv(key, NULL); + + memcpy(to, cipher->iv, CIPHER_IV_LEN); crypto_cipher_encrypt(cipher, to+CIPHER_IV_LEN, from, fromlen); + crypto_cipher_free(cipher); return (int)(fromlen + CIPHER_IV_LEN); } /** Decrypt <b>fromlen</b> bytes (at least 1+CIPHER_IV_LEN) from <b>from</b> - * with the key in <b>cipher</b> to the buffer in <b>to</b> of length + * with the key in <b>key</b> to the buffer in <b>to</b> of length * <b>tolen</b>. <b>tolen</b> must be at least <b>fromlen</b> minus * CIPHER_IV_LEN bytes for the initialization vector. On success, return the * number of bytes written, on failure, return -1. - * - * This function adjusts the current position of the counter in <b>cipher</b> - * to immediately after the decrypted data. */ int -crypto_cipher_decrypt_with_iv(crypto_cipher_env_t *cipher, +crypto_cipher_decrypt_with_iv(const char *key, char *to, size_t tolen, const char *from, size_t fromlen) { - tor_assert(cipher); + crypto_cipher_t *cipher; + tor_assert(key); tor_assert(from); tor_assert(to); tor_assert(fromlen < INT_MAX); @@ -1488,9 +1384,10 @@ crypto_cipher_decrypt_with_iv(crypto_cipher_env_t *cipher, if (tolen < fromlen - CIPHER_IV_LEN) return -1; - if (crypto_cipher_set_iv(cipher, from)<0) - return -1; + cipher = crypto_cipher_new_with_iv(key, from); + crypto_cipher_encrypt(cipher, to, from+CIPHER_IV_LEN, fromlen-CIPHER_IV_LEN); + crypto_cipher_free(cipher); return (int)(fromlen - CIPHER_IV_LEN); } @@ -1568,7 +1465,7 @@ crypto_digest_algorithm_parse_name(const char *name) } /** Intermediate information about the digest of a stream of data. */ -struct crypto_digest_env_t { +struct crypto_digest_t { union { SHA_CTX sha1; /**< state for SHA1 */ SHA256_CTX sha2; /**< state for SHA256 */ @@ -1579,11 +1476,11 @@ struct crypto_digest_env_t { /** Allocate and return a new digest object to compute SHA1 digests. */ -crypto_digest_env_t * -crypto_new_digest_env(void) +crypto_digest_t * +crypto_digest_new(void) { - crypto_digest_env_t *r; - r = tor_malloc(sizeof(crypto_digest_env_t)); + crypto_digest_t *r; + r = tor_malloc(sizeof(crypto_digest_t)); SHA1_Init(&r->d.sha1); r->algorithm = DIGEST_SHA1; return r; @@ -1591,12 +1488,12 @@ crypto_new_digest_env(void) /** Allocate and return a new digest object to compute 256-bit digests * using <b>algorithm</b>. */ -crypto_digest_env_t * -crypto_new_digest256_env(digest_algorithm_t algorithm) +crypto_digest_t * +crypto_digest256_new(digest_algorithm_t algorithm) { - crypto_digest_env_t *r; + crypto_digest_t *r; tor_assert(algorithm == DIGEST_SHA256); - r = tor_malloc(sizeof(crypto_digest_env_t)); + r = tor_malloc(sizeof(crypto_digest_t)); SHA256_Init(&r->d.sha2); r->algorithm = algorithm; return r; @@ -1605,18 +1502,18 @@ crypto_new_digest256_env(digest_algorithm_t algorithm) /** Deallocate a digest object. */ void -crypto_free_digest_env(crypto_digest_env_t *digest) +crypto_digest_free(crypto_digest_t *digest) { if (!digest) return; - memset(digest, 0, sizeof(crypto_digest_env_t)); + memset(digest, 0, sizeof(crypto_digest_t)); tor_free(digest); } /** Add <b>len</b> bytes from <b>data</b> to the digest object. */ void -crypto_digest_add_bytes(crypto_digest_env_t *digest, const char *data, +crypto_digest_add_bytes(crypto_digest_t *digest, const char *data, size_t len) { tor_assert(digest); @@ -1644,15 +1541,15 @@ crypto_digest_add_bytes(crypto_digest_env_t *digest, const char *data, * <b>out_len</b> must be \<= DIGEST256_LEN. */ void -crypto_digest_get_digest(crypto_digest_env_t *digest, +crypto_digest_get_digest(crypto_digest_t *digest, char *out, size_t out_len) { unsigned char r[DIGEST256_LEN]; - crypto_digest_env_t tmpenv; + crypto_digest_t tmpenv; tor_assert(digest); tor_assert(out); /* memcpy into a temporary ctx, since SHA*_Final clears the context */ - memcpy(&tmpenv, digest, sizeof(crypto_digest_env_t)); + memcpy(&tmpenv, digest, sizeof(crypto_digest_t)); switch (digest->algorithm) { case DIGEST_SHA1: tor_assert(out_len <= DIGEST_LEN); @@ -1677,13 +1574,13 @@ crypto_digest_get_digest(crypto_digest_env_t *digest, /** Allocate and return a new digest object with the same state as * <b>digest</b> */ -crypto_digest_env_t * -crypto_digest_dup(const crypto_digest_env_t *digest) +crypto_digest_t * +crypto_digest_dup(const crypto_digest_t *digest) { - crypto_digest_env_t *r; + crypto_digest_t *r; tor_assert(digest); - r = tor_malloc(sizeof(crypto_digest_env_t)); - memcpy(r,digest,sizeof(crypto_digest_env_t)); + r = tor_malloc(sizeof(crypto_digest_t)); + memcpy(r,digest,sizeof(crypto_digest_t)); return r; } @@ -1691,12 +1588,12 @@ crypto_digest_dup(const crypto_digest_env_t *digest) * of the digest object <b>from</b>. */ void -crypto_digest_assign(crypto_digest_env_t *into, - const crypto_digest_env_t *from) +crypto_digest_assign(crypto_digest_t *into, + const crypto_digest_t *from) { tor_assert(into); tor_assert(from); - memcpy(into,from,sizeof(crypto_digest_env_t)); + memcpy(into,from,sizeof(crypto_digest_t)); } /** Compute the HMAC-SHA-1 of the <b>msg_len</b> bytes in <b>msg</b>, using @@ -1715,15 +1612,15 @@ crypto_hmac_sha1(char *hmac_out, } /** Compute the HMAC-SHA-256 of the <b>msg_len</b> bytes in <b>msg</b>, using - * the <b>key</b> of length <b>key_len</b>. Store the DIGEST_LEN-byte result - * in <b>hmac_out</b>. + * the <b>key</b> of length <b>key_len</b>. Store the DIGEST256_LEN-byte + * result in <b>hmac_out</b>. */ void 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); @@ -1784,6 +1681,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. */ @@ -1791,49 +1691,324 @@ 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; + 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 */ + char *fname_new=NULL; + tor_asprintf(&fname_new, "%s.broken", fname); + + 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 @@ -1844,10 +2019,10 @@ init_dh_param(void) /** Allocate and return a new DH object for a key exchange. */ -crypto_dh_env_t * +crypto_dh_t * crypto_dh_new(int dh_type) { - crypto_dh_env_t *res = tor_malloc_zero(sizeof(crypto_dh_env_t)); + crypto_dh_t *res = tor_malloc_zero(sizeof(crypto_dh_t)); tor_assert(dh_type == DH_TYPE_CIRCUIT || dh_type == DH_TYPE_TLS || dh_type == DH_TYPE_REND); @@ -1882,7 +2057,7 @@ crypto_dh_new(int dh_type) /** Return the length of the DH key in <b>dh</b>, in bytes. */ int -crypto_dh_get_bytes(crypto_dh_env_t *dh) +crypto_dh_get_bytes(crypto_dh_t *dh) { tor_assert(dh); return DH_size(dh->dh); @@ -1892,7 +2067,7 @@ crypto_dh_get_bytes(crypto_dh_env_t *dh) * success, -1 on failure. */ int -crypto_dh_generate_public(crypto_dh_env_t *dh) +crypto_dh_generate_public(crypto_dh_t *dh) { again: if (!DH_generate_key(dh->dh)) { @@ -1916,7 +2091,7 @@ crypto_dh_generate_public(crypto_dh_env_t *dh) * success, -1 on failure. <b>pubkey_len</b> must be \>= DH_BYTES. */ int -crypto_dh_get_public(crypto_dh_env_t *dh, char *pubkey, size_t pubkey_len) +crypto_dh_get_public(crypto_dh_t *dh, char *pubkey, size_t pubkey_len) { int bytes; tor_assert(dh); @@ -1989,7 +2164,7 @@ tor_check_dh_key(int severity, BIGNUM *bn) * where || is concatenation.) */ ssize_t -crypto_dh_compute_secret(int severity, crypto_dh_env_t *dh, +crypto_dh_compute_secret(int severity, crypto_dh_t *dh, const char *pubkey, size_t pubkey_len, char *secret_out, size_t secret_bytes_out) { @@ -2080,7 +2255,7 @@ crypto_expand_key_material(const char *key_in, size_t key_in_len, /** Free a DH key exchange object. */ void -crypto_dh_free(crypto_dh_env_t *dh) +crypto_dh_free(crypto_dh_t *dh) { if (!dh) return; @@ -2097,13 +2272,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 @@ -2111,9 +2279,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 @@ -2134,7 +2302,7 @@ crypto_seed_rng(int startup) int rand_poll_status = 0; /* local variables */ -#ifdef MS_WINDOWS +#ifdef _WIN32 unsigned char buf[ADD_ENTROPY]; static int provider_set = 0; static HCRYPTPROV provider; @@ -2147,8 +2315,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) { @@ -2156,9 +2323,8 @@ crypto_seed_rng(int startup) if (rand_poll_status == 0) log_warn(LD_CRYPTO, "RAND_poll() failed."); } -#endif -#ifdef MS_WINDOWS +#ifdef _WIN32 if (!provider_set) { if (!CryptAcquireContext(&provider, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)) { @@ -2282,9 +2448,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) @@ -2293,8 +2462,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; @@ -2682,7 +2856,7 @@ void secret_to_key(char *key_out, size_t key_out_len, const char *secret, size_t secret_len, const char *s2k_specifier) { - crypto_digest_env_t *d; + crypto_digest_t *d; uint8_t c; size_t count, tmplen; char *tmp; @@ -2695,7 +2869,7 @@ secret_to_key(char *key_out, size_t key_out_len, const char *secret, tor_assert(key_out_len <= DIGEST_LEN); - d = crypto_new_digest_env(); + d = crypto_digest_new(); tmplen = 8+secret_len; tmp = tor_malloc(tmplen); memcpy(tmp,s2k_specifier,8); @@ -2713,7 +2887,7 @@ secret_to_key(char *key_out, size_t key_out_len, const char *secret, crypto_digest_get_digest(d, key_out, key_out_len); memset(tmp, 0, tmplen); tor_free(tmp); - crypto_free_digest_env(d); + crypto_digest_free(d); } #ifdef TOR_IS_MULTITHREADED @@ -2805,5 +2979,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 a0ee075ba..0482e1af6 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 @@ -37,11 +69,9 @@ * signs removed. */ #define BASE64_DIGEST256_LEN 43 -/** Constants used to indicate no padding for public-key encryption */ -#define PK_NO_PADDING 60000 -/** Constants used to indicate PKCS1 padding for public-key encryption */ +/** Constant used to indicate PKCS1 padding for public-key encryption */ #define PK_PKCS1_PADDING 60001 -/** Constants used to indicate OAEP padding for public-key encryption */ +/** Constant used to indicate OAEP padding for public-key encryption */ #define PK_PKCS1_OAEP_PADDING 60002 /** Number of bytes added for PKCS1 padding. */ @@ -75,10 +105,10 @@ typedef struct { char d[N_DIGEST_ALGORITHMS][DIGEST256_LEN]; } digests_t; -typedef struct crypto_pk_env_t crypto_pk_env_t; -typedef struct crypto_cipher_env_t crypto_cipher_env_t; -typedef struct crypto_digest_env_t crypto_digest_env_t; -typedef struct crypto_dh_env_t crypto_dh_env_t; +typedef struct crypto_pk_t crypto_pk_t; +typedef struct crypto_cipher_t crypto_cipher_t; +typedef struct crypto_digest_t crypto_digest_t; +typedef struct crypto_dh_t crypto_dh_t; /* global state */ int crypto_global_init(int hardwareAccel, @@ -88,90 +118,84 @@ void crypto_thread_cleanup(void); int crypto_global_cleanup(void); /* environment setup */ -crypto_pk_env_t *crypto_new_pk_env(void); -void crypto_free_pk_env(crypto_pk_env_t *env); +crypto_pk_t *crypto_pk_new(void); +void crypto_pk_free(crypto_pk_t *env); -/* 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); +void crypto_set_tls_dh_prime(const char *dynamic_dh_modulus_fname); -crypto_cipher_env_t *crypto_new_cipher_env(void); -void crypto_free_cipher_env(crypto_cipher_env_t *env); +crypto_cipher_t *crypto_cipher_new(const char *key); +crypto_cipher_t *crypto_cipher_new_with_iv(const char *key, const char *iv); +void crypto_cipher_free(crypto_cipher_t *env); /* public key crypto */ -int crypto_pk_generate_key_with_bits(crypto_pk_env_t *env, int bits); +int crypto_pk_generate_key_with_bits(crypto_pk_t *env, int bits); #define crypto_pk_generate_key(env) \ crypto_pk_generate_key_with_bits((env), (PK_BYTES*8)) -int crypto_pk_read_private_key_from_filename(crypto_pk_env_t *env, +int crypto_pk_read_private_key_from_filename(crypto_pk_t *env, const char *keyfile); -int crypto_pk_write_public_key_to_string(crypto_pk_env_t *env, +int crypto_pk_write_public_key_to_string(crypto_pk_t *env, char **dest, size_t *len); -int crypto_pk_write_private_key_to_string(crypto_pk_env_t *env, +int crypto_pk_write_private_key_to_string(crypto_pk_t *env, char **dest, size_t *len); -int crypto_pk_read_public_key_from_string(crypto_pk_env_t *env, +int crypto_pk_read_public_key_from_string(crypto_pk_t *env, const char *src, size_t len); -int crypto_pk_read_private_key_from_string(crypto_pk_env_t *env, +int crypto_pk_read_private_key_from_string(crypto_pk_t *env, const char *s, ssize_t len); -int crypto_pk_write_private_key_to_filename(crypto_pk_env_t *env, +int crypto_pk_write_private_key_to_filename(crypto_pk_t *env, const char *fname); -int crypto_pk_check_key(crypto_pk_env_t *env); -int crypto_pk_cmp_keys(crypto_pk_env_t *a, crypto_pk_env_t *b); -size_t crypto_pk_keysize(crypto_pk_env_t *env); -int crypto_pk_num_bits(crypto_pk_env_t *env); -crypto_pk_env_t *crypto_pk_dup_key(crypto_pk_env_t *orig); -crypto_pk_env_t *crypto_pk_copy_full(crypto_pk_env_t *orig); -int crypto_pk_key_is_private(const crypto_pk_env_t *key); -int crypto_pk_public_exponent_ok(crypto_pk_env_t *env); +int crypto_pk_check_key(crypto_pk_t *env); +int crypto_pk_cmp_keys(crypto_pk_t *a, crypto_pk_t *b); +size_t crypto_pk_keysize(crypto_pk_t *env); +int crypto_pk_num_bits(crypto_pk_t *env); +crypto_pk_t *crypto_pk_dup_key(crypto_pk_t *orig); +crypto_pk_t *crypto_pk_copy_full(crypto_pk_t *orig); +int crypto_pk_key_is_private(const crypto_pk_t *key); +int crypto_pk_public_exponent_ok(crypto_pk_t *env); -int crypto_pk_public_encrypt(crypto_pk_env_t *env, char *to, size_t tolen, +int crypto_pk_public_encrypt(crypto_pk_t *env, char *to, size_t tolen, const char *from, size_t fromlen, int padding); -int crypto_pk_private_decrypt(crypto_pk_env_t *env, char *to, size_t tolen, +int crypto_pk_private_decrypt(crypto_pk_t *env, char *to, size_t tolen, const char *from, size_t fromlen, int padding, int warnOnFailure); -int crypto_pk_public_checksig(crypto_pk_env_t *env, char *to, size_t tolen, +int crypto_pk_public_checksig(crypto_pk_t *env, char *to, size_t tolen, const char *from, size_t fromlen); -int crypto_pk_public_checksig_digest(crypto_pk_env_t *env, const char *data, +int crypto_pk_public_checksig_digest(crypto_pk_t *env, const char *data, size_t datalen, const char *sig, size_t siglen); -int crypto_pk_private_sign(crypto_pk_env_t *env, char *to, size_t tolen, +int crypto_pk_private_sign(crypto_pk_t *env, char *to, size_t tolen, const char *from, size_t fromlen); -int crypto_pk_private_sign_digest(crypto_pk_env_t *env, char *to, size_t tolen, +int crypto_pk_private_sign_digest(crypto_pk_t *env, char *to, size_t tolen, const char *from, size_t fromlen); -int crypto_pk_public_hybrid_encrypt(crypto_pk_env_t *env, char *to, +int crypto_pk_public_hybrid_encrypt(crypto_pk_t *env, char *to, size_t tolen, const char *from, size_t fromlen, int padding, int force); -int crypto_pk_private_hybrid_decrypt(crypto_pk_env_t *env, char *to, +int crypto_pk_private_hybrid_decrypt(crypto_pk_t *env, char *to, size_t tolen, const char *from, size_t fromlen, int padding, int warnOnFailure); -int crypto_pk_asn1_encode(crypto_pk_env_t *pk, char *dest, size_t dest_len); -crypto_pk_env_t *crypto_pk_asn1_decode(const char *str, size_t len); -int crypto_pk_get_digest(crypto_pk_env_t *pk, char *digest_out); -int crypto_pk_get_fingerprint(crypto_pk_env_t *pk, char *fp_out,int add_space); +int crypto_pk_asn1_encode(crypto_pk_t *pk, char *dest, size_t dest_len); +crypto_pk_t *crypto_pk_asn1_decode(const char *str, size_t len); +int crypto_pk_get_digest(crypto_pk_t *pk, char *digest_out); +int crypto_pk_get_all_digests(crypto_pk_t *pk, digests_t *digests_out); +int crypto_pk_get_fingerprint(crypto_pk_t *pk, char *fp_out,int add_space); int crypto_pk_check_fingerprint_syntax(const char *s); /* symmetric crypto */ -int crypto_cipher_generate_key(crypto_cipher_env_t *env); -void crypto_cipher_set_key(crypto_cipher_env_t *env, const char *key); -void crypto_cipher_generate_iv(char *iv_out); -int crypto_cipher_set_iv(crypto_cipher_env_t *env, const char *iv); -const char *crypto_cipher_get_key(crypto_cipher_env_t *env); -int crypto_cipher_encrypt_init_cipher(crypto_cipher_env_t *env); -int crypto_cipher_decrypt_init_cipher(crypto_cipher_env_t *env); - -int crypto_cipher_encrypt(crypto_cipher_env_t *env, char *to, +const char *crypto_cipher_get_key(crypto_cipher_t *env); + +int crypto_cipher_encrypt(crypto_cipher_t *env, char *to, const char *from, size_t fromlen); -int crypto_cipher_decrypt(crypto_cipher_env_t *env, char *to, +int crypto_cipher_decrypt(crypto_cipher_t *env, char *to, const char *from, size_t fromlen); -int crypto_cipher_crypt_inplace(crypto_cipher_env_t *env, char *d, size_t len); +int crypto_cipher_crypt_inplace(crypto_cipher_t *env, char *d, size_t len); -int crypto_cipher_encrypt_with_iv(crypto_cipher_env_t *env, +int crypto_cipher_encrypt_with_iv(const char *key, char *to, size_t tolen, const char *from, size_t fromlen); -int crypto_cipher_decrypt_with_iv(crypto_cipher_env_t *env, +int crypto_cipher_decrypt_with_iv(const char *key, char *to, size_t tolen, const char *from, size_t fromlen); @@ -182,16 +206,16 @@ int crypto_digest256(char *digest, const char *m, size_t len, int crypto_digest_all(digests_t *ds_out, const char *m, size_t len); const char *crypto_digest_algorithm_get_name(digest_algorithm_t alg); int crypto_digest_algorithm_parse_name(const char *name); -crypto_digest_env_t *crypto_new_digest_env(void); -crypto_digest_env_t *crypto_new_digest256_env(digest_algorithm_t algorithm); -void crypto_free_digest_env(crypto_digest_env_t *digest); -void crypto_digest_add_bytes(crypto_digest_env_t *digest, const char *data, +crypto_digest_t *crypto_digest_new(void); +crypto_digest_t *crypto_digest256_new(digest_algorithm_t algorithm); +void crypto_digest_free(crypto_digest_t *digest); +void crypto_digest_add_bytes(crypto_digest_t *digest, const char *data, size_t len); -void crypto_digest_get_digest(crypto_digest_env_t *digest, +void crypto_digest_get_digest(crypto_digest_t *digest, char *out, size_t out_len); -crypto_digest_env_t *crypto_digest_dup(const crypto_digest_env_t *digest); -void crypto_digest_assign(crypto_digest_env_t *into, - const crypto_digest_env_t *from); +crypto_digest_t *crypto_digest_dup(const crypto_digest_t *digest); +void crypto_digest_assign(crypto_digest_t *into, + const crypto_digest_t *from); void crypto_hmac_sha1(char *hmac_out, const char *key, size_t key_len, const char *msg, size_t msg_len); @@ -203,15 +227,15 @@ void crypto_hmac_sha256(char *hmac_out, #define DH_TYPE_CIRCUIT 1 #define DH_TYPE_REND 2 #define DH_TYPE_TLS 3 -crypto_dh_env_t *crypto_dh_new(int dh_type); -int crypto_dh_get_bytes(crypto_dh_env_t *dh); -int crypto_dh_generate_public(crypto_dh_env_t *dh); -int crypto_dh_get_public(crypto_dh_env_t *dh, char *pubkey_out, +crypto_dh_t *crypto_dh_new(int dh_type); +int crypto_dh_get_bytes(crypto_dh_t *dh); +int crypto_dh_generate_public(crypto_dh_t *dh); +int crypto_dh_get_public(crypto_dh_t *dh, char *pubkey_out, size_t pubkey_out_len); -ssize_t crypto_dh_compute_secret(int severity, crypto_dh_env_t *dh, +ssize_t crypto_dh_compute_secret(int severity, crypto_dh_t *dh, const char *pubkey, size_t pubkey_len, char *secret_out, size_t secret_out_len); -void crypto_dh_free(crypto_dh_env_t *dh); +void crypto_dh_free(crypto_dh_t *dh); int crypto_expand_key_material(const char *key_in, size_t in_len, char *key_out, size_t key_out_len); @@ -248,15 +272,16 @@ void secret_to_key(char *key_out, size_t key_out_len, const char *secret, size_t secret_len, const char *s2k_specifier); #ifdef CRYPTO_PRIVATE -/* Prototypes for private functions only used by tortls.c and crypto.c */ +/* Prototypes for private functions only used by tortls.c, crypto.c, and the + * unit tests. */ struct rsa_st; struct evp_pkey_st; struct dh_st; -struct rsa_st *_crypto_pk_env_get_rsa(crypto_pk_env_t *env); -crypto_pk_env_t *_crypto_new_pk_env_rsa(struct rsa_st *rsa); -struct evp_pkey_st *_crypto_pk_env_get_evp_pkey(crypto_pk_env_t *env, +struct rsa_st *_crypto_pk_get_rsa(crypto_pk_t *env); +crypto_pk_t *_crypto_new_pk_from_rsa(struct rsa_st *rsa); +struct evp_pkey_st *_crypto_pk_get_evp_pkey(crypto_pk_t *env, int private); -struct dh_st *_crypto_dh_env_get_dh(crypto_dh_env_t *dh); +struct dh_st *_crypto_dh_get_dh(crypto_dh_t *dh); /* Prototypes for private functions only used by crypto.c and test.c*/ void add_spaces_to_fp(char *out, size_t outlen, const char *in); #endif diff --git a/src/common/ht.h b/src/common/ht.h index 0850c0709..0783cb1e7 100644 --- a/src/common/ht.h +++ b/src/common/ht.h @@ -25,19 +25,22 @@ #define HT_INITIALIZER() \ { NULL, 0, 0, 0, -1 } +#ifdef HT_NO_CACHE_HASH_VALUES +#define HT_ENTRY(type) \ + struct { \ + struct type *hte_next; \ + } +#else #define HT_ENTRY(type) \ struct { \ struct type *hte_next; \ unsigned hte_hash; \ } +#endif #define HT_EMPTY(head) \ ((head)->hth_n_entries == 0) -/* Helper: alias for the bucket containing 'elm'. */ -#define _HT_BUCKET(head, field, elm) \ - ((head)->hth_table[elm->field.hte_hash % head->hth_table_length]) - /* How many elements in 'head'? */ #define HT_SIZE(head) \ ((head)->hth_n_entries) @@ -98,8 +101,26 @@ ht_string_hash(const char *s) return h; } -#define _HT_SET_HASH(elm, field, hashfn) \ - (elm)->field.hte_hash = hashfn(elm) +#ifndef HT_NO_CACHE_HASH_VALUES +#define HT_SET_HASH_(elm, field, hashfn) \ + do { (elm)->field.hte_hash = hashfn(elm); } while (0) +#define HT_SET_HASHVAL_(elm, field, val) \ + do { (elm)->field.hte_hash = (val); } while (0) +#define HT_ELT_HASH_(elm, field, hashfn) \ + ((elm)->field.hte_hash) +#else +#define HT_SET_HASH_(elm, field, hashfn) \ + ((void)0) +#define HT_ELT_HASH_(elm, field, hashfn) \ + (hashfn(elm)) +#define HT_SET_HASHVAL_(elm, field, val) \ + ((void)0) +#endif + +/* Helper: alias for the bucket containing 'elm'. */ +#define HT_BUCKET_(head, field, elm, hashfn) \ + ((head)->hth_table[HT_ELT_HASH_(elm,field,hashfn) \ + % head->hth_table_length]) #define HT_FOREACH(x, name, head) \ for ((x) = HT_START(name, head); \ @@ -109,7 +130,7 @@ ht_string_hash(const char *s) #define HT_PROTOTYPE(name, type, field, hashfn, eqfn) \ int name##_HT_GROW(struct name *ht, unsigned min_capacity); \ void name##_HT_CLEAR(struct name *ht); \ - int _##name##_HT_REP_IS_BAD(const struct name *ht); \ + int name##_HT_REP_IS_BAD_(const struct name *ht); \ static INLINE void \ name##_HT_INIT(struct name *head) { \ head->hth_table_length = 0; \ @@ -121,12 +142,12 @@ ht_string_hash(const char *s) /* Helper: returns a pointer to the right location in the table \ * 'head' to find or insert the element 'elm'. */ \ static INLINE struct type ** \ - _##name##_HT_FIND_P(struct name *head, struct type *elm) \ + name##_HT_FIND_P_(struct name *head, struct type *elm) \ { \ struct type **p; \ if (!head->hth_table) \ return NULL; \ - p = &_HT_BUCKET(head, field, elm); \ + p = &HT_BUCKET_(head, field, elm, hashfn); \ while (*p) { \ if (eqfn(*p, elm)) \ return p; \ @@ -141,8 +162,8 @@ ht_string_hash(const char *s) { \ struct type **p; \ struct name *h = (struct name *) head; \ - _HT_SET_HASH(elm, field, hashfn); \ - p = _##name##_HT_FIND_P(h, elm); \ + HT_SET_HASH_(elm, field, hashfn); \ + p = name##_HT_FIND_P_(h, elm); \ return p ? *p : NULL; \ } \ /* Insert the element 'elm' into the table 'head'. Do not call this \ @@ -154,8 +175,8 @@ ht_string_hash(const char *s) if (!head->hth_table || head->hth_n_entries >= head->hth_load_limit) \ name##_HT_GROW(head, head->hth_n_entries+1); \ ++head->hth_n_entries; \ - _HT_SET_HASH(elm, field, hashfn); \ - p = &_HT_BUCKET(head, field, elm); \ + HT_SET_HASH_(elm, field, hashfn); \ + p = &HT_BUCKET_(head, field, elm, hashfn); \ elm->field.hte_next = *p; \ *p = elm; \ } \ @@ -168,8 +189,8 @@ ht_string_hash(const char *s) struct type **p, *r; \ if (!head->hth_table || head->hth_n_entries >= head->hth_load_limit) \ name##_HT_GROW(head, head->hth_n_entries+1); \ - _HT_SET_HASH(elm, field, hashfn); \ - p = _##name##_HT_FIND_P(head, elm); \ + HT_SET_HASH_(elm, field, hashfn); \ + p = name##_HT_FIND_P_(head, elm); \ r = *p; \ *p = elm; \ if (r && (r!=elm)) { \ @@ -187,8 +208,8 @@ ht_string_hash(const char *s) name##_HT_REMOVE(struct name *head, struct type *elm) \ { \ struct type **p, *r; \ - _HT_SET_HASH(elm, field, hashfn); \ - p = _##name##_HT_FIND_P(head,elm); \ + HT_SET_HASH_(elm, field, hashfn); \ + p = name##_HT_FIND_P_(head,elm); \ if (!p || !*p) \ return NULL; \ r = *p; \ @@ -207,7 +228,6 @@ ht_string_hash(const char *s) void *data) \ { \ unsigned idx; \ - int remove; \ struct type **p, **nextp, *next; \ if (!head->hth_table) \ return; \ @@ -216,8 +236,7 @@ ht_string_hash(const char *s) while (*p) { \ nextp = &(*p)->field.hte_next; \ next = *nextp; \ - remove = fn(*p, data); \ - if (remove) { \ + if (fn(*p, data)) { \ --head->hth_n_entries; \ *p = next; \ } else { \ @@ -251,7 +270,8 @@ ht_string_hash(const char *s) if ((*elm)->field.hte_next) { \ return &(*elm)->field.hte_next; \ } else { \ - unsigned b = ((*elm)->field.hte_hash % head->hth_table_length)+1; \ + unsigned b = (HT_ELT_HASH_(*elm, field, hashfn) \ + % head->hth_table_length)+1; \ while (b < head->hth_table_length) { \ if (head->hth_table[b]) \ return &head->hth_table[b]; \ @@ -263,7 +283,7 @@ ht_string_hash(const char *s) static INLINE struct type ** \ name##_HT_NEXT_RMV(struct name *head, struct type **elm) \ { \ - unsigned h = (*elm)->field.hte_hash; \ + unsigned h = HT_ELT_HASH_(*elm, field, hashfn); \ *elm = (*elm)->field.hte_next; \ --head->hth_n_entries; \ if (*elm) { \ @@ -320,7 +340,7 @@ ht_string_hash(const char *s) elm = head->hth_table[b]; \ while (elm) { \ next = elm->field.hte_next; \ - b2 = elm->field.hte_hash % new_len; \ + b2 = HT_ELT_HASH_(elm, field, hashfn) % new_len; \ elm->field.hte_next = new_table[b2]; \ new_table[b2] = elm; \ elm = next; \ @@ -338,7 +358,7 @@ ht_string_hash(const char *s) for (b=0; b < head->hth_table_length; ++b) { \ struct type *e, **pE; \ for (pE = &new_table[b], e = *pE; e != NULL; e = *pE) { \ - b2 = e->field.hte_hash % new_len; \ + b2 = HT_ELT_HASH_(e, field, hashfn) % new_len; \ if (b2 == b) { \ pE = &e->field.hte_next; \ } else { \ @@ -368,7 +388,7 @@ ht_string_hash(const char *s) /* Debugging helper: return false iff the representation of 'head' is \ * internally consistent. */ \ int \ - _##name##_HT_REP_IS_BAD(const struct name *head) \ + name##_HT_REP_IS_BAD_(const struct name *head) \ { \ unsigned n, i; \ struct type *elm; \ @@ -390,9 +410,9 @@ ht_string_hash(const char *s) return 5; \ for (n = i = 0; i < head->hth_table_length; ++i) { \ for (elm = head->hth_table[i]; elm; elm = elm->field.hte_next) { \ - if (elm->field.hte_hash != hashfn(elm)) \ + if (HT_ELT_HASH_(elm, field, hashfn) != hashfn(elm)) \ return 1000 + i; \ - if ((elm->field.hte_hash % head->hth_table_length) != i) \ + if ((HT_ELT_HASH_(elm, field, hashfn) % head->hth_table_length) != i) \ return 10000 + i; \ ++n; \ } \ @@ -405,24 +425,24 @@ ht_string_hash(const char *s) /** Implements an over-optimized "find and insert if absent" block; * not meant for direct usage by typical code, or usage outside the critical * path.*/ -#define _HT_FIND_OR_INSERT(name, field, hashfn, head, eltype, elm, var, y, n) \ +#define HT_FIND_OR_INSERT_(name, field, hashfn, head, eltype, elm, var, y, n) \ { \ - struct name *_##var##_head = head; \ - eltype **var; \ - if (!_##var##_head->hth_table || \ - _##var##_head->hth_n_entries >= _##var##_head->hth_load_limit) \ - name##_HT_GROW(_##var##_head, _##var##_head->hth_n_entries+1); \ - _HT_SET_HASH((elm), field, hashfn); \ - var = _##name##_HT_FIND_P(_##var##_head, (elm)); \ + struct name *var##_head_ = head; \ + struct eltype **var; \ + if (!var##_head_->hth_table || \ + var##_head_->hth_n_entries >= var##_head_->hth_load_limit) \ + name##_HT_GROW(var##_head_, var##_head_->hth_n_entries+1); \ + HT_SET_HASH_((elm), field, hashfn); \ + var = name##_HT_FIND_P_(var##_head_, (elm)); \ if (*var) { \ y; \ } else { \ n; \ } \ } -#define _HT_FOI_INSERT(field, head, elm, newent, var) \ +#define HT_FOI_INSERT_(field, head, elm, newent, var) \ { \ - newent->field.hte_hash = (elm)->field.hte_hash; \ + HT_SET_HASHVAL_(newent, field, (elm)->field.hte_hash); \ newent->field.hte_next = NULL; \ *var = newent; \ ++((head)->hth_n_entries); \ @@ -431,9 +451,8 @@ ht_string_hash(const char *s) /* * Copyright 2005, Nick Mathewson. Implementation logic is adapted from code * by Christopher Clark, retrofit to allow drop-in memory management, and to - * use the same interface as Niels Provos's HT_H. I'm not sure whether this - * is a derived work any more, but whether it is or not, the license below - * applies. + * use the same interface as Niels Provos's tree.h. This is probably still + * a derived work, so the original license below still applies. * * Copyright (c) 2002, Christopher Clark * All rights reserved. diff --git a/src/common/log.c b/src/common/log.c index f2999f4e6..f509ddcd6 100644 --- a/src/common/log.c +++ b/src/common/log.c @@ -161,6 +161,17 @@ log_set_application_name(const char *name) appname = name ? tor_strdup(name) : NULL; } +/** Log time granularity in milliseconds. */ +static int log_time_granularity = 1; + +/** Define log time granularity for all logs to be <b>granularity_msec</b> + * milliseconds. */ +void +set_log_time_granularity(int granularity_msec) +{ + log_time_granularity = granularity_msec; +} + /** Helper: Write the standard prefix for log lines to a * <b>buf_len</b> character buffer in <b>buf</b>. */ @@ -171,14 +182,22 @@ _log_prefix(char *buf, size_t buf_len, int severity) struct timeval now; struct tm tm; size_t n; - int r; + int r, ms; tor_gettimeofday(&now); t = (time_t)now.tv_sec; + ms = (int)now.tv_usec / 1000; + if (log_time_granularity >= 1000) { + t -= t % (log_time_granularity / 1000); + ms = 0; + } else { + ms -= ((int)now.tv_usec / 1000) % log_time_granularity; + } n = strftime(buf, buf_len, "%b %d %H:%M:%S", tor_localtime_r(&t, &tm)); - r = tor_snprintf(buf+n, buf_len-n, ".%.3i [%s] ", - (int)now.tv_usec / 1000, sev_to_string(severity)); + r = tor_snprintf(buf+n, buf_len-n, ".%.3i [%s] ", ms, + sev_to_string(severity)); + if (r<0) return buf_len-1; else @@ -641,7 +660,7 @@ init_logging(void) log_mutex_initialized = 1; } if (pending_cb_messages == NULL) - pending_cb_messages = smartlist_create(); + pending_cb_messages = smartlist_new(); } /** Set whether we report logging domains as a part of our log messages. @@ -710,7 +729,7 @@ change_callback_log_severity(int loglevelMin, int loglevelMax, UNLOCK_LOGS(); } -/** If there are any log messages that were genered with LD_NOCB waiting to +/** If there are any log messages that were generated with LD_NOCB waiting to * be sent to callback-based loggers, send them now. */ void flush_pending_log_callbacks(void) @@ -725,7 +744,7 @@ flush_pending_log_callbacks(void) } messages = pending_cb_messages; - pending_cb_messages = smartlist_create(); + pending_cb_messages = smartlist_new(); do { SMARTLIST_FOREACH_BEGIN(messages, pending_cb_message_t *, msg) { const int severity = msg->severity; @@ -810,7 +829,7 @@ add_file_log(const log_severity_list_t *severity, const char *filename) int fd; logfile_t *lf; - fd = open(filename, O_WRONLY|O_CREAT|O_APPEND, 0644); + fd = tor_open_cloexec(filename, O_WRONLY|O_CREAT|O_APPEND, 0644); if (fd<0) return -1; if (tor_fd_seekend(fd)<0) @@ -887,7 +906,7 @@ log_level_to_string(int level) static const char *domain_list[] = { "GENERAL", "CRYPTO", "NET", "CONFIG", "FS", "PROTOCOL", "MM", "HTTP", "APP", "CONTROL", "CIRC", "REND", "BUG", "DIR", "DIRSERV", - "OR", "EDGE", "ACCT", "HIST", "HANDSHAKE", NULL + "OR", "EDGE", "ACCT", "HIST", "HANDSHAKE", "HEARTBEAT", NULL }; /** Return a bitmask for the log domain for which <b>domain</b> is the name, @@ -981,7 +1000,7 @@ parse_log_severity_config(const char **cfg_ptr, return -1; domains = 0; domains_str = tor_strndup(cfg+1, closebracket-cfg-1); - domains_list = smartlist_create(); + domains_list = smartlist_new(); smartlist_split_string(domains_list, domains_str, ",", SPLIT_SKIP_SPACE, -1); tor_free(domains_str); diff --git a/src/common/mempool.c b/src/common/mempool.c index c44492318..1462c5f8f 100644 --- a/src/common/mempool.c +++ b/src/common/mempool.c @@ -62,7 +62,6 @@ #if 1 /* Tor dependencies */ -#include "orconfig.h" #include "util.h" #include "compat.h" #include "torlog.h" @@ -137,7 +136,7 @@ struct mp_chunk_t { int capacity; /**< Number of items that can be fit into this chunk. */ size_t mem_size; /**< Number of usable bytes in mem. */ char *next_mem; /**< Pointer into part of <b>mem</b> not yet carved up. */ - char mem[1]; /**< Storage for this chunk. (Not actual size.) */ + char mem[FLEXIBLE_ARRAY_MEMBER]; /**< Storage for this chunk. */ }; /** Number of extra bytes needed beyond mem_size to allocate a chunk. */ diff --git a/src/common/procmon.c b/src/common/procmon.c index 5c10e9a22..85d2a2fe3 100644 --- a/src/common/procmon.c +++ b/src/common/procmon.c @@ -21,7 +21,7 @@ #include <errno.h> #endif -#ifdef MS_WINDOWS +#ifdef _WIN32 #include <windows.h> /* Windows does not define pid_t, but _getpid() returns an int. */ @@ -91,7 +91,7 @@ struct tor_process_monitor_t { * polls. */ pid_t pid; -#ifdef MS_WINDOWS +#ifdef _WIN32 /** Windows-only: Should we poll hproc? If false, poll pid * instead. */ int poll_hproc; @@ -192,7 +192,7 @@ tor_process_monitor_new(struct event_base *base, procmon->pid = ppspec.pid; -#ifdef MS_WINDOWS +#ifdef _WIN32 procmon->hproc = OpenProcess(PROCESS_QUERY_INFORMATION | SYNCHRONIZE, FALSE, procmon->pid); @@ -246,7 +246,7 @@ tor_process_monitor_poll_cb(evutil_socket_t unused1, short unused2, tor_assert(procmon != NULL); -#ifdef MS_WINDOWS +#ifdef _WIN32 if (procmon->poll_hproc) { DWORD exit_code; if (!GetExitCodeProcess(procmon->hproc, &exit_code)) { @@ -323,7 +323,7 @@ tor_process_monitor_free(tor_process_monitor_t *procmon) if (procmon == NULL) return; -#ifdef MS_WINDOWS +#ifdef _WIN32 if (procmon->hproc != NULL) CloseHandle(procmon->hproc); #endif diff --git a/src/common/torgzip.c b/src/common/torgzip.c index a26e5b21e..62e4a0703 100644 --- a/src/common/torgzip.c +++ b/src/common/torgzip.c @@ -44,11 +44,7 @@ #define off64_t int64_t #endif -#ifdef _MSC_VER -#include "..\..\contrib\zlib\zlib.h" -#else #include <zlib.h> -#endif /** Set to 1 if zlib is a version that supports gzip; set to 0 if it doesn't; * set to -1 if we haven't checked yet. */ diff --git a/src/common/torint.h b/src/common/torint.h index 0b5c29adc..af975471f 100644 --- a/src/common/torint.h +++ b/src/common/torint.h @@ -111,6 +111,15 @@ typedef signed int int32_t; typedef unsigned int uint32_t; #define HAVE_UINT32_T #endif +#ifndef UINT16_MAX +#define UINT16_MAX 0xffffu +#endif +#ifndef INT16_MAX +#define INT16_MAX 0x7fff +#endif +#ifndef INT16_MIN +#define INT16_MIN (-INT16_MAX-1) +#endif #ifndef UINT32_MAX #define UINT32_MAX 0xffffffffu #endif diff --git a/src/common/torlog.h b/src/common/torlog.h index 541a0d173..4c5729ef5 100644 --- a/src/common/torlog.h +++ b/src/common/torlog.h @@ -92,8 +92,10 @@ #define LD_HIST (1u<<18) /** OR handshaking */ #define LD_HANDSHAKE (1u<<19) +/** Heartbeat messages */ +#define LD_HEARTBEAT (1u<<20) /** Number of logging domains in the code. */ -#define N_LOGGING_DOMAINS 20 +#define N_LOGGING_DOMAINS 21 /** This log message is not safe to send to a callback-based logger * immediately. Used as a flag, not a log domain. */ @@ -145,6 +147,7 @@ void change_callback_log_severity(int loglevelMin, int loglevelMax, log_callback cb); void flush_pending_log_callbacks(void); void log_set_application_name(const char *name); +void set_log_time_granularity(int granularity_msec); void tor_log(int severity, log_domain_mask_t domain, const char *format, ...) CHECK_PRINTF(3,4); diff --git a/src/common/tortls.c b/src/common/tortls.c index 4c9d2188d..1120f3e8b 100644 --- a/src/common/tortls.c +++ b/src/common/tortls.c @@ -21,9 +21,10 @@ #endif #include <assert.h> -#ifdef MS_WINDOWS /*wrkard for dtls1.h >= 0.9.8m of "#include <winsock.h>"*/ - #define WIN32_WINNT 0x400 - #define _WIN32_WINNT 0x400 +#ifdef _WIN32 /*wrkard for dtls1.h >= 0.9.8m of "#include <winsock.h>"*/ + #ifndef _WIN32_WINNT + #define _WIN32_WINNT 0x0501 + #endif #define WIN32_LEAN_AND_MEAN #if defined(_MSC_VER) && (_MSC_VER < 1300) #include <winsock.h> @@ -40,20 +41,27 @@ #include <openssl/bio.h> #include <openssl/opensslv.h> -#if OPENSSL_VERSION_NUMBER < 0x00907000l -#error "We require OpenSSL >= 0.9.7" +#ifdef USE_BUFFEREVENTS +#include <event2/bufferevent_ssl.h> +#include <event2/buffer.h> +#include <event2/event.h> +#include "compat_libevent.h" #endif #define CRYPTO_PRIVATE /* to import prototypes from crypto.h */ +#define TORTLS_PRIVATE #include "crypto.h" #include "tortls.h" #include "util.h" #include "torlog.h" #include "container.h" -#include "ht.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 @@ -68,9 +76,9 @@ #define ADDR(tls) (((tls) && (tls)->address) ? tls->address : "peer") -#if (OPENSSL_VERSION_NUMBER < 0x0090813fL || \ - (OPENSSL_VERSION_NUMBER >= 0x00909000L && \ - OPENSSL_VERSION_NUMBER < 0x1000006fL)) +#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-4576 fix, and as such it can't use RELEASE_BUFFERS and * SSL3 safely at the same time. @@ -96,22 +104,36 @@ static int use_unsafe_renegotiation_op = 0; * SSL3_FLAGS_ALLOW_UNSAFE_LEGACY_RENEGOTIATION? */ static int use_unsafe_renegotiation_flag = 0; +/** Structure that we use for a single certificate. */ +struct tor_cert_t { + X509 *cert; + uint8_t *encoded; + size_t encoded_len; + unsigned pkey_digests_set : 1; + digests_t cert_digests; + digests_t pkey_digests; +}; + /** Holds a SSL_CTX object and related state used to configure TLS * connections. */ typedef struct tor_tls_context_t { int refcnt; SSL_CTX *ctx; - X509 *my_cert; - X509 *my_id_cert; - crypto_pk_env_t *key; + tor_cert_t *my_link_cert; + tor_cert_t *my_id_cert; + tor_cert_t *my_auth_cert; + crypto_pk_t *link_key; + crypto_pk_t *auth_key; } tor_tls_context_t; +#define TOR_TLS_MAGIC 0x71571571 + /** Holds a SSL object and its associated data. Members are only * accessed from within tortls.c. */ struct tor_tls_t { - HT_ENTRY(tor_tls_t) node; + uint32_t magic; tor_tls_context_t *context; /** A link to the context object for this tls. */ SSL *ssl; /**< An OpenSSL SSL object. */ int socket; /**< The underlying file descriptor for this TLS connection. */ @@ -119,6 +141,7 @@ struct tor_tls_t { enum { TOR_TLS_ST_HANDSHAKE, TOR_TLS_ST_OPEN, TOR_TLS_ST_GOTCLOSE, TOR_TLS_ST_SENTCLOSE, TOR_TLS_ST_CLOSED, TOR_TLS_ST_RENEGOTIATE, + TOR_TLS_ST_BUFFEREVENT } state : 3; /**< The current SSL state, depending on which operations have * completed successfully. */ unsigned int isServer:1; /**< True iff this is a server-side connection */ @@ -127,8 +150,10 @@ struct tor_tls_t { * of the connection protocol (client sends * different cipher list, server sends only * one certificate). */ - /** True iff we should call negotiated_callback when we're done reading. */ + /** True iff we should call negotiated_callback when we're done reading. */ unsigned int got_renegotiate:1; + /** Incremented every time we start the server side of a handshake. */ + uint8_t server_handshake_count; size_t wantwrite_n; /**< 0 normally, >0 if we returned wantwrite last * time. */ /** Last values retrieved from BIO_number_read()/write(); see @@ -153,65 +178,55 @@ static SSL_CIPHER *CLIENT_CIPHER_DUMMIES = NULL; static STACK_OF(SSL_CIPHER) *CLIENT_CIPHER_STACK = NULL; #endif -/** Helper: compare tor_tls_t objects by its SSL. */ -static INLINE int -tor_tls_entries_eq(const tor_tls_t *a, const tor_tls_t *b) -{ - return a->ssl == b->ssl; -} +/** The ex_data index in which we store a pointer to an SSL object's + * corresponding tor_tls_t object. */ +static int tor_tls_object_ex_data_index = -1; -/** Helper: return a hash value for a tor_tls_t by its SSL. */ -static INLINE unsigned int -tor_tls_entry_hash(const tor_tls_t *a) +/** Helper: Allocate tor_tls_object_ex_data_index. */ +static void +tor_tls_allocate_tor_tls_object_ex_data_index(void) { -#if SIZEOF_INT == SIZEOF_VOID_P - return ((unsigned int)(uintptr_t)a->ssl); -#else - return (unsigned int) ((((uint64_t)a->ssl)>>2) & UINT_MAX); -#endif + if (tor_tls_object_ex_data_index == -1) { + tor_tls_object_ex_data_index = + SSL_get_ex_new_index(0, NULL, NULL, NULL, NULL); + tor_assert(tor_tls_object_ex_data_index != -1); + } } -/** Map from SSL* pointers to tor_tls_t objects using those pointers. - */ -static HT_HEAD(tlsmap, tor_tls_t) tlsmap_root = HT_INITIALIZER(); - -HT_PROTOTYPE(tlsmap, tor_tls_t, node, tor_tls_entry_hash, - tor_tls_entries_eq) -HT_GENERATE(tlsmap, tor_tls_t, node, tor_tls_entry_hash, - tor_tls_entries_eq, 0.6, malloc, realloc, free) - /** Helper: given a SSL* pointer, return the tor_tls_t object using that * pointer. */ static INLINE tor_tls_t * tor_tls_get_by_ssl(const SSL *ssl) { - tor_tls_t search, *result; - memset(&search, 0, sizeof(search)); - search.ssl = (SSL*)ssl; - result = HT_FIND(tlsmap, &tlsmap_root, &search); + tor_tls_t *result = SSL_get_ex_data(ssl, tor_tls_object_ex_data_index); + if (result) + tor_assert(result->magic == TOR_TLS_MAGIC); return result; } static void tor_tls_context_decref(tor_tls_context_t *ctx); static void tor_tls_context_incref(tor_tls_context_t *ctx); -static X509* tor_tls_create_certificate(crypto_pk_env_t *rsa, - crypto_pk_env_t *rsa_sign, +static X509* tor_tls_create_certificate(crypto_pk_t *rsa, + crypto_pk_t *rsa_sign, const char *cname, const char *cname_sign, unsigned int lifetime); -static void tor_tls_unblock_renegotiation(tor_tls_t *tls); + static int tor_tls_context_init_one(tor_tls_context_t **ppcontext, - crypto_pk_env_t *identity, + crypto_pk_t *identity, unsigned int key_lifetime, int is_client); -static tor_tls_context_t *tor_tls_context_new(crypto_pk_env_t *identity, +static tor_tls_context_t *tor_tls_context_new(crypto_pk_t *identity, unsigned int key_lifetime, int is_client); +static int check_cert_lifetime_internal(int severity, const X509 *cert, + int past_tolerance, int future_tolerance); /** Global TLS contexts. We keep them here because nobody else needs * to touch them. */ static tor_tls_context_t *server_tls_context = NULL; static tor_tls_context_t *client_tls_context = NULL; + /** True iff tor_tls_init() has been called. */ static int tls_library_is_initialized = 0; @@ -219,20 +234,82 @@ 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" +/** Write a description of the current state of <b>tls</b> into the + * <b>sz</b>-byte buffer at <b>buf</b>. */ +void +tor_tls_get_state_description(tor_tls_t *tls, char *buf, size_t sz) +{ + const char *ssl_state; + const char *tortls_state; + + if (PREDICT_UNLIKELY(!tls || !tls->ssl)) { + strlcpy(buf, "(No SSL object)", sz); + return; + } + + 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); + CASE(OPEN); + CASE(GOTCLOSE); + CASE(SENTCLOSE); + CASE(CLOSED); + CASE(RENEGOTIATE); +#undef CASE + case TOR_TLS_ST_BUFFEREVENT: + tortls_state = ""; + break; + default: + tortls_state = " in unknown TLS state"; + break; + } + + tor_snprintf(buf, sz, "%s%s", ssl_state, tortls_state); +} -/** Return the symbolic name of an OpenSSL state. */ -static const char * -ssl_state_to_string(int ssl_state) +void +tor_tls_log_one_error(tor_tls_t *tls, unsigned long err, + int severity, int domain, const char *doing) { - 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; + const char *state = NULL, *addr; + const char *msg, *lib, *func; + + state = (tls && tls->ssl)?SSL_state_string_long(tls->ssl):"---"; + + addr = tls ? tls->address : NULL; + + /* Some errors are known-benign, meaning they are the fault of the other + * side of the connection. The caller doesn't know this, so override the + * priority for those cases. */ + switch (ERR_GET_REASON(err)) { + case SSL_R_HTTP_REQUEST: + case SSL_R_HTTPS_PROXY_REQUEST: + case SSL_R_RECORD_LENGTH_MISMATCH: + case SSL_R_RECORD_TOO_LARGE: + case SSL_R_UNKNOWN_PROTOCOL: + case SSL_R_UNSUPPORTED_PROTOCOL: + severity = LOG_INFO; + break; + default: + break; + } + + msg = (const char*)ERR_reason_error_string(err); + lib = (const char*)ERR_lib_error_string(err); + func = (const char*)ERR_func_error_string(err); + if (!msg) msg = "(null)"; + if (!lib) lib = "(null)"; + if (!func) func = "(null)"; + if (doing) { + log(severity, domain, "TLS error while %s%s%s: %s (in %s:%s:%s)", + doing, addr?" with ":"", addr?addr:"", + msg, lib, func, state); + } else { + log(severity, domain, "TLS error%s%s: %s (in %s:%s:%s)", + addr?" with ":"", addr?addr:"", + msg, lib, func, state); } - tor_snprintf(buf, sizeof(buf), "Unknown state %d", ssl_state); - return buf; } /** Log all pending tls errors at level <b>severity</b>. Use @@ -241,30 +318,10 @@ ssl_state_to_string(int ssl_state) static void tls_log_errors(tor_tls_t *tls, int severity, int domain, const char *doing) { - const char *state = NULL; - int st; unsigned long err; - const char *msg, *lib, *func, *addr; - addr = tls ? tls->address : NULL; - st = (tls && tls->ssl) ? tls->ssl->state : -1; + while ((err = ERR_get_error()) != 0) { - msg = (const char*)ERR_reason_error_string(err); - lib = (const char*)ERR_lib_error_string(err); - func = (const char*)ERR_func_error_string(err); - if (!state) - state = (st>=0)?ssl_state_to_string(st):"---"; - if (!msg) msg = "(null)"; - if (!lib) lib = "(null)"; - if (!func) func = "(null)"; - if (doing) { - log(severity, domain, "TLS error while %s%s%s: %s (in %s:%s:%s)", - doing, addr?" with ":"", addr?addr:"", - msg, lib, func, state); - } else { - log(severity, domain, "TLS error%s%s: %s (in %s:%s:%s)", - addr?" with ":"", addr?addr:"", - msg, lib, func, state); - } + tor_tls_log_one_error(tls, err, severity, domain, doing); } } @@ -273,7 +330,7 @@ tls_log_errors(tor_tls_t *tls, int severity, int domain, const char *doing) static int tor_errno_to_tls_error(int e) { -#if defined(MS_WINDOWS) +#if defined(_WIN32) switch (e) { case WSAECONNRESET: // most common return TOR_TLS_ERROR_CONNRESET; @@ -355,14 +412,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); @@ -371,7 +428,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: @@ -414,18 +471,20 @@ 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) { - log_notice(LD_GENERAL, "OpenSSL %s looks like version 0.9.8l; " - "I will try SSL3_FLAGS to enable renegotation.", + 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, but " + "some vendors have backported renegotiation code from " + "0.9.8m without updating the version number. " + "I will try SSL3_FLAGS and SSL_OP 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 " @@ -435,10 +494,13 @@ 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); } + tor_tls_allocate_tor_tls_object_ex_data_index(); + tls_library_is_initialized = 1; } } @@ -457,10 +519,6 @@ tor_tls_free_all(void) client_tls_context = NULL; tor_tls_context_decref(ctx); } - if (!HT_EMPTY(&tlsmap_root)) { - log_warn(LD_MM, "Still have entries in the tlsmap at shutdown."); - } - HT_CLEAR(tlsmap, &tlsmap_root); #ifdef V2_HANDSHAKE_CLIENT if (CLIENT_CIPHER_DUMMIES) tor_free(CLIENT_CIPHER_DUMMIES); @@ -508,13 +566,19 @@ tor_x509_name_new(const char *cname) * failure. */ static X509 * -tor_tls_create_certificate(crypto_pk_env_t *rsa, - crypto_pk_env_t *rsa_sign, +tor_tls_create_certificate(crypto_pk_t *rsa, + crypto_pk_t *rsa_sign, const char *cname, 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; @@ -527,16 +591,23 @@ tor_tls_create_certificate(crypto_pk_env_t *rsa, tor_assert(cname); tor_assert(rsa_sign); tor_assert(cname_sign); - if (!(sign_pkey = _crypto_pk_env_get_evp_pkey(rsa_sign,1))) + if (!(sign_pkey = _crypto_pk_get_evp_pkey(rsa_sign,1))) goto error; - if (!(pkey = _crypto_pk_env_get_evp_pkey(rsa,0))) + if (!(pkey = _crypto_pk_get_evp_pkey(rsa,0))) goto error; if (!(x509 = X509_new())) 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; @@ -569,11 +640,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.*/ @@ -620,6 +695,137 @@ static const int N_CLIENT_CIPHERS = SSL3_TXT_EDH_RSA_DES_192_CBC3_SHA) #endif +/** Free all storage held in <b>cert</b> */ +void +tor_cert_free(tor_cert_t *cert) +{ + if (! cert) + return; + if (cert->cert) + X509_free(cert->cert); + tor_free(cert->encoded); + memset(cert, 0x03, sizeof(*cert)); + tor_free(cert); +} + +/** + * Allocate a new tor_cert_t to hold the certificate "x509_cert". + * + * Steals a reference to x509_cert. + */ +static tor_cert_t * +tor_cert_new(X509 *x509_cert) +{ + tor_cert_t *cert; + EVP_PKEY *pkey; + RSA *rsa; + int length, length2; + unsigned char *cp; + + if (!x509_cert) + return NULL; + + length = i2d_X509(x509_cert, NULL); + cert = tor_malloc_zero(sizeof(tor_cert_t)); + if (length <= 0) { + tor_free(cert); + log_err(LD_CRYPTO, "Couldn't get length of encoded x509 certificate"); + X509_free(x509_cert); + return NULL; + } + cert->encoded_len = (size_t) length; + cp = cert->encoded = tor_malloc(length); + length2 = i2d_X509(x509_cert, &cp); + tor_assert(length2 == length); + + cert->cert = x509_cert; + + crypto_digest_all(&cert->cert_digests, + (char*)cert->encoded, cert->encoded_len); + + if ((pkey = X509_get_pubkey(x509_cert)) && + (rsa = EVP_PKEY_get1_RSA(pkey))) { + crypto_pk_t *pk = _crypto_new_pk_from_rsa(rsa); + crypto_pk_get_all_digests(pk, &cert->pkey_digests); + cert->pkey_digests_set = 1; + crypto_pk_free(pk); + EVP_PKEY_free(pkey); + } + + return cert; +} + +/** Read a DER-encoded X509 cert, of length exactly <b>certificate_len</b>, + * from a <b>certificate</b>. Return a newly allocated tor_cert_t on success + * and NULL on failure. */ +tor_cert_t * +tor_cert_decode(const uint8_t *certificate, size_t certificate_len) +{ + X509 *x509; + const unsigned char *cp = (const unsigned char *)certificate; + tor_cert_t *newcert; + tor_assert(certificate); + + if (certificate_len > INT_MAX) + return NULL; + +#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); +#else + x509 = d2i_X509(NULL, &cp, (int)certificate_len); +#endif + if (!x509) + return NULL; /* Couldn't decode */ + if (cp - certificate != (int)certificate_len) { + X509_free(x509); + return NULL; /* Didn't use all the bytes */ + } + newcert = tor_cert_new(x509); + if (!newcert) { + return NULL; + } + if (newcert->encoded_len != certificate_len || + fast_memneq(newcert->encoded, certificate, certificate_len)) { + /* Cert wasn't in DER */ + tor_cert_free(newcert); + return NULL; + } + return newcert; +} + +/** Set *<b>encoded_out</b> and *<b>size_out/b> to <b>cert</b>'s encoded DER + * representation and length, respectively. */ +void +tor_cert_get_der(const tor_cert_t *cert, + const uint8_t **encoded_out, size_t *size_out) +{ + tor_assert(cert); + tor_assert(encoded_out); + tor_assert(size_out); + *encoded_out = cert->encoded; + *size_out = cert->encoded_len; +} + +/** Return a set of digests for the public key in <b>cert</b>, or NULL if this + * cert's public key is not one we know how to take the digest of. */ +const digests_t * +tor_cert_get_id_digests(const tor_cert_t *cert) +{ + if (cert->pkey_digests_set) + return &cert->pkey_digests; + else + return NULL; +} + +/** Return a set of digests for the public key in <b>cert</b>. */ +const digests_t * +tor_cert_get_cert_digests(const tor_cert_t *cert) +{ + return &cert->cert_digests; +} + /** Remove a reference to <b>ctx</b>, and free it if it has no more * references. */ static void @@ -628,13 +834,172 @@ tor_tls_context_decref(tor_tls_context_t *ctx) tor_assert(ctx); if (--ctx->refcnt == 0) { SSL_CTX_free(ctx->ctx); - X509_free(ctx->my_cert); - X509_free(ctx->my_id_cert); - crypto_free_pk_env(ctx->key); + tor_cert_free(ctx->my_link_cert); + tor_cert_free(ctx->my_id_cert); + tor_cert_free(ctx->my_auth_cert); + crypto_pk_free(ctx->link_key); + crypto_pk_free(ctx->auth_key); tor_free(ctx); } } +/** Set *<b>link_cert_out</b> and *<b>id_cert_out</b> to the link certificate + * and ID certificate that we're currently using for our V3 in-protocol + * handshake's certificate chain. If <b>server</b> is true, provide the certs + * that we use in server mode; otherwise, provide the certs that we use in + * client mode. */ +int +tor_tls_get_my_certs(int server, + const tor_cert_t **link_cert_out, + const tor_cert_t **id_cert_out) +{ + tor_tls_context_t *ctx = server ? server_tls_context : client_tls_context; + if (! ctx) + return -1; + if (link_cert_out) + *link_cert_out = server ? ctx->my_link_cert : ctx->my_auth_cert; + if (id_cert_out) + *id_cert_out = ctx->my_id_cert; + return 0; +} + +/** + * Return the authentication key that we use to authenticate ourselves as a + * client in the V3 in-protocol handshake. + */ +crypto_pk_t * +tor_tls_get_my_client_auth_key(void) +{ + if (! client_tls_context) + return NULL; + return client_tls_context->auth_key; +} + +/** + * Return a newly allocated copy of the public key that a certificate + * certifies. Return NULL if the cert's key is not RSA. + */ +crypto_pk_t * +tor_tls_cert_get_key(tor_cert_t *cert) +{ + crypto_pk_t *result = NULL; + EVP_PKEY *pkey = X509_get_pubkey(cert->cert); + RSA *rsa; + if (!pkey) + return NULL; + rsa = EVP_PKEY_get1_RSA(pkey); + if (!rsa) { + EVP_PKEY_free(pkey); + return NULL; + } + result = _crypto_new_pk_from_rsa(rsa); + EVP_PKEY_free(pkey); + return result; +} + +/** Return true iff <b>a</b> and <b>b</b> represent the same public key. */ +static int +pkey_eq(EVP_PKEY *a, EVP_PKEY *b) +{ + /* We'd like to do this, but openssl 0.9.7 doesn't have it: + return EVP_PKEY_cmp(a,b) == 1; + */ + unsigned char *a_enc=NULL, *b_enc=NULL, *a_ptr, *b_ptr; + int a_len1, b_len1, a_len2, b_len2, result; + a_len1 = i2d_PublicKey(a, NULL); + b_len1 = i2d_PublicKey(b, NULL); + if (a_len1 != b_len1) + return 0; + a_ptr = a_enc = tor_malloc(a_len1); + b_ptr = b_enc = tor_malloc(b_len1); + a_len2 = i2d_PublicKey(a, &a_ptr); + b_len2 = i2d_PublicKey(b, &b_ptr); + tor_assert(a_len2 == a_len1); + tor_assert(b_len2 == b_len1); + result = tor_memeq(a_enc, b_enc, a_len1); + tor_free(a_enc); + tor_free(b_enc); + return result; +} + +/** Return true iff the other side of <b>tls</b> has authenticated to us, and + * the key certified in <b>cert</b> is the same as the key they used to do it. + */ +int +tor_tls_cert_matches_key(const tor_tls_t *tls, const tor_cert_t *cert) +{ + X509 *peercert = SSL_get_peer_certificate(tls->ssl); + EVP_PKEY *link_key = NULL, *cert_key = NULL; + int result; + + if (!peercert) + return 0; + link_key = X509_get_pubkey(peercert); + cert_key = X509_get_pubkey(cert->cert); + + result = link_key && cert_key && pkey_eq(cert_key, link_key); + + X509_free(peercert); + if (link_key) + EVP_PKEY_free(link_key); + if (cert_key) + EVP_PKEY_free(cert_key); + + return result; +} + +/** Check whether <b>cert</b> is well-formed, currently live, and correctly + * signed by the public key in <b>signing_cert</b>. If <b>check_rsa_1024</b>, + * make sure that it has an RSA key with 1024 bits; otherwise, just check that + * the key is long enough. Return 1 if the cert is good, and 0 if it's bad or + * we couldn't check it. */ +int +tor_tls_cert_is_valid(int severity, + const tor_cert_t *cert, + const tor_cert_t *signing_cert, + int check_rsa_1024) +{ + EVP_PKEY *cert_key; + EVP_PKEY *signing_key = X509_get_pubkey(signing_cert->cert); + int r, key_ok = 0; + if (!signing_key) + return 0; + r = X509_verify(cert->cert, signing_key); + EVP_PKEY_free(signing_key); + if (r <= 0) + return 0; + + /* okay, the signature checked out right. Now let's check the check the + * lifetime. */ + if (check_cert_lifetime_internal(severity, cert->cert, + 48*60*60, 30*24*60*60) < 0) + return 0; + + cert_key = X509_get_pubkey(cert->cert); + if (check_rsa_1024 && cert_key) { + RSA *rsa = EVP_PKEY_get1_RSA(cert_key); + if (rsa && BN_num_bits(rsa->n) == 1024) + key_ok = 1; + if (rsa) + RSA_free(rsa); + } else if (cert_key) { + int min_bits = 1024; +#ifdef EVP_PKEY_EC + if (EVP_PKEY_type(cert_key->type) == EVP_PKEY_EC) + min_bits = 128; +#endif + if (EVP_PKEY_bits(cert_key) >= min_bits) + key_ok = 1; + } + EVP_PKEY_free(cert_key); + if (!key_ok) + return 0; + + /* XXXX compare DNs or anything? */ + + return 1; +} + /** Increase the reference count of <b>ctx</b>. */ static void tor_tls_context_incref(tor_tls_context_t *ctx) @@ -650,8 +1015,8 @@ tor_tls_context_incref(tor_tls_context_t *ctx) * ignore <b>client_identity</b>. */ int tor_tls_context_init(int is_public_server, - crypto_pk_env_t *client_identity, - crypto_pk_env_t *server_identity, + crypto_pk_t *client_identity, + crypto_pk_t *server_identity, unsigned int key_lifetime) { int rv1 = 0; @@ -709,7 +1074,7 @@ tor_tls_context_init(int is_public_server, */ static int tor_tls_context_init_one(tor_tls_context_t **ppcontext, - crypto_pk_env_t *identity, + crypto_pk_t *identity, unsigned int key_lifetime, int is_client) { @@ -737,32 +1102,45 @@ tor_tls_context_init_one(tor_tls_context_t **ppcontext, * certificate. */ static tor_tls_context_t * -tor_tls_context_new(crypto_pk_env_t *identity, unsigned int key_lifetime, +tor_tls_context_new(crypto_pk_t *identity, unsigned int key_lifetime, int is_client) { - crypto_pk_env_t *rsa = NULL; + crypto_pk_t *rsa = NULL, *rsa_auth = NULL; EVP_PKEY *pkey = NULL; tor_tls_context_t *result = NULL; - X509 *cert = NULL, *idcert = NULL; + X509 *cert = NULL, *idcert = NULL, *authcert = NULL; char *nickname = NULL, *nn2 = NULL; tor_tls_init(); nickname = crypto_random_hostname(8, 20, "www.", ".net"); +#ifdef DISABLE_V3_LINKPROTO_SERVERSIDE nn2 = crypto_random_hostname(8, 20, "www.", ".net"); +#else + nn2 = crypto_random_hostname(8, 20, "www.", ".com"); +#endif - /* Generate short-term RSA key. */ - if (!(rsa = crypto_new_pk_env())) + /* Generate short-term RSA key for use with TLS. */ + if (!(rsa = crypto_pk_new())) goto error; if (crypto_pk_generate_key(rsa)<0) goto error; if (!is_client) { - /* Create certificate signed by identity key. */ + /* Generate short-term RSA key for use in the in-protocol ("v3") + * authentication handshake. */ + if (!(rsa_auth = crypto_pk_new())) + goto error; + if (crypto_pk_generate_key(rsa_auth)<0) + goto error; + /* Create a link certificate signed by identity key. */ cert = tor_tls_create_certificate(rsa, identity, nickname, nn2, key_lifetime); /* Create self-signed certificate for identity key. */ idcert = tor_tls_create_certificate(identity, identity, nn2, nn2, IDENTITY_CERT_LIFETIME); - if (!cert || !idcert) { + /* Create an authentication certificate signed by identity key. */ + authcert = tor_tls_create_certificate(rsa_auth, identity, nickname, nn2, + key_lifetime); + if (!cert || !idcert || !authcert) { log(LOG_WARN, LD_CRYPTO, "Error creating certificate"); goto error; } @@ -771,9 +1149,13 @@ tor_tls_context_new(crypto_pk_env_t *identity, unsigned int key_lifetime, result = tor_malloc_zero(sizeof(tor_tls_context_t)); result->refcnt = 1; if (!is_client) { - result->my_cert = X509_dup(cert); - result->my_id_cert = X509_dup(idcert); - result->key = crypto_pk_dup_key(rsa); + result->my_link_cert = tor_cert_new(X509_dup(cert)); + result->my_id_cert = tor_cert_new(X509_dup(idcert)); + result->my_auth_cert = tor_cert_new(X509_dup(authcert)); + if (!result->my_link_cert || !result->my_id_cert || !result->my_auth_cert) + goto error; + result->link_key = crypto_pk_dup_key(rsa); + result->auth_key = crypto_pk_dup_key(rsa_auth); } #if 0 @@ -794,9 +1176,9 @@ tor_tls_context_new(crypto_pk_env_t *identity, unsigned int key_lifetime, #ifdef DISABLE_SSL3_HANDSHAKE 1 || #endif - SSLeay() < 0x0090813fL || - (SSLeay() >= 0x00909000L && - SSLeay() < 0x1000006fL)) { + 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-4576. */ log_info(LD_NET, "Disabling SSLv3 because this OpenSSL version " "might otherwise be vulnerable to CVE-2011-4576 " @@ -843,7 +1225,7 @@ tor_tls_context_new(crypto_pk_env_t *identity, unsigned int key_lifetime, SSL_CTX_set_session_cache_mode(result->ctx, SSL_SESS_CACHE_OFF); if (!is_client) { tor_assert(rsa); - if (!(pkey = _crypto_pk_env_get_evp_pkey(rsa,1))) + if (!(pkey = _crypto_pk_get_evp_pkey(rsa,1))) goto error; if (!SSL_CTX_use_PrivateKey(result->ctx, pkey)) goto error; @@ -853,9 +1235,9 @@ tor_tls_context_new(crypto_pk_env_t *identity, unsigned int key_lifetime, goto error; } { - crypto_dh_env_t *dh = crypto_dh_new(DH_TYPE_TLS); + crypto_dh_t *dh = crypto_dh_new(DH_TYPE_TLS); tor_assert(dh); - SSL_CTX_set_tmp_dh(result->ctx, _crypto_dh_env_get_dh(dh)); + SSL_CTX_set_tmp_dh(result->ctx, _crypto_dh_get_dh(dh)); crypto_dh_free(dh); } SSL_CTX_set_verify(result->ctx, SSL_VERIFY_PEER, @@ -864,7 +1246,10 @@ tor_tls_context_new(crypto_pk_env_t *identity, unsigned int key_lifetime, SSL_CTX_set_mode(result->ctx, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER); if (rsa) - crypto_free_pk_env(rsa); + crypto_pk_free(rsa); + if (rsa_auth) + crypto_pk_free(rsa_auth); + X509_free(authcert); tor_free(nickname); tor_free(nn2); return result; @@ -876,13 +1261,17 @@ tor_tls_context_new(crypto_pk_env_t *identity, unsigned int key_lifetime, if (pkey) EVP_PKEY_free(pkey); if (rsa) - crypto_free_pk_env(rsa); + crypto_pk_free(rsa); + if (rsa_auth) + crypto_pk_free(rsa_auth); if (result) tor_tls_context_decref(result); if (cert) X509_free(cert); if (idcert) X509_free(idcert); + if (authcert) + X509_free(authcert); return NULL; } @@ -922,7 +1311,7 @@ tor_tls_client_is_using_v2_ciphers(const SSL *ssl, const char *address) return 0; dump_list: { - smartlist_t *elts = smartlist_create(); + smartlist_t *elts = smartlist_new(); char *s; for (i = 0; i < sk_SSL_CIPHER_num(session->ciphers); ++i) { SSL_CIPHER *cipher = sk_SSL_CIPHER_value(session->ciphers, i); @@ -938,6 +1327,13 @@ tor_tls_client_is_using_v2_ciphers(const SSL *ssl, const char *address) return 1; } +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_string_long(ssl), type, val); +} + /** Invoked when we're accepting a connection on <b>ssl</b>, and the connection * changes state. We use this: * <ul><li>To alter the state of the handshake partway through, so we @@ -949,6 +1345,9 @@ tor_tls_server_info_callback(const SSL *ssl, int type, int val) { tor_tls_t *tls; (void) val; + + tor_tls_debug_state_callback(ssl, type, val); + if (type != SSL_CB_ACCEPT_LOOP) return; if (ssl->state != SSL3_ST_SW_SRVR_HELLO_A) @@ -959,13 +1358,18 @@ tor_tls_server_info_callback(const SSL *ssl, int type, int val) /* Check whether we're watching for renegotiates. If so, this is one! */ if (tls->negotiated_callback) tls->got_renegotiate = 1; + if (tls->server_handshake_count < 127) /*avoid any overflow possibility*/ + ++tls->server_handshake_count; } else { log_warn(LD_BUG, "Couldn't look up the tls for an SSL*. How odd!"); + return; } /* Now check the cipher list. */ if (tor_tls_client_is_using_v2_ciphers(ssl, ADDR(tls))) { - /*XXXX_TLS keep this from happening more than once! */ + if (tls->wasV2Handshake) + return; /* We already turned this stuff off for the first handshake; + * This is a renegotiation. */ /* Yes, we're casting away the const from ssl. This is very naughty of us. * Let's hope openssl doesn't notice! */ @@ -977,6 +1381,10 @@ tor_tls_server_info_callback(const SSL *ssl, int type, int val) if (tls) { tls->wasV2Handshake = 1; +#ifdef USE_BUFFEREVENTS + if (use_unsafe_renegotiation_flag) + tls->ssl->s3->flags |= SSL3_FLAGS_ALLOW_UNSAFE_LEGACY_RENEGOTIATION; +#endif } else { log_warn(LD_BUG, "Couldn't look up the tls for an SSL*. How odd!"); } @@ -1064,6 +1472,7 @@ tor_tls_new(int sock, int isServer) tor_tls_t *result = tor_malloc_zero(sizeof(tor_tls_t)); tor_tls_context_t *context = isServer ? server_tls_context : client_tls_context; + result->magic = TOR_TLS_MAGIC; tor_assert(context); /* make sure somebody made it first */ if (!(result->ssl = SSL_new(context->ctx))) { @@ -1104,7 +1513,14 @@ tor_tls_new(int sock, int isServer) tor_free(result); return NULL; } - HT_INSERT(tlsmap, &tlsmap_root, result); + { + int set_worked = + SSL_set_ex_data(result->ssl, tor_tls_object_ex_data_index, result); + if (!set_worked) { + log_warn(LD_BUG, + "Couldn't set the tls for an SSL*; connection will fail"); + } + } SSL_set_bio(result->ssl, bio, bio); tor_tls_context_incref(context); result->context = context; @@ -1120,8 +1536,11 @@ tor_tls_new(int sock, int isServer) #ifdef V2_HANDSHAKE_SERVER if (isServer) { SSL_set_info_callback(result->ssl, tor_tls_server_info_callback); - } + } else #endif + { + SSL_set_info_callback(result->ssl, tor_tls_debug_state_callback); + } /* Not expected to get called. */ tls_log_errors(NULL, LOG_WARN, LD_NET, "creating tor_tls_t object"); @@ -1155,7 +1574,7 @@ tor_tls_set_renegotiate_callback(tor_tls_t *tls, if (cb) { SSL_set_info_callback(tls->ssl, tor_tls_server_info_callback); } else { - SSL_set_info_callback(tls->ssl, NULL); + SSL_set_info_callback(tls->ssl, tor_tls_debug_state_callback); } #endif } @@ -1163,7 +1582,7 @@ tor_tls_set_renegotiate_callback(tor_tls_t *tls, /** If this version of openssl requires it, turn on renegotiation on * <b>tls</b>. */ -static void +void tor_tls_unblock_renegotiation(tor_tls_t *tls) { /* Yes, we know what we are doing here. No, we do not treat a renegotiation @@ -1187,6 +1606,19 @@ tor_tls_block_renegotiation(tor_tls_t *tls) tls->ssl->s3->flags &= ~SSL3_FLAGS_ALLOW_UNSAFE_LEGACY_RENEGOTIATION; } +void +tor_tls_assert_renegotiation_unblocked(tor_tls_t *tls) +{ + if (use_unsafe_renegotiation_flag) { + tor_assert(0 != (tls->ssl->s3->flags & + SSL3_FLAGS_ALLOW_UNSAFE_LEGACY_RENEGOTIATION)); + } + if (use_unsafe_renegotiation_op) { + long options = SSL_get_options(tls->ssl); + tor_assert(0 != (options & SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION)); + } +} + /** Return whether this tls initiated the connect (client) or * received it (server). */ int @@ -1202,14 +1634,9 @@ tor_tls_is_server(tor_tls_t *tls) void tor_tls_free(tor_tls_t *tls) { - tor_tls_t *removed; if (!tls) return; tor_assert(tls->ssl); - removed = HT_REMOVE(tlsmap, &tlsmap_root, tls); - if (!removed) { - log_warn(LD_BUG, "Freeing a TLS that was not in the ssl->tls map."); - } #ifdef SSL_set_tlsext_host_name SSL_set_tlsext_host_name(tls->ssl, NULL); #endif @@ -1219,6 +1646,7 @@ tor_tls_free(tor_tls_t *tls) if (tls->context) tor_tls_context_decref(tls->context); tor_free(tls->address); + tls->magic = 0x99999999; tor_free(tls); } @@ -1310,16 +1738,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); @@ -1331,56 +1759,86 @@ tor_tls_handshake(tor_tls_t *tls) } if (r == TOR_TLS_DONE) { tls->state = TOR_TLS_ST_OPEN; - if (tls->isServer) { - SSL_set_info_callback(tls->ssl, NULL); - SSL_set_verify(tls->ssl, SSL_VERIFY_PEER, always_accept_verify_cb); - /* There doesn't seem to be a clear OpenSSL API to clear mode flags. */ - tls->ssl->mode &= ~SSL_MODE_NO_AUTO_CHAIN; + return tor_tls_finish_handshake(tls); + } + return r; +} + +/** Perform the final part of the intial TLS handshake on <b>tls</b>. This + * should be called for the first handshake only: it determines whether the v1 + * or the v2 handshake was used, and adjusts things for the renegotiation + * handshake as appropriate. + * + * tor_tls_handshake() calls this on its own; you only need to call this if + * bufferevent is doing the handshake for you. + */ +int +tor_tls_finish_handshake(tor_tls_t *tls) +{ + int r = TOR_TLS_DONE; + if (tls->isServer) { + SSL_set_info_callback(tls->ssl, NULL); + SSL_set_verify(tls->ssl, SSL_VERIFY_PEER, always_accept_verify_cb); + /* There doesn't seem to be a clear OpenSSL API to clear mode flags. */ + tls->ssl->mode &= ~SSL_MODE_NO_AUTO_CHAIN; #ifdef V2_HANDSHAKE_SERVER - if (tor_tls_client_is_using_v2_ciphers(tls->ssl, ADDR(tls))) { - /* This check is redundant, but back when we did it in the callback, - * we might have not been able to look up the tor_tls_t if the code - * was buggy. Fixing that. */ - if (!tls->wasV2Handshake) { - log_warn(LD_BUG, "For some reason, wasV2Handshake didn't" - " get set. Fixing that."); - } - tls->wasV2Handshake = 1; - log_debug(LD_HANDSHAKE, - "Completed V2 TLS handshake with client; waiting " - "for renegotiation."); - } else { - tls->wasV2Handshake = 0; + if (tor_tls_client_is_using_v2_ciphers(tls->ssl, ADDR(tls))) { + /* This check is redundant, but back when we did it in the callback, + * we might have not been able to look up the tor_tls_t if the code + * was buggy. Fixing that. */ + if (!tls->wasV2Handshake) { + log_warn(LD_BUG, "For some reason, wasV2Handshake didn't" + " get set. Fixing that."); } -#endif + tls->wasV2Handshake = 1; + log_debug(LD_HANDSHAKE, "Completed V2 TLS handshake with client; waiting" + " for renegotiation."); } else { + tls->wasV2Handshake = 0; + } +#endif + } else { #ifdef V2_HANDSHAKE_CLIENT - /* If we got no ID cert, we're a v2 handshake. */ - X509 *cert = SSL_get_peer_certificate(tls->ssl); - STACK_OF(X509) *chain = SSL_get_peer_cert_chain(tls->ssl); - int n_certs = sk_X509_num(chain); - if (n_certs > 1 || (n_certs == 1 && cert != sk_X509_value(chain, 0))) { - log_debug(LD_HANDSHAKE, "Server sent back multiple certificates; it " - "looks like a v1 handshake on %p", tls); - tls->wasV2Handshake = 0; - } else { - log_debug(LD_HANDSHAKE, - "Server sent back a single certificate; looks like " - "a v2 handshake on %p.", tls); - tls->wasV2Handshake = 1; - } - if (cert) - X509_free(cert); + /* If we got no ID cert, we're a v2 handshake. */ + X509 *cert = SSL_get_peer_certificate(tls->ssl); + STACK_OF(X509) *chain = SSL_get_peer_cert_chain(tls->ssl); + int n_certs = sk_X509_num(chain); + if (n_certs > 1 || (n_certs == 1 && cert != sk_X509_value(chain, 0))) { + log_debug(LD_HANDSHAKE, "Server sent back multiple certificates; it " + "looks like a v1 handshake on %p", tls); + tls->wasV2Handshake = 0; + } else { + log_debug(LD_HANDSHAKE, + "Server sent back a single certificate; looks like " + "a v2 handshake on %p.", tls); + tls->wasV2Handshake = 1; + } + if (cert) + X509_free(cert); #endif - if (SSL_set_cipher_list(tls->ssl, SERVER_CIPHER_LIST) == 0) { - tls_log_errors(NULL, LOG_WARN, LD_HANDSHAKE, "re-setting ciphers"); - r = TOR_TLS_ERROR_MISC; - } + if (SSL_set_cipher_list(tls->ssl, SERVER_CIPHER_LIST) == 0) { + tls_log_errors(NULL, LOG_WARN, LD_HANDSHAKE, "re-setting ciphers"); + r = TOR_TLS_ERROR_MISC; } } return r; } +#ifdef USE_BUFFEREVENTS +/** Put <b>tls</b>, which must be a client connection, into renegotiation + * mode. */ +int +tor_tls_start_renegotiating(tor_tls_t *tls) +{ + int r = SSL_renegotiate(tls->ssl); + if (r <= 0) { + return tor_tls_get_error(tls, r, 0, "renegotiating", LOG_WARN, + LD_HANDSHAKE); + } + return 0; +} +#endif + /** Client only: Renegotiate a TLS session. When finished, returns * TOR_TLS_DONE. On failure, returns TOR_TLS_ERROR, TOR_TLS_WANTREAD, or * TOR_TLS_WANTWRITE. @@ -1486,9 +1944,21 @@ tor_tls_peer_has_cert(tor_tls_t *tls) return 1; } +/** Return the peer certificate, or NULL if there isn't one. */ +tor_cert_t * +tor_tls_get_peer_cert(tor_tls_t *tls) +{ + X509 *cert; + cert = SSL_get_peer_certificate(tls->ssl); + tls_log_errors(tls, LOG_WARN, LD_HANDSHAKE, "getting peer certificate"); + if (!cert) + return NULL; + return tor_cert_new(cert); +} + /** Warn that a certificate lifetime extends through a certain range. */ static void -log_cert_lifetime(X509 *cert, const char *problem) +log_cert_lifetime(int severity, const X509 *cert, const char *problem) { BIO *bio = NULL; BUF_MEM *buf; @@ -1498,9 +1968,10 @@ log_cert_lifetime(X509 *cert, const char *problem) struct tm tm; if (problem) - log_warn(LD_GENERAL, - "Certificate %s: is your system clock set incorrectly?", - problem); + log(severity, LD_GENERAL, + "Certificate %s. Either their clock is set wrong, or your clock " + "is wrong.", + problem); if (!(bio = BIO_new(BIO_s_mem()))) { log_warn(LD_GENERAL, "Couldn't allocate BIO!"); goto end; @@ -1522,9 +1993,9 @@ log_cert_lifetime(X509 *cert, const char *problem) strftime(mytime, 32, "%b %d %H:%M:%S %Y GMT", tor_gmtime_r(&now, &tm)); - log_warn(LD_GENERAL, - "(certificate lifetime runs from %s through %s. Your time is %s.)", - s1,s2,mytime); + log(severity, LD_GENERAL, + "(certificate lifetime runs from %s through %s. Your time is %s.)", + s1,s2,mytime); end: /* Not expected to get invoked */ @@ -1581,7 +2052,7 @@ try_to_extract_certs_from_tls(int severity, tor_tls_t *tls, * 0. Else, return -1 and log complaints with log-level <b>severity</b>. */ int -tor_tls_verify(int severity, tor_tls_t *tls, crypto_pk_env_t **identity_key) +tor_tls_verify(int severity, tor_tls_t *tls, crypto_pk_t **identity_key) { X509 *cert = NULL, *id_cert = NULL; EVP_PKEY *id_pkey = NULL; @@ -1597,6 +2068,8 @@ tor_tls_verify(int severity, tor_tls_t *tls, crypto_pk_env_t **identity_key) log_fn(severity,LD_PROTOCOL,"No distinct identity certificate found"); goto done; } + tls_log_errors(tls, severity, LD_HANDSHAKE, "before verifying certificate"); + if (!(id_pkey = X509_get_pubkey(id_cert)) || X509_verify(cert, id_pkey) <= 0) { log_fn(severity,LD_PROTOCOL,"X509_verify on cert and pkey returned <= 0"); @@ -1607,7 +2080,7 @@ tor_tls_verify(int severity, tor_tls_t *tls, crypto_pk_env_t **identity_key) rsa = EVP_PKEY_get1_RSA(id_pkey); if (!rsa) goto done; - *identity_key = _crypto_new_pk_env_rsa(rsa); + *identity_key = _crypto_new_pk_from_rsa(rsa); r = 0; @@ -1624,34 +2097,25 @@ tor_tls_verify(int severity, tor_tls_t *tls, crypto_pk_env_t **identity_key) return r; } -/** Check whether the certificate set on the connection <b>tls</b> is - * expired or not-yet-valid, give or take <b>tolerance</b> - * seconds. Return 0 for valid, -1 for failure. +/** Check whether the certificate set on the connection <b>tls</b> is expired + * give or take <b>past_tolerance</b> seconds, or not-yet-valid give or take + * <b>future_tolerance</b> seconds. Return 0 for valid, -1 for failure. * * NOTE: you should call tor_tls_verify before tor_tls_check_lifetime. */ int -tor_tls_check_lifetime(tor_tls_t *tls, int tolerance) +tor_tls_check_lifetime(int severity, tor_tls_t *tls, + int past_tolerance, int future_tolerance) { - time_t now, t; X509 *cert; int r = -1; - now = time(NULL); - if (!(cert = SSL_get_peer_certificate(tls->ssl))) goto done; - t = now + tolerance; - if (X509_cmp_time(X509_get_notBefore(cert), &t) > 0) { - log_cert_lifetime(cert, "not yet valid"); + if (check_cert_lifetime_internal(severity, cert, + past_tolerance, future_tolerance) < 0) goto done; - } - t = now - tolerance; - if (X509_cmp_time(X509_get_notAfter(cert), &t) < 0) { - log_cert_lifetime(cert, "already expired"); - goto done; - } r = 0; done: @@ -1663,6 +2127,32 @@ tor_tls_check_lifetime(tor_tls_t *tls, int tolerance) return r; } +/** Helper: check whether <b>cert</b> is expired give or take + * <b>past_tolerance</b> seconds, or not-yet-valid give or take + * <b>future_tolerance</b> seconds. If it is live, return 0. If it is not + * live, log a message and return -1. */ +static int +check_cert_lifetime_internal(int severity, const X509 *cert, + int past_tolerance, int future_tolerance) +{ + time_t now, t; + + now = time(NULL); + + t = now + future_tolerance; + if (X509_cmp_time(X509_get_notBefore(cert), &t) > 0) { + log_cert_lifetime(severity, cert, "not yet valid"); + return -1; + } + t = now - past_tolerance; + if (X509_cmp_time(X509_get_notAfter(cert), &t) < 0) { + log_cert_lifetime(severity, cert, "already expired"); + return -1; + } + + return 0; +} + /** Return the number of bytes available for reading from <b>tls</b>. */ int @@ -1746,6 +2236,138 @@ tor_tls_used_v1_handshake(tor_tls_t *tls) return 1; } +/** Return true iff <b>name</b> is a DN of a kind that could only + * occur in a v3-handshake-indicating certificate */ +static int +dn_indicates_v3_cert(X509_NAME *name) +{ +#ifdef DISABLE_V3_LINKPROTO_CLIENTSIDE + (void)name; + return 0; +#else + X509_NAME_ENTRY *entry; + int n_entries; + ASN1_OBJECT *obj; + ASN1_STRING *str; + unsigned char *s; + int len, r; + + n_entries = X509_NAME_entry_count(name); + if (n_entries != 1) + return 1; /* More than one entry in the DN. */ + entry = X509_NAME_get_entry(name, 0); + + obj = X509_NAME_ENTRY_get_object(entry); + if (OBJ_obj2nid(obj) != OBJ_txt2nid("commonName")) + return 1; /* The entry isn't a commonName. */ + + str = X509_NAME_ENTRY_get_data(entry); + len = ASN1_STRING_to_UTF8(&s, str); + if (len < 0) + return 0; + r = fast_memneq(s + len - 4, ".net", 4); + OPENSSL_free(s); + return r; +#endif +} + +/** Return true iff the peer certificate we're received on <b>tls</b> + * indicates that this connection should use the v3 (in-protocol) + * authentication handshake. + * + * Only the connection initiator should use this, and only once the initial + * handshake is done; the responder detects a v1 handshake by cipher types, + * and a v3/v2 handshake by Versions cell vs renegotiation. + */ +int +tor_tls_received_v3_certificate(tor_tls_t *tls) +{ + X509 *cert = SSL_get_peer_certificate(tls->ssl); + EVP_PKEY *key = NULL; + X509_NAME *issuer_name, *subject_name; + int is_v3 = 0; + + if (!cert) { + log_warn(LD_BUG, "Called on a connection with no peer certificate"); + goto done; + } + + subject_name = X509_get_subject_name(cert); + issuer_name = X509_get_issuer_name(cert); + + if (X509_name_cmp(subject_name, issuer_name) == 0) { + is_v3 = 1; /* purportedly self signed */ + goto done; + } + + if (dn_indicates_v3_cert(subject_name) || + dn_indicates_v3_cert(issuer_name)) { + is_v3 = 1; /* DN is fancy */ + goto done; + } + + key = X509_get_pubkey(cert); + if (EVP_PKEY_bits(key) != 1024 || + EVP_PKEY_type(key->type) != EVP_PKEY_RSA) { + is_v3 = 1; /* Key is fancy */ + goto done; + } + + done: + if (key) + EVP_PKEY_free(key); + if (cert) + X509_free(cert); + + return is_v3; +} + +/** Return the number of server handshakes that we've noticed doing on + * <b>tls</b>. */ +int +tor_tls_get_num_server_handshakes(tor_tls_t *tls) +{ + return tls->server_handshake_count; +} + +/** Return true iff the server TLS connection <b>tls</b> got the renegotiation + * request it was waiting for. */ +int +tor_tls_server_got_renegotiate(tor_tls_t *tls) +{ + return tls->got_renegotiate; +} + +/** Set the DIGEST256_LEN buffer at <b>secrets_out</b> to the value used in + * the v3 handshake to prove that the client knows the TLS secrets for the + * connection <b>tls</b>. Return 0 on success, -1 on failure. + */ +int +tor_tls_get_tlssecrets(tor_tls_t *tls, uint8_t *secrets_out) +{ +#define TLSSECRET_MAGIC "Tor V3 handshake TLS cross-certification" + char buf[128]; + size_t len; + tor_assert(tls); + tor_assert(tls->ssl); + tor_assert(tls->ssl->s3); + tor_assert(tls->ssl->session); + /* + The value is an HMAC, using the TLS master key as the HMAC key, of + client_random | server_random | TLSSECRET_MAGIC + */ + memcpy(buf + 0, tls->ssl->s3->client_random, 32); + memcpy(buf + 32, tls->ssl->s3->server_random, 32); + memcpy(buf + 64, TLSSECRET_MAGIC, strlen(TLSSECRET_MAGIC) + 1); + len = 64 + strlen(TLSSECRET_MAGIC) + 1; + crypto_hmac_sha256((char*)secrets_out, + (char*)tls->ssl->session->master_key, + tls->ssl->session->master_key_length, + buf, len); + memset(buf, 0, sizeof(buf)); + return 0; +} + /** Examine the amount of memory used and available for buffers in <b>tls</b>. * Set *<b>rbuf_capacity</b> to the amount of storage allocated for the read * buffer and *<b>rbuf_bytes</b> to the amount actually used. @@ -1768,3 +2390,75 @@ tor_tls_get_buffer_sizes(tor_tls_t *tls, *wbuf_bytes = tls->ssl->s3->wbuf.left; } +#ifdef USE_BUFFEREVENTS +/** Construct and return an TLS-encrypting bufferevent to send data over + * <b>socket</b>, which must match the socket of the underlying bufferevent + * <b>bufev_in</b>. The TLS object <b>tls</b> is used for encryption. + * + * This function will either create a filtering bufferevent that wraps around + * <b>bufev_in</b>, or it will free bufev_in and return a new bufferevent that + * uses the <b>tls</b> to talk to the network directly. Do not use + * <b>bufev_in</b> after calling this function. + * + * The connection will start out doing a server handshake if <b>receiving</b> + * is strue, and a client handshake otherwise. + * + * Returns NULL on failure. + */ +struct bufferevent * +tor_tls_init_bufferevent(tor_tls_t *tls, struct bufferevent *bufev_in, + evutil_socket_t socket, int receiving, + int filter) +{ + struct bufferevent *out; + const enum bufferevent_ssl_state state = receiving ? + BUFFEREVENT_SSL_ACCEPTING : BUFFEREVENT_SSL_CONNECTING; + + if (filter || tor_libevent_using_iocp_bufferevents()) { + /* Grab an extra reference to the SSL, since BEV_OPT_CLOSE_ON_FREE + means that the SSL will get freed too. + + This increment makes our SSL usage not-threadsafe, BTW. We should + see if we're allowed to use CRYPTO_add from outside openssl. */ + tls->ssl->references += 1; + out = bufferevent_openssl_filter_new(tor_libevent_get_base(), + bufev_in, + tls->ssl, + state, + BEV_OPT_DEFER_CALLBACKS| + BEV_OPT_CLOSE_ON_FREE); + /* Tell the underlying bufferevent when to accept more data from the SSL + filter (only when it's got less than 32K to write), and when to notify + the SSL filter that it could write more (when it drops under 24K). */ + bufferevent_setwatermark(bufev_in, EV_WRITE, 24*1024, 32*1024); + } else { + if (bufev_in) { + evutil_socket_t s = bufferevent_getfd(bufev_in); + tor_assert(s == -1 || s == socket); + tor_assert(evbuffer_get_length(bufferevent_get_input(bufev_in)) == 0); + tor_assert(evbuffer_get_length(bufferevent_get_output(bufev_in)) == 0); + tor_assert(BIO_number_read(SSL_get_rbio(tls->ssl)) == 0); + tor_assert(BIO_number_written(SSL_get_rbio(tls->ssl)) == 0); + bufferevent_free(bufev_in); + } + + /* Current versions (as of 2.0.x) of Libevent need to defer + * bufferevent_openssl callbacks, or else our callback functions will + * get called reentrantly, which is bad for us. + */ + out = bufferevent_openssl_socket_new(tor_libevent_get_base(), + socket, + tls->ssl, + state, + BEV_OPT_DEFER_CALLBACKS); + } + tls->state = TOR_TLS_ST_BUFFEREVENT; + + /* Unblock _after_ creating the bufferevent, since accept/connect tend to + * clear flags. */ + tor_tls_unblock_renegotiation(tls); + + return out; +} +#endif + diff --git a/src/common/tortls.h b/src/common/tortls.h index 55fee81ae..bcec63f05 100644 --- a/src/common/tortls.h +++ b/src/common/tortls.h @@ -17,6 +17,9 @@ /* Opaque structure to hold a TLS connection. */ typedef struct tor_tls_t tor_tls_t; +/* Opaque structure to hold an X509 certificate. */ +typedef struct tor_cert_t tor_cert_t; + /* Possible return values for most tor_tls_* functions. */ #define _MIN_TOR_TLS_ERROR_VAL -9 #define TOR_TLS_ERROR_MISC -9 @@ -48,11 +51,12 @@ typedef struct tor_tls_t tor_tls_t; #define TOR_TLS_IS_ERROR(rv) ((rv) < TOR_TLS_CLOSE) const char *tor_tls_err_to_string(int err); +void tor_tls_get_state_description(tor_tls_t *tls, char *buf, size_t sz); void tor_tls_free_all(void); int tor_tls_context_init(int is_public_server, - crypto_pk_env_t *client_identity, - crypto_pk_env_t *server_identity, + crypto_pk_t *client_identity, + crypto_pk_t *server_identity, unsigned int key_lifetime); tor_tls_t *tor_tls_new(int sock, int is_server); void tor_tls_set_logged_address(tor_tls_t *tls, const char *address); @@ -62,13 +66,19 @@ void tor_tls_set_renegotiate_callback(tor_tls_t *tls, int tor_tls_is_server(tor_tls_t *tls); void tor_tls_free(tor_tls_t *tls); int tor_tls_peer_has_cert(tor_tls_t *tls); -int tor_tls_verify(int severity, tor_tls_t *tls, crypto_pk_env_t **identity); -int tor_tls_check_lifetime(tor_tls_t *tls, int tolerance); +tor_cert_t *tor_tls_get_peer_cert(tor_tls_t *tls); +int tor_tls_verify(int severity, tor_tls_t *tls, crypto_pk_t **identity); +int tor_tls_check_lifetime(int severity, + tor_tls_t *tls, int past_tolerance, + int future_tolerance); int tor_tls_read(tor_tls_t *tls, char *cp, size_t len); int tor_tls_write(tor_tls_t *tls, const char *cp, size_t n); int tor_tls_handshake(tor_tls_t *tls); +int tor_tls_finish_handshake(tor_tls_t *tls); int tor_tls_renegotiate(tor_tls_t *tls); +void tor_tls_unblock_renegotiation(tor_tls_t *tls); void tor_tls_block_renegotiation(tor_tls_t *tls); +void tor_tls_assert_renegotiation_unblocked(tor_tls_t *tls); int tor_tls_shutdown(tor_tls_t *tls); int tor_tls_get_pending_bytes(tor_tls_t *tls); size_t tor_tls_get_forced_write_size(tor_tls_t *tls); @@ -81,12 +91,44 @@ void tor_tls_get_buffer_sizes(tor_tls_t *tls, size_t *wbuf_capacity, size_t *wbuf_bytes); int tor_tls_used_v1_handshake(tor_tls_t *tls); +int tor_tls_received_v3_certificate(tor_tls_t *tls); +int tor_tls_get_num_server_handshakes(tor_tls_t *tls); +int tor_tls_server_got_renegotiate(tor_tls_t *tls); +int tor_tls_get_tlssecrets(tor_tls_t *tls, uint8_t *secrets_out); /* Log and abort if there are unhandled TLS errors in OpenSSL's error stack. */ #define check_no_tls_errors() _check_no_tls_errors(__FILE__,__LINE__) void _check_no_tls_errors(const char *fname, int line); +void tor_tls_log_one_error(tor_tls_t *tls, unsigned long err, + int severity, int domain, const char *doing); + +#ifdef USE_BUFFEREVENTS +int tor_tls_start_renegotiating(tor_tls_t *tls); +struct bufferevent *tor_tls_init_bufferevent(tor_tls_t *tls, + struct bufferevent *bufev_in, + evutil_socket_t socket, int receiving, + int filter); +#endif + +void tor_cert_free(tor_cert_t *cert); +tor_cert_t *tor_cert_decode(const uint8_t *certificate, + size_t certificate_len); +void tor_cert_get_der(const tor_cert_t *cert, + const uint8_t **encoded_out, size_t *size_out); +const digests_t *tor_cert_get_id_digests(const tor_cert_t *cert); +const digests_t *tor_cert_get_cert_digests(const tor_cert_t *cert); +int tor_tls_get_my_certs(int server, + const tor_cert_t **link_cert_out, + const tor_cert_t **id_cert_out); +crypto_pk_t *tor_tls_get_my_client_auth_key(void); +crypto_pk_t *tor_tls_cert_get_key(tor_cert_t *cert); +int tor_tls_cert_matches_key(const tor_tls_t *tls, const tor_cert_t *cert); +int tor_tls_cert_is_valid(int severity, + const tor_cert_t *cert, + const tor_cert_t *signing_cert, + int check_rsa_1024); #endif diff --git a/src/common/tortls_states.h b/src/common/tortls_states.h deleted file mode 100644 index dcff2479f..000000000 --- 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 a03a57632..e7979d85b 100644 --- a/src/common/util.c +++ b/src/common/util.c @@ -14,6 +14,10 @@ #define _GNU_SOURCE #include "orconfig.h" +#ifdef HAVE_FCNTL_H +#include <fcntl.h> +#endif +#define UTIL_PRIVATE #include "util.h" #include "torlog.h" #undef log @@ -22,11 +26,12 @@ #include "container.h" #include "address.h" -#ifdef MS_WINDOWS +#ifdef _WIN32 #include <io.h> #include <direct.h> #include <process.h> #include <tchar.h> +#include <winbase.h> #else #include <dirent.h> #include <pwd.h> @@ -42,6 +47,7 @@ #include <stdio.h> #include <string.h> #include <assert.h> +#include <signal.h> #ifdef HAVE_NETINET_IN_H #include <netinet/in.h> @@ -67,9 +73,6 @@ #ifdef HAVE_SYS_FCNTL_H #include <sys/fcntl.h> #endif -#ifdef HAVE_FCNTL_H -#include <fcntl.h> -#endif #ifdef HAVE_TIME_H #include <time.h> #endif @@ -87,6 +90,9 @@ #ifdef HAVE_MALLOC_NP_H #include <malloc_np.h> #endif +#ifdef HAVE_SYS_WAIT_H +#include <sys/wait.h> +#endif /* ===== * Memory management @@ -166,6 +172,35 @@ _tor_malloc_zero(size_t size DMALLOC_PARAMS) return result; } +/** Allocate a chunk of <b>nmemb</b>*<b>size</b> bytes of memory, fill + * the memory with zero bytes, and return a pointer to the result. + * Log and terminate the process on error. (Same as + * calloc(<b>nmemb</b>,<b>size</b>), but never returns NULL.) + * + * XXXX This implementation probably asserts in cases where it could + * work, because it only tries dividing SIZE_MAX by size (according to + * the calloc(3) man page, the size of an element of the nmemb-element + * array to be allocated), not by nmemb (which could in theory be + * smaller than size). Don't do that then. + */ +void * +_tor_calloc(size_t nmemb, size_t size DMALLOC_PARAMS) +{ + /* You may ask yourself, "wouldn't it be smart to use calloc instead of + * malloc+memset? Perhaps libc's calloc knows some nifty optimization trick + * we don't!" Indeed it does, but its optimizations are only a big win when + * we're allocating something very big (it knows if it just got the memory + * from the OS in a pre-zeroed state). We don't want to use tor_malloc_zero + * for big stuff, so we don't bother with calloc. */ + void *result; + size_t max_nmemb = (size == 0) ? SIZE_MAX : SIZE_MAX/size; + + tor_assert(nmemb < max_nmemb); + + result = _tor_malloc_zero((nmemb * size) DMALLOC_FN_ARGS); + return result; +} + /** Change the size of the memory block pointed to by <b>ptr</b> to <b>size</b> * bytes long; return the new memory block. On error, log and * terminate. (Like realloc(ptr,size), but never returns NULL.) @@ -417,6 +452,32 @@ round_uint64_to_next_multiple_of(uint64_t number, uint64_t divisor) return number; } +/** Return the number of bits set in <b>v</b>. */ +int +n_bits_set_u8(uint8_t v) +{ + static const int nybble_table[] = { + 0, /* 0000 */ + 1, /* 0001 */ + 1, /* 0010 */ + 2, /* 0011 */ + 1, /* 0100 */ + 2, /* 0101 */ + 2, /* 0110 */ + 3, /* 0111 */ + 1, /* 1000 */ + 2, /* 1001 */ + 2, /* 1010 */ + 3, /* 1011 */ + 2, /* 1100 */ + 3, /* 1101 */ + 3, /* 1110 */ + 4, /* 1111 */ + }; + + return nybble_table[v & 15] + nybble_table[v>>4]; +} + /* ===== * String manipulation * ===== */ @@ -500,6 +561,23 @@ tor_strisnonupper(const char *s) return 1; } +/** As strcmp, except that either string may be NULL. The NULL string is + * considered to be before any non-NULL string. */ +int +strcmp_opt(const char *s1, const char *s2) +{ + if (!s1) { + if (!s2) + return 0; + else + return -1; + } else if (!s2) { + return 1; + } else { + return strcmp(s1, s2); + } +} + /** Compares the first strlen(s2) characters of s1 with s2. Returns as for * strcmp. */ @@ -722,6 +800,34 @@ find_str_at_start_of_line(const char *haystack, const char *needle) return NULL; } +/** Returns true if <b>string</b> could be a C identifier. + A C identifier must begin with a letter or an underscore and the + rest of its characters can be letters, numbers or underscores. No + length limit is imposed. */ +int +string_is_C_identifier(const char *string) +{ + size_t iter; + size_t length = strlen(string); + if (!length) + return 0; + + for (iter = 0; iter < length ; iter++) { + if (iter == 0) { + if (!(TOR_ISALPHA(string[iter]) || + string[iter] == '_')) + return 0; + } else { + if (!(TOR_ISALPHA(string[iter]) || + TOR_ISDIGIT(string[iter]) || + string[iter] == '_')) + return 0; + } + } + + return 1; +} + /** Return true iff the 'len' bytes at 'mem' are all zero. */ int tor_mem_is_zero(const char *mem, size_t len) @@ -803,6 +909,12 @@ tor_parse_long(const char *s, int base, long min, long max, char *endptr; long r; + if (base < 0) { + if (ok) + *ok = 0; + return 0; + } + errno = 0; r = strtol(s, &endptr, base); CHECK_STRTOX_RESULT(); @@ -816,6 +928,12 @@ tor_parse_ulong(const char *s, int base, unsigned long min, char *endptr; unsigned long r; + if (base < 0) { + if (ok) + *ok = 0; + return 0; + } + errno = 0; r = strtoul(s, &endptr, base); CHECK_STRTOX_RESULT(); @@ -842,10 +960,16 @@ tor_parse_uint64(const char *s, int base, uint64_t min, char *endptr; uint64_t r; + if (base < 0) { + if (ok) + *ok = 0; + return 0; + } + errno = 0; #ifdef HAVE_STRTOULL r = (uint64_t)strtoull(s, &endptr, base); -#elif defined(MS_WINDOWS) +#elif defined(_WIN32) #if defined(_MSC_VER) && _MSC_VER < 1300 tor_assert(base <= 10); r = (uint64_t)_atoi64(s); @@ -965,7 +1089,7 @@ esc_for_log(const char *s) char *result, *outp; size_t len = 3; if (!s) { - return tor_strdup(""); + return tor_strdup("(null)"); } for (cp = s; *cp; ++cp) { @@ -1361,6 +1485,26 @@ format_iso_time(char *buf, time_t t) strftime(buf, ISO_TIME_LEN+1, "%Y-%m-%d %H:%M:%S", tor_gmtime_r(&t, &tm)); } +/** As format_iso_time, but use the yyyy-mm-ddThh:mm:ss format to avoid + * embedding an internal space. */ +void +format_iso_time_nospace(char *buf, time_t t) +{ + format_iso_time(buf, t); + buf[10] = 'T'; +} + +/** As format_iso_time_nospace, but include microseconds in decimal + * fixed-point format. Requires that buf be at least ISO_TIME_USEC_LEN+1 + * bytes long. */ +void +format_iso_time_nospace_usec(char *buf, const struct timeval *tv) +{ + tor_assert(tv); + format_iso_time_nospace(buf, tv->tv_sec); + tor_snprintf(buf+ISO_TIME_LEN, 8, ".%06d", (int)tv->tv_usec); +} + /** Given an ISO-formatted UTC time value (after the epoch) in <b>cp</b>, * parse it and store its value in *<b>t</b>. Return 0 on success, -1 on * failure. Ignore extraneous stuff in <b>cp</b> separated by whitespace from @@ -1647,7 +1791,7 @@ read_all(tor_socket_t fd, char *buf, size_t count, int isSocket) static void clean_name_for_stat(char *name) { -#ifdef MS_WINDOWS +#ifdef _WIN32 size_t len = strlen(name); if (!len) return; @@ -1706,7 +1850,7 @@ check_private_dir(const char *dirname, cpd_check_t check, int r; struct stat st; char *f; -#ifndef MS_WINDOWS +#ifndef _WIN32 int mask; struct passwd *pw = NULL; uid_t running_uid; @@ -1728,7 +1872,7 @@ check_private_dir(const char *dirname, cpd_check_t check, } if (check & CPD_CREATE) { log_info(LD_GENERAL, "Creating directory %s", dirname); -#if defined (MS_WINDOWS) && !defined (WINCE) +#if defined (_WIN32) && !defined (WINCE) r = mkdir(dirname); #else r = mkdir(dirname, 0700); @@ -1750,7 +1894,7 @@ check_private_dir(const char *dirname, cpd_check_t check, log_warn(LD_FS, "%s is not a directory", dirname); return -1; } -#ifndef MS_WINDOWS +#ifndef _WIN32 if (effective_user) { /* Look up the user and group information. * If we have a problem, bail out. */ @@ -1837,7 +1981,7 @@ check_private_dir(const char *dirname, cpd_check_t check, int write_str_to_file(const char *fname, const char *str, int bin) { -#ifdef MS_WINDOWS +#ifdef _WIN32 if (!bin && strchr(str, '\r')) { log_warn(LD_BUG, "We're writing a text string that already contains a CR."); @@ -1880,7 +2024,6 @@ int start_writing_to_file(const char *fname, int open_flags, int mode, open_file_t **data_out) { - size_t tempname_len = strlen(fname)+16; open_file_t *new_file = tor_malloc_zero(sizeof(open_file_t)); const char *open_name; int append = 0; @@ -1891,7 +2034,6 @@ start_writing_to_file(const char *fname, int open_flags, int mode, tor_assert((open_flags & (O_BINARY|O_TEXT)) != 0); #endif new_file->fd = -1; - tor_assert(tempname_len > strlen(fname)); /*check for overflow*/ new_file->filename = tor_strdup(fname); if (open_flags & O_APPEND) { open_name = fname; @@ -1899,11 +2041,8 @@ start_writing_to_file(const char *fname, int open_flags, int mode, append = 1; open_flags &= ~O_APPEND; } else { - open_name = new_file->tempname = tor_malloc(tempname_len); - if (tor_snprintf(new_file->tempname, tempname_len, "%s.tmp", fname)<0) { - log_warn(LD_GENERAL, "Failed to generate filename"); - goto err; - } + tor_asprintf(&new_file->tempname, "%s.tmp", fname); + open_name = new_file->tempname; /* We always replace an existing temporary file if there is one. */ open_flags |= O_CREAT|O_TRUNC; open_flags &= ~O_EXCL; @@ -1912,7 +2051,7 @@ start_writing_to_file(const char *fname, int open_flags, int mode, if (open_flags & O_BINARY) new_file->binary = 1; - new_file->fd = open(open_name, open_flags, mode); + new_file->fd = tor_open_cloexec(open_name, open_flags, mode); if (new_file->fd < 0) { log_warn(LD_FS, "Couldn't open \"%s\" (%s) for writing: %s", open_name, fname, strerror(errno)); @@ -2073,36 +2212,50 @@ 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(); + smartlist_t *chunks = smartlist_new(); smartlist_add(chunks, &c); r = write_chunks_to_file_impl(fname, chunks, flags); smartlist_free(chunks); 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 @@ -2133,7 +2286,7 @@ read_file_to_str(const char *filename, int flags, struct stat *stat_out) tor_assert(filename); - fd = open(filename,O_RDONLY|(bin?O_BINARY:O_TEXT),0); + fd = tor_open_cloexec(filename,O_RDONLY|(bin?O_BINARY:O_TEXT),0); if (fd<0) { int severity = LOG_WARN; int save_errno = errno; @@ -2170,7 +2323,7 @@ read_file_to_str(const char *filename, int flags, struct stat *stat_out) } string[r] = '\0'; /* NUL-terminate the result. */ -#ifdef MS_WINDOWS +#ifdef _WIN32 if (!bin && strchr(string, '\r')) { log_debug(LD_FS, "We didn't convert CRLF to LF as well as we hoped " "when reading %s. Coping.", @@ -2335,7 +2488,7 @@ parse_config_line_from_str(const char *line, char **key_out, char **value_out) KEYCHAR = Any character except ' ', '\r', '\n', '\t', '#', "\" VALUES = QUOTEDVALUE | NORMALVALUE - QUOTEDVALUE = QUOTE QVITEM* QUOTE EOLSPACE? + QUOTEDVALUE = QUOTE QVCHAR* QUOTE EOLSPACE? QUOTE = '"' QVCHAR = KEYCHAR | ESC ('n' | 't' | 'r' | '"' | ESC |'\'' | OCTAL | HEX) ESC = "\\" @@ -2463,7 +2616,7 @@ char * expand_filename(const char *filename) { tor_assert(filename); -#ifdef MS_WINDOWS +#ifdef _WIN32 return tor_strdup(filename); #else if (*filename == '~') { @@ -2531,18 +2684,21 @@ digit_to_num(char d) * success, store the result in <b>out</b>, advance bufp to the next * character, and return 0. On failure, return -1. */ static int -scan_unsigned(const char **bufp, unsigned *out, int width) +scan_unsigned(const char **bufp, unsigned *out, int width, int base) { unsigned result = 0; int scanned_so_far = 0; + const int hex = base==16; + tor_assert(base == 10 || base == 16); if (!bufp || !*bufp || !out) return -1; if (width<0) width=MAX_SCANF_WIDTH; - while (**bufp && TOR_ISDIGIT(**bufp) && scanned_so_far < width) { - int digit = digit_to_num(*(*bufp)++); - unsigned new_result = result * 10 + digit; + while (**bufp && (hex?TOR_ISXDIGIT(**bufp):TOR_ISDIGIT(**bufp)) + && scanned_so_far < width) { + int digit = hex?hex_decode_digit(*(*bufp)++):digit_to_num(*(*bufp)++); + unsigned new_result = result * base + digit; if (new_result > UINT32_MAX || new_result < result) return -1; /* over/underflow. */ result = new_result; @@ -2604,11 +2760,12 @@ tor_vsscanf(const char *buf, const char *pattern, va_list ap) if (!width) /* No zero-width things. */ return -1; } - if (*pattern == 'u') { + if (*pattern == 'u' || *pattern == 'x') { unsigned *u = va_arg(ap, unsigned *); + const int base = (*pattern == 'u') ? 10 : 16; if (!*buf) return n_matched; - if (scan_unsigned(&buf, u, width)<0) + if (scan_unsigned(&buf, u, width, base)<0) return n_matched; ++pattern; ++n_matched; @@ -2631,7 +2788,7 @@ tor_vsscanf(const char *buf, const char *pattern, va_list ap) ++n_matched; } else if (*pattern == '%') { if (*buf != '%') - return -1; + return n_matched; ++buf; ++pattern; } else { @@ -2645,9 +2802,9 @@ tor_vsscanf(const char *buf, const char *pattern, va_list ap) /** Minimal sscanf replacement: parse <b>buf</b> according to <b>pattern</b> * and store the results in the corresponding argument fields. Differs from - * sscanf in that it: Only handles %u and %Ns. Does not handle arbitrarily - * long widths. %u does not consume any space. Is locale-independent. - * Returns -1 on malformed patterns. + * sscanf in that it: Only handles %u, %x, %c and %Ns. Does not handle + * arbitrarily long widths. %u and %x do not consume any space. Is + * locale-independent. Returns -1 on malformed patterns. * * (As with other locale-independent functions, we need this to parse data that * is in ASCII without worrying that the C library's locale-handling will make @@ -2664,6 +2821,30 @@ tor_sscanf(const char *buf, const char *pattern, ...) return r; } +/** Append the string produced by tor_asprintf(<b>pattern</b>, <b>...</b>) + * to <b>sl</b>. */ +void +smartlist_add_asprintf(struct smartlist_t *sl, const char *pattern, ...) +{ + va_list ap; + va_start(ap, pattern); + smartlist_add_vasprintf(sl, pattern, ap); + va_end(ap); +} + +/** va_list-based backend of smartlist_add_asprintf. */ +void +smartlist_add_vasprintf(struct smartlist_t *sl, const char *pattern, + va_list args) +{ + char *str = NULL; + + tor_vasprintf(&str, pattern, args); + tor_assert(str != NULL); + + smartlist_add(sl, str); +} + /** Return a new list containing the filenames in the directory <b>dirname</b>. * Return NULL on error or if <b>dirname</b> is not a directory. */ @@ -2671,15 +2852,13 @@ smartlist_t * tor_listdir(const char *dirname) { smartlist_t *result; -#ifdef MS_WINDOWS - char *pattern; +#ifdef _WIN32 + char *pattern=NULL; TCHAR tpattern[MAX_PATH] = {0}; char name[MAX_PATH] = {0}; HANDLE handle; WIN32_FIND_DATA findData; - size_t pattern_len = strlen(dirname)+16; - pattern = tor_malloc(pattern_len); - tor_snprintf(pattern, pattern_len, "%s\\*", dirname); + tor_asprintf(&pattern, "%s\\*", dirname); #ifdef UNICODE mbstowcs(tpattern,pattern,MAX_PATH); #else @@ -2689,7 +2868,7 @@ tor_listdir(const char *dirname) tor_free(pattern); return NULL; } - result = smartlist_create(); + result = smartlist_new(); while (1) { #ifdef UNICODE wcstombs(name,findData.cFileName,MAX_PATH); @@ -2718,7 +2897,7 @@ tor_listdir(const char *dirname) if (!(d = opendir(dirname))) return NULL; - result = smartlist_create(); + result = smartlist_new(); while ((de = readdir(d))) { if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, "..")) @@ -2736,7 +2915,7 @@ path_is_relative(const char *filename) { if (filename && filename[0] == '/') return 0; -#ifdef MS_WINDOWS +#ifdef _WIN32 else if (filename && filename[0] == '\\') return 0; else if (filename && strlen(filename)>3 && TOR_ISALPHA(filename[0]) && @@ -2751,7 +2930,7 @@ path_is_relative(const char *filename) * Process helpers * ===== */ -#ifndef MS_WINDOWS +#ifndef _WIN32 /* Based on code contributed by christian grothoff */ /** True iff we've called start_daemon(). */ static int start_daemon_called = 0; @@ -2841,7 +3020,7 @@ finish_daemon(const char *desired_cwd) exit(1); } - nullfd = open("/dev/null", O_RDWR); + nullfd = tor_open_cloexec("/dev/null", O_RDWR, 0); if (nullfd < 0) { log_err(LD_GENERAL,"/dev/null can't be opened. Exiting."); exit(1); @@ -2865,7 +3044,7 @@ finish_daemon(const char *desired_cwd) close(daemon_filedes[1]); } #else -/* defined(MS_WINDOWS) */ +/* defined(_WIN32) */ void start_daemon(void) { @@ -2888,7 +3067,7 @@ write_pidfile(char *filename) log_warn(LD_FS, "Unable to open \"%s\" for writing: %s", filename, strerror(errno)); } else { -#ifdef MS_WINDOWS +#ifdef _WIN32 fprintf(pidfile, "%d\n", (int)_getpid()); #else fprintf(pidfile, "%d\n", (int)getpid()); @@ -2897,7 +3076,7 @@ write_pidfile(char *filename) } } -#ifdef MS_WINDOWS +#ifdef _WIN32 HANDLE load_windows_system_library(const TCHAR *library_name) { @@ -2912,3 +3091,1322 @@ load_windows_system_library(const TCHAR *library_name) } #endif +/** Format a single argument for being put on a Windows command line. + * Returns a newly allocated string */ +static char * +format_win_cmdline_argument(const char *arg) +{ + char *formatted_arg; + char need_quotes; + const char *c; + int i; + int bs_counter = 0; + /* Backslash we can point to when one is inserted into the string */ + const char backslash = '\\'; + + /* Smartlist of *char */ + smartlist_t *arg_chars; + arg_chars = smartlist_new(); + + /* Quote string if it contains whitespace or is empty */ + need_quotes = (strchr(arg, ' ') || strchr(arg, '\t') || '\0' == arg[0]); + + /* Build up smartlist of *chars */ + for (c=arg; *c != '\0'; c++) { + if ('"' == *c) { + /* Double up backslashes preceding a quote */ + for (i=0; i<(bs_counter*2); i++) + smartlist_add(arg_chars, (void*)&backslash); + bs_counter = 0; + /* Escape the quote */ + smartlist_add(arg_chars, (void*)&backslash); + smartlist_add(arg_chars, (void*)c); + } else if ('\\' == *c) { + /* Count backslashes until we know whether to double up */ + bs_counter++; + } else { + /* Don't double up slashes preceding a non-quote */ + for (i=0; i<bs_counter; i++) + smartlist_add(arg_chars, (void*)&backslash); + bs_counter = 0; + smartlist_add(arg_chars, (void*)c); + } + } + /* Don't double up trailing backslashes */ + for (i=0; i<bs_counter; i++) + smartlist_add(arg_chars, (void*)&backslash); + + /* Allocate space for argument, quotes (if needed), and terminator */ + formatted_arg = tor_malloc(sizeof(char) * + (smartlist_len(arg_chars) + (need_quotes?2:0) + 1)); + + /* Add leading quote */ + i=0; + if (need_quotes) + formatted_arg[i++] = '"'; + + /* Add characters */ + SMARTLIST_FOREACH(arg_chars, char*, c, + { + formatted_arg[i++] = *c; + }); + + /* Add trailing quote */ + if (need_quotes) + formatted_arg[i++] = '"'; + formatted_arg[i] = '\0'; + + smartlist_free(arg_chars); + return formatted_arg; +} + +/** Format a command line for use on Windows, which takes the command as a + * string rather than string array. Follows the rules from "Parsing C++ + * Command-Line Arguments" in MSDN. Algorithm based on list2cmdline in the + * Python subprocess module. Returns a newly allocated string */ +char * +tor_join_win_cmdline(const char *argv[]) +{ + smartlist_t *argv_list; + char *joined_argv; + int i; + + /* Format each argument and put the result in a smartlist */ + argv_list = smartlist_new(); + for (i=0; argv[i] != NULL; i++) { + smartlist_add(argv_list, (void *)format_win_cmdline_argument(argv[i])); + } + + /* Join the arguments with whitespace */ + joined_argv = smartlist_join_strings(argv_list, " ", 0, NULL); + + /* Free the newly allocated arguments, and the smartlist */ + SMARTLIST_FOREACH(argv_list, char *, arg, + { + tor_free(arg); + }); + smartlist_free(argv_list); + + return joined_argv; +} + +/** Format <b>child_state</b> and <b>saved_errno</b> as a hex string placed in + * <b>hex_errno</b>. Called between fork and _exit, so must be signal-handler + * safe. + * + * <b>hex_errno</b> must have at least HEX_ERRNO_SIZE bytes available. + * + * The format of <b>hex_errno</b> is: "CHILD_STATE/ERRNO\n", left-padded + * with spaces. Note that there is no trailing \0. CHILD_STATE indicates where + * in the processs of starting the child process did the failure occur (see + * CHILD_STATE_* macros for definition), and SAVED_ERRNO is the value of + * errno when the failure occurred. + */ + +void +format_helper_exit_status(unsigned char child_state, int saved_errno, + char *hex_errno) +{ + unsigned int unsigned_errno; + char *cur; + size_t i; + + /* Fill hex_errno with spaces, and a trailing newline (memset may + not be signal handler safe, so we can't use it) */ + for (i = 0; i < (HEX_ERRNO_SIZE - 1); i++) + hex_errno[i] = ' '; + hex_errno[HEX_ERRNO_SIZE - 1] = '\n'; + + /* Convert errno to be unsigned for hex conversion */ + if (saved_errno < 0) { + unsigned_errno = (unsigned int) -saved_errno; + } else { + unsigned_errno = (unsigned int) saved_errno; + } + + /* Convert errno to hex (start before \n) */ + cur = hex_errno + HEX_ERRNO_SIZE - 2; + + /* Check for overflow on first iteration of the loop */ + if (cur < hex_errno) + return; + + do { + *cur-- = "0123456789ABCDEF"[unsigned_errno % 16]; + unsigned_errno /= 16; + } while (unsigned_errno != 0 && cur >= hex_errno); + + /* Prepend the minus sign if errno was negative */ + if (saved_errno < 0 && cur >= hex_errno) + *cur-- = '-'; + + /* Leave a gap */ + if (cur >= hex_errno) + *cur-- = '/'; + + /* Check for overflow on first iteration of the loop */ + if (cur < hex_errno) + return; + + /* Convert child_state to hex */ + do { + *cur-- = "0123456789ABCDEF"[child_state % 16]; + child_state /= 16; + } while (child_state != 0 && cur >= hex_errno); +} + +/* Maximum number of file descriptors, if we cannot get it via sysconf() */ +#define DEFAULT_MAX_FD 256 + +/** Terminate the process of <b>process_handle</b>. + * Code borrowed from Python's os.kill. */ +int +tor_terminate_process(process_handle_t *process_handle) +{ +#ifdef _WIN32 + 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; + } +#else /* Unix */ + 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 _WIN32 + return (int) process_handle->pid.dwProcessId; +#else + return (int) process_handle->pid; +#endif +} + +#ifdef _WIN32 +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 _WIN32 + 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 +#define CHILD_STATE_FORK 3 +#define CHILD_STATE_DUPOUT 4 +#define CHILD_STATE_DUPERR 5 +#define CHILD_STATE_REDIRECT 6 +#define CHILD_STATE_CLOSEFD 7 +#define CHILD_STATE_EXEC 8 +#define CHILD_STATE_FAILEXEC 9 + +/** Start a program in the background. If <b>filename</b> contains a '/', then + * it will be treated as an absolute or relative path. Otherwise, on + * non-Windows systems, the system path will be searched for <b>filename</b>. + * On Windows, only the current directory will be searched. Here, to search the + * system path (as well as the application directory, current working + * directory, and system directories), set filename to NULL. + * + * The strings in <b>argv</b> will be passed as the command line arguments of + * the child program (following convention, argv[0] should normally be the + * filename of the executable, and this must be the case if <b>filename</b> is + * NULL). The last element of argv must be NULL. A handle to the child process + * will be returned in process_handle (which must be non-NULL). Read + * process_handle.status to find out if the process was successfully launched. + * For convenience, process_handle.status is returned by this function. + * + * Some parts of this code are based on the POSIX subprocess module from + * Python, and example code from + * http://msdn.microsoft.com/en-us/library/ms682499%28v=vs.85%29.aspx. + */ +int +tor_spawn_background(const char *const filename, const char **argv, + process_environment_t *env, + process_handle_t **process_handle_out) +{ +#ifdef _WIN32 + 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; + + SECURITY_ATTRIBUTES saAttr; + char *joined_argv; + + 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 */ + 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 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 status; + } + + /* Set up pipe for stderr */ + if (!CreatePipe(&stderr_pipe_read, &stderr_pipe_write, &saAttr, 0)) { + log_warn(LD_GENERAL, + "Failed to create pipe for stderr communication with child process: %s", + format_win32_error(GetLastError())); + 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 status; + } + + /* Create the child process */ + + /* Windows expects argv to be a whitespace delimited string, so join argv up + */ + 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); + siStartInfo.hStdError = stderr_pipe_write; + siStartInfo.hStdOutput = stdout_pipe_write; + siStartInfo.hStdInput = NULL; + siStartInfo.dwFlags |= STARTF_USESTDHANDLES; + + /* Create the child process */ + + retval = CreateProcess(filename, // module name + joined_argv, // command line + /* TODO: should we set explicit security attributes? (#2046, comment 5) */ + NULL, // process security attributes + NULL, // primary thread security attributes + TRUE, // handles are inherited + /*(TODO: set CREATE_NEW CONSOLE/PROCESS_GROUP to make GetExitCodeProcess() + * work?) */ + 0, // creation flags + (env==NULL) ? NULL : env->windows_environment_block, + NULL, // use parent's current directory + &siStartInfo, // STARTUPINFO pointer + &(process_handle->pid)); // receives PROCESS_INFORMATION + + tor_free(joined_argv); + + if (!retval) { + 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; + status = process_handle->status = PROCESS_STATUS_RUNNING; + } + + /* TODO: Close pipes on exit */ + *process_handle_out = process_handle; + return status; +#else // _WIN32 + 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; + + /* Represents where in the process of spawning the program is; + this is used for printing out the error message */ + unsigned char child_state = CHILD_STATE_INIT; + + char hex_errno[HEX_ERRNO_SIZE]; + + static int max_fd = -1; + + 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 */ + error_message_length = strlen(error_message); + + child_state = CHILD_STATE_PIPE; + + /* Set up pipe for redirecting stdout and stderr of child */ + retval = pipe(stdout_pipe); + if (-1 == retval) { + log_warn(LD_GENERAL, + "Failed to set up pipe for stdout communication with child process: %s", + strerror(errno)); + return status; + } + + retval = pipe(stderr_pipe); + if (-1 == retval) { + log_warn(LD_GENERAL, + "Failed to set up pipe for stderr communication with child process: %s", + strerror(errno)); + + close(stdout_pipe[0]); + close(stdout_pipe[1]); + + return status; + } + + child_state = CHILD_STATE_MAXFD; + +#ifdef _SC_OPEN_MAX + if (-1 != max_fd) { + max_fd = (int) sysconf(_SC_OPEN_MAX); + if (max_fd == -1) + max_fd = DEFAULT_MAX_FD; + log_warn(LD_GENERAL, + "Cannot find maximum file descriptor, assuming %d", max_fd); + } +#else + max_fd = DEFAULT_MAX_FD; +#endif + + child_state = CHILD_STATE_FORK; + + pid = fork(); + if (0 == pid) { + /* In child */ + + child_state = CHILD_STATE_DUPOUT; + + /* Link child stdout to the write end of the pipe */ + retval = dup2(stdout_pipe[1], STDOUT_FILENO); + if (-1 == retval) + goto error; + + child_state = CHILD_STATE_DUPERR; + + /* Link child stderr to the write end of the pipe */ + retval = dup2(stderr_pipe[1], STDERR_FILENO); + if (-1 == retval) + goto error; + + child_state = CHILD_STATE_REDIRECT; + + /* Link stdin to /dev/null */ + fd = open("/dev/null", O_RDONLY); /* NOT cloexec, obviously. */ + if (fd != -1) + dup2(fd, STDIN_FILENO); + else + goto error; + + child_state = CHILD_STATE_CLOSEFD; + + close(stderr_pipe[0]); + close(stderr_pipe[1]); + close(stdout_pipe[0]); + close(stdout_pipe[1]); + close(fd); + + /* Close all other fds, including the read end of the pipe */ + /* XXX: We should now be doing enough FD_CLOEXEC setting to make + * this needless. */ + for (fd = STDERR_FILENO + 1; fd < max_fd; fd++) { + close(fd); + } + + child_state = CHILD_STATE_EXEC; + + /* Call the requested program. We need the cast because + execvp doesn't define argv as const, even though it + does not modify the arguments */ + if (env) + execve(filename, (char *const *) argv, env->unixoid_environment_block); + else + execvp(filename, (char *const *) argv); + + /* If we got here, the exec or open(/dev/null) failed */ + + child_state = CHILD_STATE_FAILEXEC; + + error: + /* XXX: are we leaking fds from the pipe? */ + + format_helper_exit_status(child_state, errno, hex_errno); + + /* Write the error message. GCC requires that we check the return + value, but there is nothing we can do if it fails */ + /* TODO: Don't use STDOUT, use a pipe set up just for this purpose */ + nbytes = write(STDOUT_FILENO, error_message, error_message_length); + nbytes = write(STDOUT_FILENO, hex_errno, sizeof(hex_errno)); + + (void) nbytes; + + _exit(255); + /* Never reached, but avoids compiler warning */ + return status; + } + + /* In parent */ + + if (-1 == pid) { + log_warn(LD_GENERAL, "Failed to fork child process: %s", strerror(errno)); + close(stdout_pipe[0]); + close(stdout_pipe[1]); + close(stderr_pipe[0]); + close(stderr_pipe[1]); + 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 */ + + /* Return read end of the pipes to caller, and close write end */ + process_handle->stdout_pipe = stdout_pipe[0]; + retval = close(stdout_pipe[1]); + + if (-1 == retval) { + log_warn(LD_GENERAL, + "Failed to close write end of stdout pipe in parent process: %s", + strerror(errno)); + } + + process_handle->stderr_pipe = stderr_pipe[0]; + retval = close(stderr_pipe[1]); + + if (-1 == retval) { + log_warn(LD_GENERAL, + "Failed to close write end of stderr pipe in parent process: %s", + strerror(errno)); + } + + 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); + /* Open the buffered IO streams */ + 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 // _WIN32 +} + +/** 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 _WIN32 + 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 + * the process is still running, the function will return + * PROCESS_EXIT_RUNNING, and exit_code will be left unchanged. Returns + * PROCESS_EXIT_EXITED if the process did exit. If there is a failure, + * PROCESS_EXIT_ERROR will be returned and the contents of exit_code (if + * non-NULL) will be undefined. N.B. Under *nix operating systems, this will + * 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, + int block, int *exit_code) +{ +#ifdef _WIN32 + DWORD retval; + BOOL success; + + if (block) { + /* Wait for the process to exit */ + 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); + if (WAIT_TIMEOUT == retval) { + /* Process has not exited */ + return PROCESS_EXIT_RUNNING; + } else if (retval != WAIT_OBJECT_0) { + log_warn(LD_GENERAL, "WaitForSingleObject() failed (%d): %s", + (int)retval, format_win32_error(GetLastError())); + return PROCESS_EXIT_ERROR; + } + } + + if (exit_code != NULL) { + success = GetExitCodeProcess(process_handle->pid.hProcess, + (PDWORD)exit_code); + if (!success) { + log_warn(LD_GENERAL, "GetExitCodeProcess() failed: %s", + format_win32_error(GetLastError())); + return PROCESS_EXIT_ERROR; + } + } +#else + int stat_loc; + int retval; + + 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)); + return PROCESS_EXIT_ERROR; + } + + if (!WIFEXITED(stat_loc)) { + log_warn(LD_GENERAL, "Process %d did not exit normally", + process_handle->pid); + return PROCESS_EXIT_ERROR; + } + + if (exit_code != NULL) + *exit_code = WEXITSTATUS(stat_loc); +#endif // _WIN32 + + return PROCESS_EXIT_EXITED; +} + +/** Return non-zero iff getenv would consider <b>s1</b> and <b>s2</b> + * to have the same name as strings in a process's environment. */ +int +environment_variable_names_equal(const char *s1, const char *s2) +{ + size_t s1_name_len = strcspn(s1, "="); + size_t s2_name_len = strcspn(s2, "="); + + return (s1_name_len == s2_name_len && + tor_memeq(s1, s2, s1_name_len)); +} + +/** Free <b>env</b> (assuming it was produced by + * process_environment_make). */ +void +process_environment_free(process_environment_t *env) +{ + if (env == NULL) return; + + /* As both an optimization hack to reduce consing on Unixoid systems + * and a nice way to ensure that some otherwise-Windows-specific + * code will always get tested before changes to it get merged, the + * strings which env->unixoid_environment_block points to are packed + * into env->windows_environment_block. */ + tor_free(env->unixoid_environment_block); + tor_free(env->windows_environment_block); + + tor_free(env); +} + +/** Make a process_environment_t containing the environment variables + * specified in <b>env_vars</b> (as C strings of the form + * "NAME=VALUE"). */ +process_environment_t * +process_environment_make(struct smartlist_t *env_vars) +{ + process_environment_t *env = tor_malloc_zero(sizeof(process_environment_t)); + size_t n_env_vars = smartlist_len(env_vars); + size_t i; + size_t total_env_length; + smartlist_t *env_vars_sorted; + + tor_assert(n_env_vars + 1 != 0); + env->unixoid_environment_block = tor_calloc(n_env_vars + 1, sizeof(char *)); + /* env->unixoid_environment_block is already NULL-terminated, + * because we assume that NULL == 0 (and check that during compilation). */ + + total_env_length = 1; /* terminating NUL of terminating empty string */ + for (i = 0; i < n_env_vars; ++i) { + const char *s = smartlist_get(env_vars, i); + size_t slen = strlen(s); + + tor_assert(slen + 1 != 0); + tor_assert(slen + 1 < SIZE_MAX - total_env_length); + total_env_length += slen + 1; + } + + env->windows_environment_block = tor_malloc_zero(total_env_length); + /* env->windows_environment_block is already + * (NUL-terminated-empty-string)-terminated. */ + + /* Some versions of Windows supposedly require that environment + * blocks be sorted. Or maybe some Windows programs (or their + * runtime libraries) fail to look up strings in non-sorted + * environment blocks. + * + * Also, sorting strings makes it easy to find duplicate environment + * variables and environment-variable strings without an '=' on all + * OSes, and they can cause badness. Let's complain about those. */ + env_vars_sorted = smartlist_new(); + smartlist_add_all(env_vars_sorted, env_vars); + smartlist_sort_strings(env_vars_sorted); + + /* Now copy the strings into the environment blocks. */ + { + char *cp = env->windows_environment_block; + const char *prev_env_var = NULL; + + for (i = 0; i < n_env_vars; ++i) { + const char *s = smartlist_get(env_vars_sorted, i); + size_t slen = strlen(s); + size_t s_name_len = strcspn(s, "="); + + if (s_name_len == slen) { + log_warn(LD_GENERAL, + "Preparing an environment containing a variable " + "without a value: %s", + s); + } + if (prev_env_var != NULL && + environment_variable_names_equal(s, prev_env_var)) { + log_warn(LD_GENERAL, + "Preparing an environment containing two variables " + "with the same name: %s and %s", + prev_env_var, s); + } + + prev_env_var = s; + + /* Actually copy the string into the environment. */ + memcpy(cp, s, slen+1); + env->unixoid_environment_block[i] = cp; + cp += slen+1; + } + + tor_assert(cp == env->windows_environment_block + total_env_length - 1); + } + + smartlist_free(env_vars_sorted); + + return env; +} + +/** Return a newly allocated smartlist containing every variable in + * this process's environment, as a NUL-terminated string of the form + * "NAME=VALUE". Note that on some/many/most/all OSes, the parent + * process can put strings not of that form in our environment; + * callers should try to not get crashed by that. + * + * The returned strings are heap-allocated, and must be freed by the + * caller. */ +struct smartlist_t * +get_current_process_environment_variables(void) +{ + smartlist_t *sl = smartlist_new(); + + char **environ_tmp; /* Not const char ** ? Really? */ + for (environ_tmp = get_environment(); *environ_tmp; ++environ_tmp) { + smartlist_add(sl, tor_strdup(*environ_tmp)); + } + + return sl; +} + +/** For each string s in <b>env_vars</b> such that + * environment_variable_names_equal(s, <b>new_var</b>), remove it; if + * <b>free_p</b> is non-zero, call <b>free_old</b>(s). If + * <b>new_var</b> contains '=', insert it into <b>env_vars</b>. */ +void +set_environment_variable_in_smartlist(struct smartlist_t *env_vars, + const char *new_var, + void (*free_old)(void*), + int free_p) +{ + SMARTLIST_FOREACH_BEGIN(env_vars, const char *, s) { + if (environment_variable_names_equal(s, new_var)) { + SMARTLIST_DEL_CURRENT(env_vars, s); + if (free_p) { + free_old((void *)s); + } + } + } SMARTLIST_FOREACH_END(s); + + if (strchr(new_var, '=') != NULL) { + smartlist_add(env_vars, (void *)new_var); + } +} + +#ifdef _WIN32 +/** Read from a handle <b>h</b> into <b>buf</b>, up to <b>count</b> bytes. If + * <b>hProcess</b> is NULL, the function will return immediately if there is + * nothing more to read. Otherwise <b>hProcess</b> should be set to the handle + * to the process owning the <b>h</b>. In this case, the function will exit + * only once the process has exited, or <b>count</b> bytes are read. Returns + * the number of bytes read, or -1 on error. */ +ssize_t +tor_read_all_handle(HANDLE h, char *buf, size_t count, + const process_handle_t *process) +{ + size_t numread = 0; + BOOL retval; + DWORD byte_count; + BOOL process_exited = FALSE; + + if (count > SIZE_T_CEILING || count > SSIZE_T_MAX) + return -1; + + while (numread != count) { + /* Check if there is anything to read */ + retval = PeekNamedPipe(h, NULL, 0, NULL, &byte_count, NULL); + if (!retval) { + log_warn(LD_GENERAL, + "Failed to peek from handle: %s", + format_win32_error(GetLastError())); + return -1; + } else if (0 == byte_count) { + /* Nothing available: process exited or it is busy */ + + /* Exit if we don't know whether the process is running */ + if (NULL == process) + break; + + /* The process exited and there's nothing left to read from it */ + if (process_exited) + break; + + /* If process is not running, check for output one more time in case + it wrote something after the peek was performed. Otherwise keep on + waiting for output */ + tor_assert(process != NULL); + byte_count = WaitForSingleObject(process->pid.hProcess, 0); + if (WAIT_TIMEOUT != byte_count) + process_exited = TRUE; + + continue; + } + + /* There is data to read; read it */ + retval = ReadFile(h, buf+numread, count-numread, &byte_count, NULL); + tor_assert(byte_count + numread <= count); + if (!retval) { + log_warn(LD_GENERAL, "Failed to read from handle: %s", + format_win32_error(GetLastError())); + return -1; + } else if (0 == byte_count) { + /* End of file */ + break; + } + numread += byte_count; + } + return (ssize_t)numread; +} +#else +/** Read from a handle <b>h</b> into <b>buf</b>, up to <b>count</b> bytes. If + * <b>process</b> is NULL, the function will return immediately if there is + * nothing more to read. Otherwise data will be read until end of file, or + * <b>count</b> bytes are read. Returns the number of bytes read, or -1 on + * error. Sets <b>eof</b> to true if <b>eof</b> is not NULL and the end of the + * file has been reached. */ +ssize_t +tor_read_all_handle(FILE *h, char *buf, size_t count, + const process_handle_t *process, + int *eof) +{ + size_t numread = 0; + char *retval; + + if (eof) + *eof = 0; + + if (count > SIZE_T_CEILING || count > SSIZE_T_MAX) + return -1; + + while (numread != count) { + /* Use fgets because that is what we use in log_from_pipe() */ + retval = fgets(buf+numread, (int)(count-numread), h); + if (NULL == retval) { + if (feof(h)) { + log_debug(LD_GENERAL, "fgets() reached end of file"); + if (eof) + *eof = 1; + break; + } else { + if (EAGAIN == errno) { + if (process) + continue; + else + break; + } else { + log_warn(LD_GENERAL, "fgets() from handle failed: %s", + strerror(errno)); + return -1; + } + } + } + tor_assert(retval != NULL); + tor_assert(strlen(retval) + numread <= count); + numread += strlen(retval); + } + + log_debug(LD_GENERAL, "fgets() read %d bytes from handle", (int)numread); + return (ssize_t)numread; +} +#endif + +/** Read from stdout of a process until the process exits. */ +ssize_t +tor_read_all_from_process_stdout(const process_handle_t *process_handle, + char *buf, size_t count) +{ +#ifdef _WIN32 + return tor_read_all_handle(process_handle->stdout_pipe, buf, count, + process_handle); +#else + return tor_read_all_handle(process_handle->stdout_handle, buf, count, + process_handle, NULL); +#endif +} + +/** Read from stdout of a process until the process exits. */ +ssize_t +tor_read_all_from_process_stderr(const process_handle_t *process_handle, + char *buf, size_t count) +{ +#ifdef _WIN32 + return tor_read_all_handle(process_handle->stderr_pipe, buf, count, + process_handle); +#else + return tor_read_all_handle(process_handle->stderr_handle, buf, count, + process_handle, NULL); +#endif +} + +/** Split buf into lines, and add to smartlist. The buffer <b>buf</b> will be + * modified. The resulting smartlist will consist of pointers to buf, so there + * is no need to free the contents of sl. <b>buf</b> must be a NUL-terminated + * string. <b>len</b> should be set to the length of the buffer excluding the + * NUL. Non-printable characters (including NUL) will be replaced with "." */ +int +tor_split_lines(smartlist_t *sl, char *buf, int len) +{ + /* Index in buf of the start of the current line */ + int start = 0; + /* Index in buf of the current character being processed */ + int cur = 0; + /* Are we currently in a line */ + char in_line = 0; + + /* Loop over string */ + while (cur < len) { + /* Loop until end of line or end of string */ + for (; cur < len; cur++) { + if (in_line) { + if ('\r' == buf[cur] || '\n' == buf[cur]) { + /* End of line */ + buf[cur] = '\0'; + /* Point cur to the next line */ + cur++; + /* Line starts at start and ends with a nul */ + break; + } else { + if (!TOR_ISPRINT(buf[cur])) + buf[cur] = '.'; + } + } else { + if ('\r' == buf[cur] || '\n' == buf[cur]) { + /* Skip leading vertical space */ + ; + } else { + in_line = 1; + start = cur; + if (!TOR_ISPRINT(buf[cur])) + buf[cur] = '.'; + } + } + } + /* We are at the end of the line or end of string. If in_line is true there + * is a line which starts at buf+start and ends at a NUL. cur points to + * the character after the NUL. */ + if (in_line) + smartlist_add(sl, (void *)(buf+start)); + in_line = 0; + } + return smartlist_len(sl); +} + +#ifdef _WIN32 +/** Read from stream, and send lines to log at the specified log level. + * Returns -1 if there is a error reading, and 0 otherwise. + * If the generated stream is flushed more often than on new lines, or + * a read exceeds 256 bytes, lines will be truncated. This should be fixed, + * along with the corresponding problem on *nix (see bug #2045). + */ +static int +log_from_handle(HANDLE *pipe, int severity) +{ + char buf[256]; + int pos; + smartlist_t *lines; + + pos = tor_read_all_handle(pipe, buf, sizeof(buf) - 1, NULL); + if (pos < 0) { + /* Error */ + log_warn(LD_GENERAL, "Failed to read data from subprocess"); + return -1; + } + + if (0 == pos) { + /* There's nothing to read (process is busy or has exited) */ + log_debug(LD_GENERAL, "Subprocess had nothing to say"); + return 0; + } + + /* End with a null even if there isn't a \r\n at the end */ + /* TODO: What if this is a partial line? */ + buf[pos] = '\0'; + log_debug(LD_GENERAL, "Subprocess had %d bytes to say", pos); + + /* Split up the buffer */ + lines = smartlist_new(); + tor_split_lines(lines, buf, pos); + + /* Log each line */ + SMARTLIST_FOREACH(lines, char *, line, + { + log_fn(severity, LD_GENERAL, "Port forwarding helper says: %s", line); + }); + smartlist_free(lines); + + return 0; +} + +#else + +/** Read from stream, and send lines to log at the specified log level. + * Returns 1 if stream is closed normally, -1 if there is a error reading, and + * 0 otherwise. Handles lines from tor-fw-helper and + * tor_spawn_background() specially. + */ +static int +log_from_pipe(FILE *stream, int severity, const char *executable, + int *child_status) +{ + char buf[256]; + enum stream_status r; + + for (;;) { + r = get_string_from_pipe(stream, buf, sizeof(buf) - 1); + + if (r == IO_STREAM_CLOSED) { + return 1; + } else if (r == IO_STREAM_EAGAIN) { + return 0; + } else if (r == IO_STREAM_TERM) { + return -1; + } + + tor_assert(r == IO_STREAM_OKAY); + + /* Check if buf starts with SPAWN_ERROR_MESSAGE */ + if (strcmpstart(buf, SPAWN_ERROR_MESSAGE) == 0) { + /* Parse error message */ + int retval, child_state, saved_errno; + retval = tor_sscanf(buf, SPAWN_ERROR_MESSAGE "%x/%x", + &child_state, &saved_errno); + if (retval == 2) { + log_warn(LD_GENERAL, + "Failed to start child process \"%s\" in state %d: %s", + executable, child_state, strerror(saved_errno)); + if (child_status) + *child_status = 1; + } else { + /* Failed to parse message from child process, log it as a + warning */ + log_warn(LD_GENERAL, + "Unexpected message from port forwarding helper \"%s\": %s", + executable, buf); + } + } else { + log_fn(severity, LD_GENERAL, "Port forwarding helper says: %s", buf); + } + } + + /* We should never get here */ + return -1; +} +#endif + +/** Reads from <b>stream</b> and stores input in <b>buf_out</b> making + * sure it's below <b>count</b> bytes. + * If the string has a trailing newline, we strip it off. + * + * This function is specifically created to handle input from managed + * proxies, according to the pluggable transports spec. Make sure it + * fits your needs before using it. + * + * Returns: + * IO_STREAM_CLOSED: If the stream is closed. + * IO_STREAM_EAGAIN: If there is nothing to read and we should check back + * later. + * IO_STREAM_TERM: If something is wrong with the stream. + * IO_STREAM_OKAY: If everything went okay and we got a string + * in <b>buf_out</b>. */ +enum stream_status +get_string_from_pipe(FILE *stream, char *buf_out, size_t count) +{ + char *retval; + size_t len; + + tor_assert(count <= INT_MAX); + + retval = fgets(buf_out, (int)count, stream); + + if (!retval) { + if (feof(stream)) { + /* Program has closed stream (probably it exited) */ + /* TODO: check error */ + return IO_STREAM_CLOSED; + } else { + if (EAGAIN == errno) { + /* Nothing more to read, try again next time */ + return IO_STREAM_EAGAIN; + } else { + /* There was a problem, abandon this child process */ + return IO_STREAM_TERM; + } + } + } else { + len = strlen(buf_out); + tor_assert(len>0); + + if (buf_out[len - 1] == '\n') { + /* Remove the trailing newline */ + buf_out[len - 1] = '\0'; + } else { + /* No newline; check whether we overflowed the buffer */ + if (!feof(stream)) + log_info(LD_GENERAL, + "Line from stream was truncated: %s", buf_out); + /* TODO: What to do with this error? */ + } + + return IO_STREAM_OKAY; + } + + /* We should never get here */ + return IO_STREAM_TERM; +} + +void +tor_check_port_forwarding(const char *filename, int dir_port, int or_port, + time_t now) +{ +/* When fw-helper succeeds, how long do we wait until running it again */ +#define TIME_TO_EXEC_FWHELPER_SUCCESS 300 +/* When fw-helper failed to start, how long do we wait until running it again + */ +#define TIME_TO_EXEC_FWHELPER_FAIL 60 + + /* 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=NULL; + + static time_t time_to_run_helper = 0; + int stdout_status, stderr_status, retval; + const char *argv[10]; + char s_dirport[6], s_orport[6]; + + tor_assert(filename); + + /* Set up command line for tor-fw-helper */ + snprintf(s_dirport, sizeof s_dirport, "%d", dir_port); + snprintf(s_orport, sizeof s_orport, "%d", or_port); + + /* TODO: Allow different internal and external ports */ + argv[0] = filename; + argv[1] = "--internal-or-port"; + argv[2] = s_orport; + argv[3] = "--external-or-port"; + argv[4] = s_orport; + argv[5] = "--internal-dir-port"; + argv[6] = s_dirport; + argv[7] = "--external-dir-port"; + argv[8] = s_dirport; + argv[9] = NULL; + + /* Start the child, if it is not already 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 _WIN32 + /* Passing NULL as lpApplicationName makes Windows search for the .exe */ + status = tor_spawn_background(NULL, argv, NULL, &child_handle); +#else + status = tor_spawn_background(filename, argv, NULL, &child_handle); +#endif + + 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; + } + + log_info(LD_GENERAL, + "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 (child_handle && PROCESS_STATUS_RUNNING == child_handle->status) { + /* Read from stdout/stderr and log result */ + retval = 0; +#ifdef _WIN32 + 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, + LOG_INFO, filename, &retval); + stderr_status = log_from_pipe(child_handle->stderr_handle, + LOG_WARN, filename, &retval); +#endif + if (retval) { + /* There was a problem in the child process */ + time_to_run_helper = now + TIME_TO_EXEC_FWHELPER_FAIL; + } + + /* Combine the two statuses in order of severity */ + if (-1 == stdout_status || -1 == stderr_status) + /* There was a failure */ + retval = -1; +#ifdef _WIN32 + 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 */ + /* TODO: What if the process output something since + * between log_from_handle and tor_get_exit_code? */ + retval = 1; + } +#else + else if (1 == stdout_status || 1 == stderr_status) + /* stdout or stderr was closed, the process probably + * exited. It will be reaped by waitpid() in main.c */ + /* TODO: Do something with the process return value */ + retval = 1; +#endif + else + /* Both are fine */ + retval = 0; + + /* If either pipe indicates a failure, act on it */ + if (0 != retval) { + if (1 == retval) { + log_info(LD_GENERAL, "Port forwarding helper terminated"); + child_handle->status = PROCESS_STATUS_NOTRUNNING; + } else { + log_warn(LD_GENERAL, "Failed to read from port forwarding helper"); + child_handle->status = PROCESS_STATUS_ERROR; + } + + /* TODO: The child might not actually be finished (maybe it failed or + closed stdout/stderr), so maybe we shouldn't start another? */ + } + } +} + diff --git a/src/common/util.h b/src/common/util.h index d4771562e..36601fa79 100644 --- a/src/common/util.h +++ b/src/common/util.h @@ -73,6 +73,7 @@ void *_tor_malloc(size_t size DMALLOC_PARAMS) ATTR_MALLOC; void *_tor_malloc_zero(size_t size DMALLOC_PARAMS) ATTR_MALLOC; void *_tor_malloc_roundup(size_t *size DMALLOC_PARAMS) ATTR_MALLOC; +void *_tor_calloc(size_t nmemb, size_t size DMALLOC_PARAMS) ATTR_MALLOC; void *_tor_realloc(void *ptr, size_t size DMALLOC_PARAMS); char *_tor_strdup(const char *s DMALLOC_PARAMS) ATTR_MALLOC ATTR_NONNULL((1)); char *_tor_strndup(const char *s, size_t n DMALLOC_PARAMS) @@ -107,6 +108,7 @@ extern int dmalloc_free(const char *file, const int line, void *pnt, #define tor_malloc(size) _tor_malloc(size DMALLOC_ARGS) #define tor_malloc_zero(size) _tor_malloc_zero(size DMALLOC_ARGS) +#define tor_calloc(nmemb,size) _tor_calloc(nmemb, size DMALLOC_ARGS) #define tor_malloc_roundup(szp) _tor_malloc_roundup(szp DMALLOC_ARGS) #define tor_realloc(ptr, size) _tor_realloc(ptr, size DMALLOC_ARGS) #define tor_strdup(s) _tor_strdup(s DMALLOC_ARGS) @@ -160,6 +162,7 @@ uint64_t round_to_power_of_2(uint64_t u64); unsigned round_to_next_multiple_of(unsigned number, unsigned divisor); uint32_t round_uint32_to_next_multiple_of(uint32_t number, uint32_t divisor); uint64_t round_uint64_to_next_multiple_of(uint64_t number, uint64_t divisor); +int n_bits_set_u8(uint8_t v); /* Compute the CEIL of <b>a</b> divided by <b>b</b>, for nonnegative <b>a</b> * and positive <b>b</b>. Works on integer types only. Not defined if a+b can @@ -172,18 +175,15 @@ uint64_t round_uint64_to_next_multiple_of(uint64_t number, uint64_t divisor); #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 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, @@ -195,17 +195,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; -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; +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); +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; @@ -222,6 +224,11 @@ int tor_sscanf(const char *buf, const char *pattern, ...) #endif ; +void smartlist_add_asprintf(struct smartlist_t *sl, const char *pattern, ...) + CHECK_PRINTF(2, 3); +void smartlist_add_vasprintf(struct smartlist_t *sl, const char *pattern, + va_list args); + int hex_decode_digit(char c); void base16_encode(char *dest, size_t destlen, const char *src, size_t srclen); int base16_decode(char *dest, size_t destlen, const char *src, size_t srclen); @@ -237,8 +244,11 @@ time_t tor_timegm(struct tm *tm); void format_rfc1123_time(char *buf, time_t t); int parse_rfc1123_time(const char *buf, time_t *t); #define ISO_TIME_LEN 19 +#define ISO_TIME_USEC_LEN (ISO_TIME_LEN+7) void format_local_iso_time(char *buf, time_t t); void format_iso_time(char *buf, time_t t); +void format_iso_time_nospace(char *buf, time_t t); +void format_iso_time_nospace_usec(char *buf, const struct timeval *tv); int parse_iso_time(const char *buf, time_t *t); int parse_http_time(const char *buf, struct tm *tm); int format_time_interval(char *out, size_t out_len, long interval); @@ -283,6 +293,16 @@ char *rate_limit_log(ratelim_t *lim, time_t now); ssize_t write_all(tor_socket_t fd, const char *buf, size_t count,int isSocket); ssize_t read_all(tor_socket_t fd, char *buf, size_t count, int isSocket); +/** Status of an I/O stream. */ +enum stream_status { + IO_STREAM_OKAY, + IO_STREAM_EAGAIN, + IO_STREAM_TERM, + IO_STREAM_CLOSED +}; + +enum stream_status get_string_from_pipe(FILE *stream, char *buf, size_t count); + /** Return values from file_status(); see that function's documentation * for details. */ typedef enum { FN_ERROR, FN_NOENT, FN_FILE, FN_DIR } file_status_t; @@ -300,6 +320,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); @@ -321,6 +342,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 @@ -334,17 +357,119 @@ 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); void finish_daemon(const char *desired_cwd); void write_pidfile(char *filename); -#ifdef MS_WINDOWS +/* Port forwarding */ +void tor_check_port_forwarding(const char *filename, + int dir_port, int or_port, time_t now); + +typedef struct process_handle_t process_handle_t; +typedef struct process_environment_t process_environment_t; +int tor_spawn_background(const char *const filename, const char **argv, + process_environment_t *env, + process_handle_t **process_handle_out); + +#define SPAWN_ERROR_MESSAGE "ERR: Failed to spawn background process - code " + +#ifdef _WIN32 HANDLE load_windows_system_library(const TCHAR *library_name); #endif +int environment_variable_names_equal(const char *s1, const char *s2); + +struct process_environment_t { + /** A pointer to a sorted empty-string-terminated sequence of + * NUL-terminated strings of the form "NAME=VALUE". */ + char *windows_environment_block; + /** A pointer to a NULL-terminated array of pointers to + * NUL-terminated strings of the form "NAME=VALUE". */ + char **unixoid_environment_block; +}; + +process_environment_t *process_environment_make(struct smartlist_t *env_vars); +void process_environment_free(process_environment_t *env); + +struct smartlist_t *get_current_process_environment_variables(void); + +void set_environment_variable_in_smartlist(struct smartlist_t *env_vars, + const char *new_var, + void (*free_old)(void*), + int free_p); + +/* 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 + +#ifdef UTIL_PRIVATE +/*DOCDOC*/ +struct process_handle_t { + int status; +#ifdef _WIN32 + HANDLE stdout_pipe; + HANDLE stderr_pipe; + PROCESS_INFORMATION pid; +#else + int stdout_pipe; + int stderr_pipe; + FILE *stdout_handle; + FILE *stderr_handle; + pid_t pid; +#endif // _WIN32 +}; +#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 block, int *exit_code); +int tor_split_lines(struct smartlist_t *sl, char *buf, int len); +#ifdef _WIN32 +ssize_t tor_read_all_handle(HANDLE h, char *buf, size_t count, + const process_handle_t *process); +#else +ssize_t tor_read_all_handle(FILE *h, char *buf, size_t count, + const process_handle_t *process, + int *eof); +#endif +ssize_t tor_read_all_from_process_stdout( + const process_handle_t *process_handle, char *buf, size_t count); +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 _WIN32 +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); + +/* Space for hex values of child state, a slash, saved_errno (with + leading minus) and newline (no null) */ +#define HEX_ERRNO_SIZE (sizeof(char) * 2 + 1 + \ + 1 + sizeof(int) * 2 + 1) +#endif + const char *libor_get_digests(void); #endif |